[MDEV-15225] Can't import .ibd file with temporal type format differing from mysql56_temporal_format Created: 2018-02-06 Updated: 2023-08-03 Resolved: 2021-07-13 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Data Definition - Alter Table, Storage Engine - InnoDB, Temporal Types |
| Affects Version/s: | 10.1.29 |
| Fix Version/s: | 11.2 |
| Type: | Bug | Priority: | Critical |
| Reporter: | Geoff Montee (Inactive) | Assignee: | Yuchen Pei |
| Resolution: | Won't Fix | Votes: | 3 |
| Labels: | datetime, tablespace, workaround-exists | ||
| Issue Links: |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Description |
|
Let's say that we have two servers. One of the servers has a table with a temporal type that was created with MySQL 5.5 or earlier, and the other is a MariaDB 10.1 server that has mysql56_temporal_format=ON (the default). If we want to import a .ibd file for a table with temporal types from the first server into the second server, it seems difficult to do in a safe manner without setting mysql56_temporal_format=OFF and restarting mysqld. We can simulate this problem by doing the following:
This will fail:
Update: Can we please support the following simplified workflow:
and simply
to have InnoDB automatically create its table definition based on the .frm and optionally .cfg files, with no need to execute CREATE TABLE and ALTER TABLE…DISCARD TABLESPACE? That would create all temporal-type columns exactly as is. |
| Comments |
| Comment by Geoff Montee (Inactive) [ 2018-02-06 ] | |||||||||||||||||||||||||||||||||||||
|
If the original server with the source table is running a version that supports the new temporal type format, then a workaround for this is to "modify" the datetime column, so that it has the same data type, but uses the new format. e.g.:
Then you should be able to export/import the table. | |||||||||||||||||||||||||||||||||||||
| Comment by Elena Stepanova [ 2018-02-06 ] | |||||||||||||||||||||||||||||||||||||
|
In reality, if you import a tablespace from "pre-5.6" instance, you won't be able to run FLUSH FOR EXPORT, so no cfg file, thus you'll have to import it without metadata verification, and it will "work" (in the sense that it won't fail); but the data will be broken. It also appears true for MySQL 5.5 => 5.6 import. So, maybe it's good that it actually produces an error. I'm assigning it to bar to clarify if it was designed this way. | |||||||||||||||||||||||||||||||||||||
| Comment by Geoff Montee (Inactive) [ 2018-02-07 ] | |||||||||||||||||||||||||||||||||||||
|
It does seem to be an intentional decision by the MySQL team. The MySQL 5.6 upgrade notes describe a pretty convoluted workaround:
https://dev.mysql.com/doc/refman/5.6/en/upgrading-from-previous-series.html | |||||||||||||||||||||||||||||||||||||
| Comment by Alexander Barkov [ 2018-02-07 ] | |||||||||||||||||||||||||||||||||||||
|
To simplify upgrade, we could add a special syntax, like this: CREATE TABLE t1 (a /*!M100305 OLD */TIME); So one could specify the old temporal types when importing tables from old table spaces. Adding this syntax into versions before 10.3 is not desirable. With the new type handler conception (added into 10.3) it will be easier. Also, adding this new syntax will mean that old formats will remain in MariaDB for a long time. This is not desirable. Old data types do not support well replication with binary logging. So we want to get rid of them as soon as possible. | |||||||||||||||||||||||||||||||||||||
| Comment by Geoff Montee (Inactive) [ 2018-02-07 ] | |||||||||||||||||||||||||||||||||||||
|
An entirely different user just ran into this same problem today. The table involved was originally created on MariaDB 10.0, and the server had later been upgraded to 10.1, and then later to 10.2. The user tried to import the tablespace into another MariaDB 10.2 server. The import succeeded (probably because they didn't copy the .cfg file), but the data in the table was not being interpreted properly. When the user used the ALTER TABLE ... MODIFY command from It does sound like it makes sense to get rid of this old storage format ASAP. Maybe we should change mysql_upgrade to "upgrade" tables that use the old format? I believe that this would require the table to be rebuilt, which could take a lot of time and resources for large tables, so maybe it could be an option that has to be manually enabled? | |||||||||||||||||||||||||||||||||||||
| Comment by Alexander Barkov [ 2018-07-20 ] | |||||||||||||||||||||||||||||||||||||
|
With 1. Shutdown the destination server Does this sound too complex? | |||||||||||||||||||||||||||||||||||||
| Comment by Geoff Montee (Inactive) [ 2018-07-20 ] | |||||||||||||||||||||||||||||||||||||
|
Even without 1. Shutdown the destination server The problem is that users can't always shutdown production databases when they need to import tables. Would it make sense to make mysql56_temporal_format a dynamic session variable, so that something like this could be done?:
| |||||||||||||||||||||||||||||||||||||
| Comment by Alexander Barkov [ 2018-07-23 ] | |||||||||||||||||||||||||||||||||||||
|
We had changed formats (true VARCHAR, new DATE, new DECIMAL) even before these TIME/DATETIME/TIMESTAMP changes, and it's very likely will change formats for the built-in data types in the future. With the idea of pluggable data types, the probability of format changes becomes even higher in the future. Developers of data type plugins will also change their formats through time. So changing low level formats is not something extraordinary. For me it seems that further development around specifically mysql56_temporal_format is a dead way. The problem is that IMPORT TABLESPACE does not get enough information about the exact data types. CREATE TABLE ... Instead of the above cumbersome combination, we could introduce something like this: IMPORT TABLE t1; It would create the FRM file and import the IBD file in one shot. | |||||||||||||||||||||||||||||||||||||
| Comment by Alexander Barkov [ 2018-07-23 ] | |||||||||||||||||||||||||||||||||||||
|
That's true, users can't always shutdown production server. As a workaround, one can start one more temporary mariadb server for the format conversion purposes. Then do the following on this conversion server:
Then copy the newly exported table to the destination server and import it. | |||||||||||||||||||||||||||||||||||||
| Comment by Alexander Barkov [ 2018-07-23 ] | |||||||||||||||||||||||||||||||||||||
|
For now (before we add the precise IMPORT functionality) I can see the following solutions: 1. Do nothing in the code. Use a temporary conversion server on production servers that cannot be shut down. This sounds best for me at this point. 2. Add a format hint inside the column data type syntax: CREATE TABLE t1 (a /*!M100305 OLD */TIME); But here we need to think of the hint naming convention. "OLD" does not look good enough. It should be some sort of data-type-format-version-number here. 3. Add a session variable mysql56_temporal_format | |||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-07-24 ] | |||||||||||||||||||||||||||||||||||||
|
My preference is to introduce names for the old data types. The format hint comment seems to be the cleanest option (as it could be emitted by SHOW CREATE TABLE too). I would prefer not to change the format of InnoDB .cfg files in any way. Those files would be removed as part of MDEV-11655 (removing the InnoDB internal data dictionary) or MDEV-11658 (simpler, faster IMPORT). | |||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-10-05 ] | |||||||||||||||||||||||||||||||||||||
|
I have always found the InnoDB design of IMPORT TABLESPACE questionable. You first have to create an empty table, then violate the referential integrity of the data dictionary by issuing DISCARD TABLESPACE and by copying some data file to the server data directory, and finally issue IMPORT TABLESPACE to hopefully bring back the referential integrity. This would be fixed in MDEV-11658. You would just copy the .ibd and .frm files and let the table to be found by issuing an SQL statement, such as SELECT. The table would be created just like the .frm file describes it. Unfortunately, the .frm file does not contain the root page numbers of secondary indexes. These could still be scanned from the data file, or we could change the .ibd file format and store the secondary index page numbers in the metadata record that was introduced in | |||||||||||||||||||||||||||||||||||||
| Comment by Julien Fritsch [ 2019-01-23 ] | |||||||||||||||||||||||||||||||||||||
|
bar ? Any possible feedback, please? | |||||||||||||||||||||||||||||||||||||
| Comment by Alexander Barkov [ 2019-01-24 ] | |||||||||||||||||||||||||||||||||||||
|
julien.fritsch, sorry this is not something I can fix or give a feedback for. marko has some solution for a future version. For a simpler fix (e.g. format hints) please ask serg to have a look and approve this architecture proposal. I'm not very happy with this proposal. | |||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2019-04-11 ] | |||||||||||||||||||||||||||||||||||||
|
Here is one more idea what we could do. In addition to the current workflow of
we could support a simpler workflow:
This would import the table definition exactly as it was originally created. We would have to change some code in class ha_innobase and possibly its callers, so that if the table did not exist in InnoDB, InnoDB will create its own table metadata based on the handler::table_share, which is derived from the .frm file contents. | |||||||||||||||||||||||||||||||||||||
| Comment by Geoff Montee (Inactive) [ 2019-04-13 ] | |||||||||||||||||||||||||||||||||||||
|
Hi marko, That sounds like a cool idea. That would make | |||||||||||||||||||||||||||||||||||||
| Comment by Geoff Montee (Inactive) [ 2019-07-24 ] | |||||||||||||||||||||||||||||||||||||
|
The documentation for the mysql56_temporal_format system variable incorrectly said that the variable was not dynamic. It actually is dynamic:
I've fixed the documentation. This makes this bug a little easier to work around, since the server does not have to be restarted in order to change the value of mysql56_temporal_format. | |||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2019-12-19 ] | |||||||||||||||||||||||||||||||||||||
|
If the table was not rebuilt after executing CREATE INDEX or ADD INDEX, the secondary index root pages can be basically anywhere in the .ibd file. Normally they would immediately follow the clustered index root page (page 3). I wrote in MDEV-11658 that some existing code tries to guess the root page numbers when the .cfg file is not available. I am not at all convinced that the code actually works when ADD INDEX or DROP INDEX had been executed without rebuilding the table. If there are multiple secondary indexes on similar columns (say, multiple INT columns that are indexed individually), I think that in the worst case, finding the correct mapping could require a full scan of the secondary indexes and a full scan of the clustered index. And even then, we would have to try to avoid hitting crashes or debug assertion failures when trying to interpret the secondary index records. After all, in ROW_FORMAT≠REDUNDANT, reading the records normally requires the correct index metadata. It seems to be more worthwhile to support an option where all secondary indexes are outright marked as corrupted? Then the user would have to rebuild all secondary indexes:
Only with a file format change, we could guarantee the correct import of secondary indexes without having a .cfg file. In MDEV-4259 I wrote down an idea how to store the index metadata inside the .ibd files, to eventually allow the InnoDB data dictionary tables to be removed. The directory inside the .ibd files would store the root page numbers for each secondary index, and the indexes would be written in the same order as they appear in the TABLE_SHARE or the .frm files. | |||||||||||||||||||||||||||||||||||||
| Comment by Geoff Montee (Inactive) [ 2020-02-12 ] | |||||||||||||||||||||||||||||||||||||
|
To clarify, the full workaround for this bug is: 1. On the original server, execute the following query to find the mysql56_temporal_format value for the table. Replace $SCHEMA and $TABLE with the names of the specific database and table:
2. On the new server, set the value of mysql56_temporal_format to the value returned by the above query for the table. i.e.:
3. On the new server, create the table:
4. On the new server, discard the table's tablespace:
5. On the new server, copy the tablespace file and the .cfg file for the table:
6. On the new server, import the table's tablespace:
And then if you want to upgrade the table's temporal types, then you can use the following additional steps: 1. Set mysql56_temporal_format to ON:
2. Use the ALTER TABLE t1 MODIFY workaround for the affected table:
| |||||||||||||||||||||||||||||||||||||
| Comment by Geoff Montee (Inactive) [ 2020-02-13 ] | |||||||||||||||||||||||||||||||||||||
|
Note that MariaDB 10.1 does not support CTEs, so the MariaDB 10.1-compatible version of the large query from my last comment is:
| |||||||||||||||||||||||||||||||||||||
| Comment by Sergei Golubchik [ 2020-08-17 ] | |||||||||||||||||||||||||||||||||||||
|
marko, GeoffMontee, I think this "simplified workflow" is a great idea, let's do that. It's usefulness go way beyond fixing this temporal format mismatch. | |||||||||||||||||||||||||||||||||||||
| Comment by Eugene Kosov (Inactive) [ 2021-06-15 ] | |||||||||||||||||||||||||||||||||||||
|
serg Please, approve syntax. Is it OK for the patch to go into 10.2? | |||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-07-01 ] | |||||||||||||||||||||||||||||||||||||
|
kevg, I think that the proposed fix includes some unnecessary code changes. As far as I can tell, the parameter except_discarded is always being passed as false. If that flag were truly needed, I would suggest to add it to dict_err_ignore_t. Other than that, the change looks fine to me: we are basically making the first 2 steps optional:
| |||||||||||||||||||||||||||||||||||||
| Comment by Sergei Golubchik [ 2021-07-11 ] | |||||||||||||||||||||||||||||||||||||
|
I don't see why you need the third step. This can be simply
And in ha_innobase::open() it could be, like
Doesn't need any changes in the server at all, doesn't need the user to do anything beyond copying. | |||||||||||||||||||||||||||||||||||||
| Comment by Eugene Kosov (Inactive) [ 2021-07-13 ] | |||||||||||||||||||||||||||||||||||||
|
It was decided to not fix the issue. ALTER TABLE IMPORT will be enhanced in 10.7 task MDEV-26137 | |||||||||||||||||||||||||||||||||||||
| Comment by Hartmut Holzgraefe [ 2021-07-14 ] | |||||||||||||||||||||||||||||||||||||
|
The root problem though was that the error message gave no hints towards mysql56 temporal format being the cause of the mismatch? How would you guess that being the root cause when just seeing
as error message? If that message gave clear hints towards the workaround needed it would be much less of an issue. | |||||||||||||||||||||||||||||||||||||
| Comment by Sergei Golubchik [ 2021-07-14 ] | |||||||||||||||||||||||||||||||||||||
|
It doesn't really matter. If you have a schema mismatch, you need to have ibd/cgf/frm files, and import using all the three. You cannot import only ibd/cfg if you have a schema mismatch. No matter what exactly mismatch it is.
| |||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-07-16 ] | |||||||||||||||||||||||||||||||||||||
|
serg, what you suggested 5 days ago is MDEV-11658. It is not as simple as you imply, because until we have removed the InnoDB data dictionary, the discovered table would have to be registered there. I would not want to allow arbitrary DML statements to modify the InnoDB data dictionary tables. | |||||||||||||||||||||||||||||||||||||
| Comment by Sergei Golubchik [ 2021-07-16 ] | |||||||||||||||||||||||||||||||||||||
|
Okay. Then there's still a way to do it. I didn't mention it at first, hoping it won't be needed. There's fallback-and-retry loop in the table open process. MyISAM uses it to auto-repair tables on open, discovery uses it to auto-magically create tables on open, system versioning uses it to add new partitions ( | |||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-08-03 ] | |||||||||||||||||||||||||||||||||||||
|
|