From 0381921e265b4e7c8c97dfeb21f074dfd02e02cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 8 Feb 2024 10:35:45 +0200 Subject: [PATCH] MDEV-33277 In-place upgrade causes invalid AUTO_INCREMENT values MDEV-33308 CHECK TABLE is modifying .frm file even if --read-only As noted in commit d0ef1aaf618452efea5a2e74f6d86ed0a586b63e, MySQL as well as older versions of MariaDB server would during ALTER TABLE ... IMPORT TABLESPACE write bogus values to the PAGE_MAX_TRX_ID field to pages of the clustered index, instead of letting that field remain 0. In commit 8777458a6eb73ac1d7d864ebac390ea7039e21c1 this field was repurposed for PAGE_ROOT_AUTO_INC in the clustered index root page. To avoid trouble when upgrading from MySQL or older versions of MariaDB, we will try to detect and correct bogus values of PAGE_ROOT_AUTO_INC when opening a table for the first time from the SQL layer. btr_read_autoinc_with_fallback(): Add the parameters to mysql_version,max to indicate the TABLE_SHARE::mysql_version of the .frm file and the maximum value allowed for the type of the AUTO_INCREMENT column. In case the table was originally created in MySQL or an older version of MariaDB, read also the maximum value of the AUTO_INCREMENT column from the table and reset the PAGE_ROOT_AUTO_INC if it is above the limit. dict_table_t::get_index(const dict_col_t &) const: Find an index that starts with the specified column. ha_innobase::check_for_upgrade(): Return HA_ADMIN_FAILED if InnoDB needs upgrading but is in read-only mode. In this way, the call to update_frm_version() will be skipped. row_import_autoinc(): Adjust the AUTO_INCREMENT column at the end of ALTER TABLE...IMPORT TABLESPACE. This refinement was suggested by Debarun Banerjee. The changes outside InnoDB were developed by Michael 'Monty' Widenius: Added print_check_msg() service for easy reporting of check/repair messages in ENGINE=Aria and ENGINE=InnoDB. Fixed that CHECK TABLE do not update the .frm file under --read-only. Added 'handler_flags' to HA_CHECK_OPT as a way for storage engines to store state from handler::check_for_upgrade(). Reviewed by: Debarun Banerjee --- include/mysql/service_print_check_msg.h | 44 ++++ include/service_versions.h | 1 + libservices/CMakeLists.txt | 1 + libservices/print_check_msg_service.c | 18 ++ mysql-test/std_data/autoinc_import_101.frm | Bin 0 -> 922 bytes mysql-test/std_data/autoinc_import_57.frm | Bin 0 -> 8556 bytes .../suite/innodb/r/autoinc_import.result | 232 ++++++++++++++++++ mysql-test/suite/innodb/t/autoinc_import.test | 168 +++++++++++++ sql/handler.cc | 6 +- sql/handler.h | 1 + sql/sql_class.cc | 34 +++ sql/sql_plugin_services.inl | 7 + storage/innobase/btr/btr0btr.cc | 97 +++++--- storage/innobase/handler/ha_innodb.cc | 142 +++++++---- storage/innobase/handler/ha_innodb.h | 7 + storage/innobase/handler/handler0alter.cc | 17 +- storage/innobase/include/btr0btr.h | 17 +- storage/innobase/include/dict0mem.h | 3 + storage/innobase/row/row0import.cc | 44 +++- storage/maria/ha_maria.cc | 28 +-- 20 files changed, 723 insertions(+), 144 deletions(-) create mode 100644 include/mysql/service_print_check_msg.h create mode 100644 libservices/print_check_msg_service.c create mode 100644 mysql-test/std_data/autoinc_import_101.frm create mode 100644 mysql-test/std_data/autoinc_import_57.frm create mode 100644 mysql-test/suite/innodb/r/autoinc_import.result create mode 100644 mysql-test/suite/innodb/t/autoinc_import.test diff --git a/include/mysql/service_print_check_msg.h b/include/mysql/service_print_check_msg.h new file mode 100644 index 00000000000..c2c7cf0a381 --- /dev/null +++ b/include/mysql/service_print_check_msg.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2019, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#pragma once + +/** + @file include/mysql/service_print_check_msg.h + This service provides functions to write messages for check or repair +*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern struct print_check_msg_service_st { + void (*print_check_msg)(MYSQL_THD, const char *db_name, const char *table_name, + const char *op, const char *msg_type, const char *message, + my_bool print_to_log); +} *print_check_msg_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN +# define print_check_msg_context(_THD) print_check_msg_service->print_check_msg +#else +extern void print_check_msg(MYSQL_THD, const char *db_name, const char *table_name, + const char *op, const char *msg_type, const char *message, + my_bool print_to_log); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/include/service_versions.h b/include/service_versions.h index ee250864287..cace4f37c69 100644 --- a/include/service_versions.h +++ b/include/service_versions.h @@ -45,3 +45,4 @@ #define VERSION_json 0x0100 #define VERSION_sql_service 0x0100 #define VERSION_thd_mdl 0x0100 +#define VERSION_print_check_msg 0x0100 diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt index 6b47bb53fdb..d1329b665d5 100644 --- a/libservices/CMakeLists.txt +++ b/libservices/CMakeLists.txt @@ -25,6 +25,7 @@ SET(MYSQLSERVICES_SOURCES my_crypt_service.c my_md5_service.c my_print_error_service.c + print_check_msg_service.c my_sha1_service.c my_sha2_service.c my_snprintf_service.c diff --git a/libservices/print_check_msg_service.c b/libservices/print_check_msg_service.c new file mode 100644 index 00000000000..12f7bc51118 --- /dev/null +++ b/libservices/print_check_msg_service.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2024, MariaDB Plc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +SERVICE_VERSION print_check_msg_context= (void*) VERSION_print_check_msg; diff --git a/mysql-test/std_data/autoinc_import_101.frm b/mysql-test/std_data/autoinc_import_101.frm new file mode 100644 index 0000000000000000000000000000000000000000..b16ae91b110ccd90bf149d73db70c8d0299d0a28 GIT binary patch literal 922 zcmeyz$jKwb5XQjBFpHUi;U^<20~|0ZGH@_3fCYdw1pH)VU@&cGWDtM}FbKTXU|PNN zrr^7&-vp{%Hr8aNFflMN0+n$Am9PNK1@anzLQ+7^|9~J*U&o-x|8U)-jNugm(3oXo z@XX80cX5Jv&WzCvlnh2N0C^vjA6OyT2^#siRY!imT$%K`7nay9A`Zf=}mT8Lw&U>{6(3?vft@ zIsLX?@{-xy0Eqy5z)FN~1zSu3zoBDWR?i)tz^32GG9c|294SBn3Q&Lo6rcbFC_n)U zP=Epy7^Q&d4}Gl3Lg)nY2!ORNTv-*DV|8_-4E>LxI>`x&rn#R_?I(QXSrniE1t>rP z3Q&Lo6rcbFC_sVX1#W_5_*=$%gJ|C)_Z^U^ed^$&mRNO>a|9ABcj=F%trKHwgeH#R OTPX{`=<5k*Ylk-*dlr}g literal 0 HcmV?d00001 diff --git a/mysql-test/suite/innodb/r/autoinc_import.result b/mysql-test/suite/innodb/r/autoinc_import.result new file mode 100644 index 00000000000..4a8ddd02e3d --- /dev/null +++ b/mysql-test/suite/innodb/r/autoinc_import.result @@ -0,0 +1,232 @@ +CREATE TABLE t1 (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES(42); +CREATE TABLE t1b LIKE t1; +INSERT INTO t1b VALUES(3); +CREATE TABLE t1z LIKE t1; +CREATE TABLE t1t (id TINYINT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +CREATE TABLE t0t LIKE t1t; +INSERT INTO t1t VALUES(123); +FLUSH TABLES t1,t1b,t1t FOR EXPORT; +UNLOCK TABLES; +CREATE TABLE t5_7 LIKE t1; +CREATE TABLE t5_7b LIKE t1b; +CREATE TABLE t10_1 LIKE t1; +CREATE TABLE t10_1b LIKE t1b; +ALTER TABLE t1 DISCARD TABLESPACE; +ALTER TABLE t1b DISCARD TABLESPACE; +ALTER TABLE t1z DISCARD TABLESPACE; +ALTER TABLE t1t DISCARD TABLESPACE; +ALTER TABLE t0t DISCARD TABLESPACE; +ALTER TABLE t5_7 DISCARD TABLESPACE; +ALTER TABLE t5_7b DISCARD TABLESPACE; +ALTER TABLE t10_1 DISCARD TABLESPACE; +ALTER TABLE t10_1b DISCARD TABLESPACE; +FLUSH TABLES; +ALTER TABLE t0t IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t0t.cfg', will attempt to import without schema verification +INSERT INTO t0t VALUES(NULL); +SELECT * FROM t0t; +id +123 +124 +DROP TABLE t0t; +ALTER TABLE t1 IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t1.cfg', will attempt to import without schema verification +ALTER TABLE t1b IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t1b.cfg', will attempt to import without schema verification +ALTER TABLE t1z IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t1z.cfg', will attempt to import without schema verification +ALTER TABLE t1t IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t1t.cfg', will attempt to import without schema verification +ALTER TABLE t5_7 IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t5_7.cfg', will attempt to import without schema verification +ALTER TABLE t5_7b IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t5_7b.cfg', will attempt to import without schema verification +ALTER TABLE t10_1 IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t10_1.cfg', will attempt to import without schema verification +ALTER TABLE t10_1b IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t10_1b.cfg', will attempt to import without schema verification +FOUND 1 /InnoDB: Resetting PAGE_ROOT_AUTO_INC from 128 to 123 on table `test`\.`t0t`/ in mysqld.1.err +FOUND 1 /InnoDB: Resetting PAGE_ROOT_AUTO_INC from 0 to 42 on table `test`\.`t1z`/ in mysqld.1.err +FOUND 1 /InnoDB: Resetting PAGE_ROOT_AUTO_INC from 128 to 123 on table `test`\.`t1t`/ in mysqld.1.err +FOUND 1 /InnoDB: Resetting PAGE_ROOT_AUTO_INC from 3 to 42 on table `test`\.`t5_7` \(created with version 50744\)/ in mysqld.1.err +FOUND 1 /InnoDB: Resetting PAGE_ROOT_AUTO_INC from 3 to 42 on table `test`\.`t10_1` \(created with version 100149\)/ in mysqld.1.err +FOUND 5 /InnoDB: Resetting PAGE_ROOT_AUTO_INC/ in mysqld.1.err +# restart: --read-only +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7 check status OK +test.t5_7b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7b check status OK +test.t10_1 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1 check status OK +test.t10_1b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1b check status OK +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7 check status OK +test.t5_7b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7b check status OK +test.t10_1 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1 check status OK +test.t10_1b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1b check status OK +# restart: --innodb-read-only --read-only +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7 check status OK +test.t5_7b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7b check status OK +test.t10_1 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1 check status OK +test.t10_1b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1b check status OK +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7 check status OK +test.t5_7b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t5_7b check status OK +test.t10_1 check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1 check status OK +test.t10_1b check note Auto_increment will be checked on each open until CHECK TABLE FOR UPGRADE is executed +test.t10_1b check status OK +# restart: --innodb-read-only +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check status Operation failed +test.t5_7b check status Operation failed +test.t10_1 check status Operation failed +test.t10_1b check status Operation failed +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check status Operation failed +test.t5_7b check status Operation failed +test.t10_1 check status Operation failed +test.t10_1b check status Operation failed +SELECT COUNT(*) FROM t1; +COUNT(*) +1 +SELECT COUNT(*) FROM t1b; +COUNT(*) +1 +SELECT COUNT(*) FROM t1t; +COUNT(*) +1 +SELECT COUNT(*) FROM t1z; +COUNT(*) +1 +SELECT COUNT(*) FROM t5_7; +COUNT(*) +1 +SELECT COUNT(*) FROM t5_7b; +COUNT(*) +1 +SELECT COUNT(*) FROM t10_1; +COUNT(*) +1 +SELECT COUNT(*) FROM t10_1b; +COUNT(*) +1 +# restart +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check note Auto_increment checked and .frm file version updated +test.t5_7 check status OK +test.t5_7b check note Auto_increment checked and .frm file version updated +test.t5_7b check status OK +test.t10_1 check note Auto_increment checked and .frm file version updated +test.t10_1 check status OK +test.t10_1b check note Auto_increment checked and .frm file version updated +test.t10_1b check status OK +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1b VALUES(NULL); +INSERT INTO t1t VALUES(NULL); +INSERT INTO t1z VALUES(NULL); +INSERT INTO t5_7 VALUES(NULL); +INSERT INTO t5_7b VALUES(NULL); +INSERT INTO t10_1 VALUES(NULL); +INSERT INTO t10_1b VALUES(NULL); +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t1b check status OK +test.t1t check status OK +test.t1z check status OK +test.t5_7 check status OK +test.t5_7b check status OK +test.t10_1 check status OK +test.t10_1b check status OK +SELECT * FROM t1; +id +4 +42 +SELECT * FROM t1b; +id +3 +347 +SELECT * FROM t1t; +id +123 +124 +SELECT * FROM t1z; +id +42 +43 +SELECT * FROM t5_7; +id +42 +43 +SELECT * FROM t5_7b; +id +3 +347 +SELECT * FROM t10_1; +id +42 +43 +SELECT * FROM t10_1b; +id +3 +347 +DROP TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; diff --git a/mysql-test/suite/innodb/t/autoinc_import.test b/mysql-test/suite/innodb/t/autoinc_import.test new file mode 100644 index 00000000000..e137413dfb5 --- /dev/null +++ b/mysql-test/suite/innodb/t/autoinc_import.test @@ -0,0 +1,168 @@ +--source include/have_innodb.inc + +CREATE TABLE t1 (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES(42); +CREATE TABLE t1b LIKE t1; +INSERT INTO t1b VALUES(3); +CREATE TABLE t1z LIKE t1; +CREATE TABLE t1t (id TINYINT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +CREATE TABLE t0t LIKE t1t; +INSERT INTO t1t VALUES(123); +--let DATADIR=`select @@datadir` +--let PAGE_SIZE=`select @@innodb_page_size` +FLUSH TABLES t1,t1b,t1t FOR EXPORT; +--copy_file $DATADIR/test/t1.ibd $DATADIR/test/t.ibd +--copy_file $DATADIR/test/t1.ibd $DATADIR/test/tz.ibd +--copy_file $DATADIR/test/t1b.ibd $DATADIR/test/tb.ibd +--copy_file $DATADIR/test/t1t.ibd $DATADIR/test/tt.ibd +UNLOCK TABLES; +CREATE TABLE t5_7 LIKE t1; +CREATE TABLE t5_7b LIKE t1b; +CREATE TABLE t10_1 LIKE t1; +CREATE TABLE t10_1b LIKE t1b; +ALTER TABLE t1 DISCARD TABLESPACE; +ALTER TABLE t1b DISCARD TABLESPACE; +ALTER TABLE t1z DISCARD TABLESPACE; +ALTER TABLE t1t DISCARD TABLESPACE; +ALTER TABLE t0t DISCARD TABLESPACE; +ALTER TABLE t5_7 DISCARD TABLESPACE; +ALTER TABLE t5_7b DISCARD TABLESPACE; +ALTER TABLE t10_1 DISCARD TABLESPACE; +ALTER TABLE t10_1b DISCARD TABLESPACE; +FLUSH TABLES; + +# Update the PAGE_ROOT_AUTO_INC field of a few files. +perl; +do "$ENV{MTR_SUITE_DIR}/include/crc32.pl"; +sub update_autoinc +{ + my ($file, $value) = @_; + open(FILE, "+<$file") || die "Unable to open $file"; + binmode FILE; + my $ps= $ENV{PAGE_SIZE}; + my $page; + die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps; + my $full_crc32 = unpack("N",substr($page,54,4)) & 0x10; # FIL_SPACE_FLAGS + sysseek(FILE, 3*$ps, 0) || die "Unable to seek $file\n"; + die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps; + substr($page,56,8)=pack("NN",0,$value); + my $polynomial = 0x82f63b78; # CRC-32C + if ($full_crc32) { + my $ck = mycrc32(substr($page, 0, $ps-4), 0, $polynomial); + substr($page, $ps-4, 4) = pack("N", $ck); + } + else + { + my $ck= pack("N",mycrc32(substr($page, 4, 22), 0, $polynomial) ^ + mycrc32(substr($page, 38, $ps - 38 - 8), 0, $polynomial)); + substr($page,0,4)=$ck; + substr($page,$ps-8,4)=$ck; + } + sysseek(FILE, 3*$ps, 0) || die "Unable to rewind $file\n"; + syswrite(FILE, $page, $ps)==$ps || die "Unable to write $file\n"; + close(FILE) || die "Unable to close $file"; +} +update_autoinc("$ENV{DATADIR}/test/tz.ibd", 0); +update_autoinc("$ENV{DATADIR}/test/t.ibd", 3); +update_autoinc("$ENV{DATADIR}/test/tb.ibd", 346); +update_autoinc("$ENV{DATADIR}/test/tt.ibd", 128); +EOF + +--remove_file $DATADIR/test/t5_7.frm +--remove_file $DATADIR/test/t5_7b.frm +--copy_file $MYSQL_TEST_DIR/std_data/autoinc_import_57.frm $DATADIR/test/t5_7.frm +--copy_file $MYSQL_TEST_DIR/std_data/autoinc_import_57.frm $DATADIR/test/t5_7b.frm +--remove_file $DATADIR/test/t10_1.frm +--remove_file $DATADIR/test/t10_1b.frm +--copy_file $MYSQL_TEST_DIR/std_data/autoinc_import_101.frm $DATADIR/test/t10_1.frm +--copy_file $MYSQL_TEST_DIR/std_data/autoinc_import_101.frm $DATADIR/test/t10_1b.frm +--copy_file $DATADIR/test/t.ibd $DATADIR/test/t5_7.ibd +--copy_file $DATADIR/test/tb.ibd $DATADIR/test/t5_7b.ibd +--copy_file $DATADIR/test/t.ibd $DATADIR/test/t10_1.ibd +--copy_file $DATADIR/test/tb.ibd $DATADIR/test/t10_1b.ibd +--move_file $DATADIR/test/t.ibd $DATADIR/test/t1.ibd +--move_file $DATADIR/test/tb.ibd $DATADIR/test/t1b.ibd +--copy_file $DATADIR/test/tt.ibd $DATADIR/test/t0t.ibd +--move_file $DATADIR/test/tt.ibd $DATADIR/test/t1t.ibd +--move_file $DATADIR/test/tz.ibd $DATADIR/test/t1z.ibd + +ALTER TABLE t0t IMPORT TABLESPACE; +INSERT INTO t0t VALUES(NULL); +SELECT * FROM t0t; +DROP TABLE t0t; +ALTER TABLE t1 IMPORT TABLESPACE; +ALTER TABLE t1b IMPORT TABLESPACE; +ALTER TABLE t1z IMPORT TABLESPACE; +ALTER TABLE t1t IMPORT TABLESPACE; +ALTER TABLE t5_7 IMPORT TABLESPACE; +ALTER TABLE t5_7b IMPORT TABLESPACE; +ALTER TABLE t10_1 IMPORT TABLESPACE; +ALTER TABLE t10_1b IMPORT TABLESPACE; + +--let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let SEARCH_PATTERN= InnoDB: Resetting PAGE_ROOT_AUTO_INC from 128 to 123 on table `test`\.`t0t` +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= InnoDB: Resetting PAGE_ROOT_AUTO_INC from 0 to 42 on table `test`\.`t1z` +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= InnoDB: Resetting PAGE_ROOT_AUTO_INC from 128 to 123 on table `test`\.`t1t` +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= InnoDB: Resetting PAGE_ROOT_AUTO_INC from 3 to 42 on table `test`\.`t5_7` \(created with version 50744\) +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= InnoDB: Resetting PAGE_ROOT_AUTO_INC from 3 to 42 on table `test`\.`t10_1` \(created with version 100149\) +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= InnoDB: Resetting PAGE_ROOT_AUTO_INC +--source include/search_pattern_in_file.inc + +# Restart, so that the InnoDB tables will be loaded into the data dictionary. +--let $restart_parameters=--read-only +--source include/restart_mysqld.inc + +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; + +--let $restart_parameters=--innodb-read-only --read-only +--source include/restart_mysqld.inc + +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; + +--let $restart_parameters=--innodb-read-only +--source include/restart_mysqld.inc + +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; + +SELECT COUNT(*) FROM t1; +SELECT COUNT(*) FROM t1b; +SELECT COUNT(*) FROM t1t; +SELECT COUNT(*) FROM t1z; +SELECT COUNT(*) FROM t5_7; +SELECT COUNT(*) FROM t5_7b; +SELECT COUNT(*) FROM t10_1; +SELECT COUNT(*) FROM t10_1b; + +--let $restart_parameters= +--source include/restart_mysqld.inc + +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; + +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1b VALUES(NULL); +INSERT INTO t1t VALUES(NULL); +INSERT INTO t1z VALUES(NULL); +INSERT INTO t5_7 VALUES(NULL); +INSERT INTO t5_7b VALUES(NULL); +INSERT INTO t10_1 VALUES(NULL); +INSERT INTO t10_1b VALUES(NULL); + +CHECK TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b FOR UPGRADE; + +SELECT * FROM t1; +SELECT * FROM t1b; +SELECT * FROM t1t; +SELECT * FROM t1z; +SELECT * FROM t5_7; +SELECT * FROM t5_7b; +SELECT * FROM t10_1; +SELECT * FROM t10_1b; +DROP TABLE t1, t1b, t1t, t1z, t5_7, t5_7b, t10_1, t10_1b; diff --git a/sql/handler.cc b/sql/handler.cc index 2e2961bef5f..f4453a9aebb 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -4669,7 +4669,7 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt) if (unlikely((error= check(thd, check_opt)))) return error; /* Skip updating frm version if not main handler. */ - if (table->file != this) + if (table->file != this || opt_readonly) return error; return update_frm_version(table); } @@ -4718,7 +4718,7 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) DBUG_ASSERT(result == HA_ADMIN_NOT_IMPLEMENTED || ha_table_flags() & HA_CAN_REPAIR); - if (result == HA_ADMIN_OK) + if (result == HA_ADMIN_OK && !opt_readonly) result= update_frm_version(table); return result; } @@ -5659,7 +5659,7 @@ err: void st_ha_check_opt::init() { - flags= sql_flags= 0; + flags= sql_flags= handler_flags= 0; start_time= my_time(0); } diff --git a/sql/handler.h b/sql/handler.h index 67245850d62..f4d828285d5 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2615,6 +2615,7 @@ typedef struct st_ha_check_opt st_ha_check_opt() = default; /* Remove gcc warning */ uint flags; /* isam layer flags (e.g. for myisamchk) */ uint sql_flags; /* sql layer flags - for something myisamchk cannot do */ + uint handler_flags; /* Reserved for handler usage */ time_t start_time; /* When check/repair starts */ KEY_CACHE *key_cache; /* new key cache when changing key cache */ void init(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 52d65ee46f6..a358d9c3351 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5665,6 +5665,40 @@ extern "C" void *thd_mdl_context(MYSQL_THD thd) return &thd->mdl_context; } +/** + Send check/repair message to the user + + @param op one of check or repair + @param msg_type one of info, warning or error + @param print_to_log <> 0 if we should also print the message to error log. +*/ + +extern "C" void +print_check_msg(THD *thd, const char *db_name, const char *table_name, const char *op, + const char *msg_type, const char *message, my_bool print_to_log) +{ + char name[NAME_LEN * 2 + 2]; + Protocol *protocol= thd->protocol; + + DBUG_ASSERT(strlen(db_name) <= NAME_LEN); + DBUG_ASSERT(strlen(table_name) <= NAME_LEN); + + size_t length= size_t(strxnmov(name, sizeof name - 1, + db_name, ".", table_name, NullS) - + name); + protocol->prepare_for_resend(); + protocol->store(name, length, system_charset_info); + protocol->store(op, system_charset_info); + protocol->store(msg_type, system_charset_info); + protocol->store(message, system_charset_info); + if (protocol->write()) + sql_print_error("Failed on my_net_write, writing to stderr instead: %s: %s\n", + table_name, message); + else if (thd->variables.log_warnings > 2 && print_to_log) + sql_print_error("%s: table '%s' got '%s' during %s", + msg_type, table_name, message, op); +} + /**************************************************************************** Handling of statement states in functions and triggers. diff --git a/sql/sql_plugin_services.inl b/sql/sql_plugin_services.inl index 6b21f64bc64..e560812260a 100644 --- a/sql/sql_plugin_services.inl +++ b/sql/sql_plugin_services.inl @@ -18,6 +18,7 @@ #include #include #include +#include struct st_service_ref { const char *name; @@ -217,6 +218,11 @@ static struct my_print_error_service_st my_print_error_handler= my_printv_error }; +static struct print_check_msg_service_st print_check_msg_handler= +{ + print_check_msg +}; + struct json_service_st json_handler= { json_type, @@ -264,6 +270,7 @@ static struct st_service_ref list_of_services[]= { "my_crypt_service", VERSION_my_crypt, &crypt_handler}, { "my_md5_service", VERSION_my_md5, &my_md5_handler}, { "my_print_error_service", VERSION_my_print_error, &my_print_error_handler}, + { "print_check_msg_service", VERSION_print_check_msg, &print_check_msg_handler}, { "my_sha1_service", VERSION_my_sha1, &my_sha1_handler}, { "my_sha2_service", VERSION_my_sha2, &my_sha2_handler}, { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler }, diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 70d2178a5af..db57d31dd15 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -43,6 +43,7 @@ Created 6/2/1994 Heikki Tuuri #include "gis0geo.h" #include "dict0boot.h" #include "row0sel.h" /* row_search_max_autoinc() */ +#include "log.h" Atomic_counter btr_validate_index_running; @@ -1267,53 +1268,71 @@ btr_read_autoinc(dict_index_t* index) return autoinc; } +dict_index_t *dict_table_t::get_index(const dict_col_t &col) const +{ + dict_index_t *index= dict_table_get_first_index(this); + + while (index && (index->fields[0].col != &col || index->is_corrupted())) + index= dict_table_get_next_index(index); + + return index; +} + /** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC, or fall back to MAX(auto_increment_column). -@param[in] table table containing an AUTO_INCREMENT column -@param[in] col_no index of the AUTO_INCREMENT column -@return the AUTO_INCREMENT value -@retval 0 on error or if no AUTO_INCREMENT value was used yet */ -ib_uint64_t -btr_read_autoinc_with_fallback(const dict_table_t* table, unsigned col_no) +@param table table containing an AUTO_INCREMENT column +@param col_no index of the AUTO_INCREMENT column +@param mysql_version TABLE_SHARE::mysql_version +@param max the maximum value of the AUTO_INCREMENT column +@return the AUTO_INCREMENT value +@retval 0 on error or if no AUTO_INCREMENT value was used yet */ +uint64_t btr_read_autoinc_with_fallback(const dict_table_t *table, + unsigned col_no, ulong mysql_version, + uint64_t max) { - ut_ad(table->persistent_autoinc); - ut_ad(!table->is_temporary()); + ut_ad(table->persistent_autoinc); + ut_ad(!table->is_temporary()); - dict_index_t* index = dict_table_get_first_index(table); + uint64_t autoinc= 0; + mtr_t mtr; + mtr.start(); - if (index == NULL) { - return 0; - } + if (buf_block_t *block= + buf_page_get(page_id_t(table->space_id, + dict_table_get_first_index(table)->page), + table->space->zip_size(), RW_SX_LATCH, &mtr)) + { + autoinc= page_get_autoinc(block->frame); - mtr_t mtr; - mtr.start(); - buf_block_t* block = buf_page_get( - page_id_t(index->table->space_id, index->page), - index->table->space->zip_size(), - RW_S_LATCH, &mtr); + if (autoinc > 0 && autoinc <= max && mysql_version >= 100210); + else if (dict_index_t *index= + table->get_index(*dict_table_get_nth_col(table, col_no))) + { + /* Read MAX(autoinc_col), in case this table had originally been + created before MariaDB 10.2.4 introduced persistent AUTO_INCREMENT + and MariaDB 10.2.10 fixed MDEV-12123, and there could be a garbage + value in the PAGE_ROOT_AUTO_INC field. */ + const uint64_t max_autoinc= row_search_max_autoinc(index); + const bool need_adjust{autoinc > max || autoinc < max_autoinc}; + ut_ad(max_autoinc <= max); - ib_uint64_t autoinc = block ? page_get_autoinc(block->frame) : 0; - const bool retry = block && autoinc == 0 - && !page_is_empty(block->frame); - mtr.commit(); + if (UNIV_UNLIKELY(need_adjust) && !high_level_read_only && !opt_readonly) + { + sql_print_information("InnoDB: Resetting PAGE_ROOT_AUTO_INC from " + UINT64PF " to " UINT64PF + " on table %`.*s.%`s (created with version %lu)", + autoinc, max_autoinc, + int(table->name.dblen()), table->name.m_name, + table->name.basename(), mysql_version); + autoinc= max_autoinc; + index->set_modified(mtr); + page_set_autoinc(block, max_autoinc, &mtr, true); + } + } + } - if (retry) { - /* This should be an old data file where - PAGE_ROOT_AUTO_INC was initialized to 0. - Fall back to reading MAX(autoinc_col). - There should be an index on it. */ - const dict_col_t* autoinc_col - = dict_table_get_nth_col(table, col_no); - while (index && index->fields[0].col != autoinc_col) { - index = dict_table_get_next_index(index); - } - - if (index) { - autoinc = row_search_max_autoinc(index); - } - } - - return autoinc; + mtr.commit(); + return autoinc; } /** Write the next available AUTO_INCREMENT value to PAGE_ROOT_AUTO_INC. diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 62b7fef4098..80c0df86d77 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -54,10 +54,13 @@ this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #include "sql_type_geom.h" #include "scope.h" #include "srv0srv.h" +extern my_bool opt_readonly; + // MYSQL_PLUGIN_IMPORT extern my_bool lower_case_file_system; // MYSQL_PLUGIN_IMPORT extern char mysql_unpacked_real_data_home[]; @@ -117,6 +120,7 @@ this program; if not, write to the Free Software Foundation, Inc., #include "row0ext.h" #include +#include // TT_FOR_UPGRADE #define thd_get_trx_isolation(X) ((enum_tx_isolation)thd_tx_isolation(X)) @@ -5620,14 +5624,10 @@ func_exit: return ret; } -/********************************************************************//** -Get the upper limit of the MySQL integral and floating-point type. -@return maximum allowed value for the field */ -UNIV_INTERN -ulonglong -innobase_get_int_col_max_value( -/*===========================*/ - const Field* field) /*!< in: MySQL field */ +/** Get the maximum integer value of a numeric column. +@param field column definition +@return maximum allowed integer value */ +ulonglong innobase_get_int_col_max_value(const Field *field) { ulonglong max_value = 0; @@ -5691,46 +5691,45 @@ ha_innobase::open(). @param[in,out] table persistent table @param[in] field the AUTO_INCREMENT column */ -static -void -initialize_auto_increment(dict_table_t* table, const Field* field) +static void initialize_auto_increment(dict_table_t *table, const Field& field, + const TABLE_SHARE &s) { - ut_ad(!table->is_temporary()); + ut_ad(!table->is_temporary()); + const unsigned col_no= innodb_col_no(&field); + table->autoinc_mutex.lock(); + table->persistent_autoinc= + uint16_t(dict_table_get_nth_col_pos(table, col_no, nullptr) + 1) & + dict_index_t::MAX_N_FIELDS; + if (table->autoinc) + /* Already initialized. Our caller checked + table->persistent_autoinc without + autoinc_mutex protection, and there might be multiple + ha_innobase::open() executing concurrently. */; + else if (srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN) + /* If innodb_force_recovery is set so high that writes + are disabled we force the AUTOINC counter to 0 + value effectively disabling writes to the table. + Secondly, we avoid reading the table in case the read + results in failure due to a corrupted table/index. - const unsigned col_no = innodb_col_no(field); + We will not return an error to the client, so that the + tables can be dumped with minimal hassle. If an error + were returned in this case, the first attempt to read + the table would fail and subsequent SELECTs would succeed. */; + else if (table->persistent_autoinc) + { + uint64_t max_value= innobase_get_int_col_max_value(&field); + table->autoinc= + innobase_next_autoinc(btr_read_autoinc_with_fallback(table, col_no, + s.mysql_version, + max_value), + 1 /* need */, + 1 /* auto_increment_increment */, + 0 /* auto_increment_offset */, + max_value); + } - table->autoinc_mutex.lock(); - - table->persistent_autoinc = static_cast( - dict_table_get_nth_col_pos(table, col_no, NULL) + 1) - & dict_index_t::MAX_N_FIELDS; - - if (table->autoinc) { - /* Already initialized. Our caller checked - table->persistent_autoinc without - autoinc_mutex protection, and there might be multiple - ha_innobase::open() executing concurrently. */ - } else if (srv_force_recovery > SRV_FORCE_NO_IBUF_MERGE) { - /* If the recovery level is set so high that writes - are disabled we force the AUTOINC counter to 0 - value effectively disabling writes to the table. - Secondly, we avoid reading the table in case the read - results in failure due to a corrupted table/index. - - We will not return an error to the client, so that the - tables can be dumped with minimal hassle. If an error - were returned in this case, the first attempt to read - the table would fail and subsequent SELECTs would succeed. */ - } else if (table->persistent_autoinc) { - table->autoinc = innobase_next_autoinc( - btr_read_autoinc_with_fallback(table, col_no), - 1 /* need */, - 1 /* auto_increment_increment */, - 0 /* auto_increment_offset */, - innobase_get_int_col_max_value(field)); - } - - table->autoinc_mutex.unlock(); + table->autoinc_mutex.unlock(); } /** Open an InnoDB table @@ -5966,7 +5965,7 @@ ha_innobase::open(const char* name, int, uint) || m_prebuilt->table->persistent_autoinc || !m_prebuilt->table->is_readable()) { } else if (const Field* ai = table->found_next_number_field) { - initialize_auto_increment(m_prebuilt->table, ai); + initialize_auto_increment(m_prebuilt->table, *ai, *table->s); } /* Set plugin parser for fulltext index */ @@ -14822,12 +14821,34 @@ ha_innobase::check( ulint n_rows_in_table = ULINT_UNDEFINED; bool is_ok = true; dberr_t ret; + uint handler_flags= check_opt->handler_flags; DBUG_ENTER("ha_innobase::check"); DBUG_ASSERT(thd == ha_thd()); ut_a(m_prebuilt->trx->magic_n == TRX_MAGIC_N); ut_a(m_prebuilt->trx == thd_to_trx(thd)); + if (handler_flags || check_for_upgrade(check_opt)) { + /* The file was already checked and fixed as part of open */ + print_check_msg(thd, table->s->db.str, table->s->table_name.str, + "check", "note", + (opt_readonly || high_level_read_only + || !(check_opt->sql_flags & TT_FOR_UPGRADE)) + ? "Auto_increment will be" + " checked on each open until" + " CHECK TABLE FOR UPGRADE is executed" + : "Auto_increment checked and" + " .frm file version updated", 1); + if (handler_flags && (check_opt->sql_flags & TT_FOR_UPGRADE)) { + /* + No other issues found (as handler_flags was only + set if there as not other problems with the table + than auto_increment). + */ + DBUG_RETURN(HA_ADMIN_OK); + } + } + if (m_prebuilt->mysql_template == NULL) { /* Build the template; we will use a dummy template in index scans done in checking */ @@ -15053,6 +15074,35 @@ ha_innobase::check( DBUG_RETURN(is_ok ? HA_ADMIN_OK : HA_ADMIN_CORRUPT); } +/** +Check if we there is a problem with the InnoDB table. +@param check_opt check options +@retval HA_ADMIN_OK if Table is ok +@retval HA_ADMIN_NEEDS_ALTER User should run ALTER TABLE FOR UPGRADE +@retval HA_ADMIN_NEEDS_CHECK User should run CHECK TABLE FOR UPGRADE +@retval HA_ADMIN_FAILED if InnoDB is in read-only mode */ +int ha_innobase::check_for_upgrade(HA_CHECK_OPT *check_opt) +{ + /* + Check if there is a possibility that the auto increment value + stored in PAGE_ROOT_AUTO_INC could be corrupt. + */ + if (table->s->mysql_version >= 100210); + else if (const Field *auto_increment= table->found_next_number_field) + { + uint col_no= innodb_col_no(auto_increment); + const dict_col_t *autoinc_col= + dict_table_get_nth_col(m_prebuilt->table, col_no); + if (m_prebuilt->table->get_index(*autoinc_col)) + { + check_opt->handler_flags= 1; + return (high_level_read_only && !opt_readonly) + ? HA_ADMIN_FAILED : HA_ADMIN_NEEDS_CHECK; + } + } + return HA_ADMIN_OK; +} + /*******************************************************************//** Gets the foreign key create info for a table stored in InnoDB. @return own: character string in the form which can be inserted to the diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 13d36e71e40..ba4f5b62f90 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -207,6 +207,7 @@ public: int rename_table(const char* from, const char* to) override; inline int defragment_table(const char* name); int check(THD* thd, HA_CHECK_OPT* check_opt) override; + int check_for_upgrade(HA_CHECK_OPT* check_opt) override; inline void reload_statistics(); @@ -944,6 +945,12 @@ unsigned innodb_col_no(const Field* field) MY_ATTRIBUTE((nonnull, warn_unused_result)); +/** Get the maximum integer value of a numeric column. +@param field column definition +@return maximum allowed integer value */ +ulonglong innobase_get_int_col_max_value(const Field *field) + MY_ATTRIBUTE((nonnull, warn_unused_result)); + /********************************************************************//** Helper function to push frm mismatch error to error log and if needed to sql-layer. */ diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 3737886814b..7e3979dce85 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -1179,15 +1179,6 @@ private: ha_innobase_inplace_ctx& operator=(const ha_innobase_inplace_ctx&); }; -/********************************************************************//** -Get the upper limit of the MySQL integral and floating-point type. -@return maximum allowed value for the field */ -UNIV_INTERN -ulonglong -innobase_get_int_col_max_value( -/*===========================*/ - const Field* field); /*!< in: MySQL field */ - /* Report an InnoDB error to the client by invoking my_error(). */ static ATTRIBUTE_COLD __attribute__((nonnull)) void @@ -9736,13 +9727,7 @@ commit_set_autoinc( const dict_col_t* autoinc_col = dict_table_get_nth_col(ctx->old_table, innodb_col_no(ai)); - dict_index_t* index - = dict_table_get_first_index(ctx->old_table); - while (index != NULL - && index->fields[0].col != autoinc_col) { - index = dict_table_get_next_index(index); - } - + auto index = ctx->old_table->get_index(*autoinc_col); ut_ad(index); ib_uint64_t max_in_table = index diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index c0dcc6f39d3..be5a16a2e6d 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -372,13 +372,16 @@ btr_read_autoinc(dict_index_t* index) /** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC, or fall back to MAX(auto_increment_column). -@param[in] table table containing an AUTO_INCREMENT column -@param[in] col_no index of the AUTO_INCREMENT column -@return the AUTO_INCREMENT value -@retval 0 on error or if no AUTO_INCREMENT value was used yet */ -ib_uint64_t -btr_read_autoinc_with_fallback(const dict_table_t* table, unsigned col_no) - MY_ATTRIBUTE((nonnull, warn_unused_result)); +@param table table containing an AUTO_INCREMENT column +@param col_no index of the AUTO_INCREMENT column +@param mysql_version TABLE_SHARE::mysql_version +@param max the maximum value of the AUTO_INCREMENT column +@return the AUTO_INCREMENT value +@retval 0 on error or if no AUTO_INCREMENT value was used yet */ +uint64_t btr_read_autoinc_with_fallback(const dict_table_t *table, + unsigned col_no, ulong mysql_version, + uint64_t max) + MY_ATTRIBUTE((nonnull, warn_unused_result)); /** Write the next available AUTO_INCREMENT value to PAGE_ROOT_AUTO_INC. @param[in,out] index clustered index diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 0a28a6a9868..b2d7b647c85 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -2366,6 +2366,9 @@ public: /** @return number of unique columns in FTS_DOC_ID index */ unsigned fts_n_uniq() const { return versioned() ? 2 : 1; } + + /** @return the index for that starts with a specific column */ + dict_index_t *get_index(const dict_col_t &col) const; }; inline void dict_index_t::set_modified(mtr_t& mtr) const diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 7644091b21c..1b1eecada5b 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -48,6 +48,8 @@ Created 2012-02-08 by Sunny Bains. #include "snappy-c.h" #endif #include "log.h" +#include "table.h" +#include "ha_innodb.h" #include "scope.h" @@ -4333,6 +4335,37 @@ fil_tablespace_iterate( return(err); } +static void row_import_autoinc(dict_table_t *table, row_prebuilt_t *prebuilt, + uint64_t autoinc) +{ + if (!table->persistent_autoinc) + { + ut_ad(!autoinc); + return; + } + + if (autoinc) + { + btr_write_autoinc(dict_table_get_first_index(table), autoinc - 1); + autoinc_set: + table->autoinc= autoinc; + sql_print_information("InnoDB: %`.*s.%`s autoinc value set to " UINT64PF, + int(table->name.dblen()), table->name.m_name, + table->name.basename(), autoinc); + } + else if (TABLE *t= prebuilt->m_mysql_table) + { + if (const Field *ai= t->found_next_number_field) + { + autoinc= 1 + + btr_read_autoinc_with_fallback(table, innodb_col_no(ai), + t->s->mysql_version, + innobase_get_int_col_max_value(ai)); + goto autoinc_set; + } + } +} + /*****************************************************************//** Imports a tablespace. The space id in the .ibd file must match the space id of the table in the data dictionary. @@ -4719,16 +4752,7 @@ row_import_for_mysql( /* Set autoinc value read from .cfg file, if one was specified. Otherwise, read the PAGE_ROOT_AUTO_INC and set it to table autoinc. */ - if (autoinc) { - ib::info() << table->name << " autoinc value set to " - << autoinc; - - table->autoinc = autoinc--; - btr_write_autoinc(dict_table_get_first_index(table), autoinc); - } else if (table->persistent_autoinc) { - autoinc = btr_read_autoinc(dict_table_get_first_index(table)); - table->autoinc = ++autoinc; - } + row_import_autoinc(table, prebuilt, autoinc); return(row_import_cleanup(prebuilt, trx, err)); } diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 35d1aa52bcd..e5ae6d052ee 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -45,6 +45,7 @@ C_MODE_END #include "key.h" #include "log.h" #include "sql_parse.h" +#include "mysql/service_print_check_msg.h" /* Note that in future versions, only *transactional* Maria tables can @@ -427,10 +428,8 @@ static void _ma_check_print_msg(HA_CHECK *param, const char *msg_type, const char *fmt, va_list args) { THD *thd= (THD *) param->thd; - Protocol *protocol= thd->protocol; - size_t length, msg_length; + size_t msg_length __attribute__((unused)); char msgbuf[MYSQL_ERRMSG_SIZE]; - char name[NAME_LEN * 2 + 2]; if (param->testflag & T_SUPPRESS_ERR_HANDLING) return; @@ -459,27 +458,10 @@ static void _ma_check_print_msg(HA_CHECK *param, const char *msg_type, _ma_check_print(param, msg_type, msgbuf); return; } - length= (uint) (strxmov(name, param->db_name, ".", param->table_name, - NullS) - name); - /* - TODO: switch from protocol to push_warning here. The main reason we didn't - it yet is parallel repair, which threads have no THD object accessible via - current_thd. - - Also we likely need to lock mutex here (in both cases with protocol and - push_warning). - */ - protocol->prepare_for_resend(); - protocol->store(name, (uint)length, system_charset_info); - protocol->store(param->op_name, system_charset_info); - protocol->store(msg_type, system_charset_info); - protocol->store(msgbuf, (uint)msg_length, system_charset_info); - if (protocol->write()) - sql_print_error("Failed on my_net_write, writing to stderr instead: %s.%s: %s\n", - param->db_name, param->table_name, msgbuf); - else if (thd->variables.log_warnings > 2) + print_check_msg(thd, param->db_name, param->table_name, + param->op_name, msg_type, msgbuf, 0); + if (thd->variables.log_warnings > 2) _ma_check_print(param, msg_type, msgbuf); - return; }