Browse Source
MDEV-27234: Data dictionary recovery was not READ COMMITTED
MDEV-27234: Data dictionary recovery was not READ COMMITTED
This also fixes MDEV-20198: Instant ALTER TABLE is not crash safe InnoDB dictionary recovery wrongly used the READ UNCOMMITTED isolation level, causing some mismatch. For example, if a table was renamed or replaced in a transaction, according to READ UNCOMMITTED the table might not exist at all. We implement READ COMMITTED isolation level for accessing the dictionary tables SYS_TABLES, SYS_COLUMNS, SYS_INDEXES, SYS_FIELDS, SYS_VIRTUAL, SYS_FOREIGN, SYS_FOREIGN_COLS. For most of these tables, no secondary index exists. For the secondary indexes (on SYS_TABLES.ID, SYS_FOREIGN.FOR_NAME, SYS_FOREIGN.REF_NAME), we will always look up the primary key in the clustered index and check if the record actually is a committed version. dict_check_sys_tables(): Recover tablespaces also from delete-marked committed records, so that if a matching .ibd file exists, it will be removed by fil_delete_tablespace() when the committed delete-marked SYS_INDEXES record of the clustered index is purged in row_purge_remove_clust_if_poss_low(). fil_ibd_open(): Change the Boolean parameter "validate" to a ternary one, to suppress error messages when the file might not exist. It is possible that a .ibd file was deleted and the server shut down before the SYS_INDEXES and SYS_TABLES records were purged. Hence, if dict_check_sys_tables() finds a committed delete-marked record, we must not complain if the tablespace file is not found. On Windows, we msut treat ERROR_PATH_NOT_FOUND (directory not found) in the same way as ERROR_FILE_NOT_FOUND. This fixes a few failures where a previous test successfully executed DROP DATABASE (and deleted all files and the directory), but a committed delete-marked SYS_TABLES record had not been purged before server restart. dict_getnext_system_low(): Do not filter out delete-marked records. dict_startscan_system(), dict_getnext_system(): Do filter out delete-marked records, for accessing the INFORMATION_SCHEMA tables. dict_sys_tables_rec_read(): Return the DB_TRX_ID of the committed version of the record. This is needed in dict_load_table_low(). dict_load_foreign_cols(), dict_load_foreign(): Add a parameter for the current transaction identifier. In some DDL operations, the FOREIGN KEY constraints are being loaded from the data dictionary before the DDL transaction has been committed. For SYS_FOREIGN and SYS_FOREIGN_COLS, we must implement the special case of READ COMMITTED that the changes of the uncommitted current transaction are visible. dict_load_foreign(): Validate the table name. We could find a SYS_FOREIGN.ID via a committed delete-marked secondary index record that does not match the REF_NAME or FOR_NAME of the secondary index record. dict_load_index_low(): Optionally take the table as a parameter, so that table->def_trx_id can be updated in case of a committed delete-marked SYS_INDEXES record corresponding to DROP INDEX, but not corresponding to an index stub of ADD INDEX. dict_load_indexes(): Do not update table->def_trx_id in case of delete-marked records. rec_is_metadata(), rec_offs_make_valid(), rec_get_offsets_func(), row_build_low(): Relax some assertions. We may now have !index->is_instant() even if a metadata record is present in the index. Previously, the recovery of instant ADD/DROP COLUMN assumed that READ UNCOMMITTED of the data dictionary will be performed. Now, we will have a READ COMMITTED copy of the data dictionary cache, and a READ UNCOMMITTED copy of the metadata record. btr_page_reorganize_low(): Correctly update the FIL_PAGE_TYPE when rolling back an instant ADD/DROP COLUMN operation. row_rec_to_index_entry_impl(): Relax some assertions, and disallow accessing "extra" fields. This fixes the recovery of a crash during an instant ADD COLUMN after a successful instant DROP COLUMN, in the test innodb.instant_alter_crash. Tested by: Matthias Leichbb-10.6-MDEV-24845-galera
16 changed files with 518 additions and 342 deletions
-
23mysql-test/suite/innodb/r/row_format_redundant.result
-
26mysql-test/suite/innodb/t/alter_crash_rebuild.test
-
27mysql-test/suite/innodb/t/row_format_redundant.test
-
24storage/innobase/btr/btr0btr.cc
-
640storage/innobase/dict/dict0load.cc
-
37storage/innobase/fil/fil0fil.cc
-
7storage/innobase/handler/ha_innodb.cc
-
4storage/innobase/handler/handler0alter.cc
-
17storage/innobase/handler/i_s.cc
-
7storage/innobase/include/dict0load.h
-
7storage/innobase/include/fil0fil.h
-
8storage/innobase/include/rem0rec.h
-
11storage/innobase/rem/rem0rec.cc
-
4storage/innobase/row/row0import.cc
-
2storage/innobase/row/row0mysql.cc
-
16storage/innobase/row/row0row.cc
@ -0,0 +1,26 @@ |
|||
--source include/have_innodb.inc |
|||
--source include/have_debug.inc |
|||
--source include/have_debug_sync.inc |
|||
|
|||
CREATE TABLE t1 (a INT NOT NULL) ENGINE=InnoDB STATS_PERSISTENT=0; |
|||
|
|||
connect ddl,localhost,root; |
|||
SET DEBUG_SYNC='after_trx_committed_in_memory SIGNAL stuck WAIT_FOR ever EXECUTE 2'; |
|||
send ALTER TABLE t1 ADD PRIMARY KEY(a); |
|||
|
|||
connection default; |
|||
SET DEBUG_SYNC='now WAIT_FOR stuck'; |
|||
SET DEBUG_SYNC='now SIGNAL ever'; |
|||
SET DEBUG_SYNC='now WAIT_FOR stuck'; |
|||
|
|||
SET GLOBAL innodb_log_checkpoint_now=ON; |
|||
|
|||
--let $shutdown_timeout=0 |
|||
--source include/restart_mysqld.inc |
|||
|
|||
disconnect ddl; |
|||
|
|||
SHOW CREATE TABLE t1; |
|||
SELECT * FROM t1; |
|||
DROP TABLE t1; |
|||
--source include/wait_all_purged.inc |
640
storage/innobase/dict/dict0load.cc
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue