diff --git a/mysql-test/include/kill_and_restart_mysqld.inc b/mysql-test/include/kill_and_restart_mysqld.inc new file mode 100644 index 00000000000..f2ac9b504d2 --- /dev/null +++ b/mysql-test/include/kill_and_restart_mysqld.inc @@ -0,0 +1,19 @@ +--let $_server_id= `SELECT @@server_id` +--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect + +if ($restart_parameters) +{ + --echo # Kill and restart: $restart_parameters + --exec echo "restart: $restart_parameters" > $_expect_file_name +} +if (!$restart_parameters) +{ + --echo # Kill and restart + --exec echo "restart" > $_expect_file_name +} + +--shutdown_server 0 +--source include/wait_until_disconnected.inc +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect diff --git a/mysql-test/suite/innodb/include/autoinc_persist_alter.inc b/mysql-test/suite/innodb/include/autoinc_persist_alter.inc new file mode 100644 index 00000000000..ddff573b056 --- /dev/null +++ b/mysql-test/suite/innodb/include/autoinc_persist_alter.inc @@ -0,0 +1,61 @@ + +eval CREATE TABLE $table LIKE $template; + +eval INSERT INTO $table SELECT * FROM $template; + +eval SELECT * FROM $table; + +eval SHOW CREATE TABLE $table; + +--echo # This will keep the autoinc counter +eval ALTER TABLE $table AUTO_INCREMENT = 250, ALGORITHM = $algorithm; +--echo # We expect the counter to be 250 +eval SHOW CREATE TABLE $table; + +--echo # This should keep the autoinc counter as well +eval ALTER TABLE $table ADD COLUMN b INT, ALGORITHM = $algorithm; +--echo # We expect the counter to be 250 +eval SHOW CREATE TABLE $table; + +eval DELETE FROM $table WHERE a > 150; + +eval SELECT * FROM $table; + +--echo # This should reset the autoinc counter to the one specified +--echo # Since it's smaller than current one but bigger than existing +--echo # biggest counter in the table +eval ALTER TABLE $table AUTO_INCREMENT = 180, ALGORITHM = $algorithm; +--echo # We expect the counter to be 180 +eval SHOW CREATE TABLE $table; + +--echo # This should reset the autoinc counter to the next value of +--echo # current max counter in the table, since the specified value +--echo # is smaller than the existing biggest value(50 < 123) +eval ALTER TABLE $table DROP COLUMN b, AUTO_INCREMENT = 50, ALGORITHM = $algorithm; +--echo # We expect the counter to be 123 +eval SHOW CREATE TABLE $table; + +eval INSERT INTO $table VALUES(0), (0); + +eval SELECT MAX(a) AS `Expect 124` FROM $table; + +eval OPTIMIZE TABLE $table; + +eval SHOW CREATE TABLE $table; + +--source include/restart_mysqld.inc + +--echo # We expect the counter to still be 125 +eval SHOW CREATE TABLE $table; + +eval DELETE FROM $table WHERE a >= 123; + +eval CREATE UNIQUE INDEX idx_aa ON $table(a); + +--source include/restart_mysqld.inc + +eval INSERT INTO $table VALUES(0), (0); + +eval SELECT MAX(a) AS `Expect 126` FROM $table; + +eval DROP TABLE $table; diff --git a/mysql-test/suite/innodb/r/autoinc_persist.result b/mysql-test/suite/innodb/r/autoinc_persist.result new file mode 100644 index 00000000000..b9eefe30ddc --- /dev/null +++ b/mysql-test/suite/innodb/r/autoinc_persist.result @@ -0,0 +1,1131 @@ +# +# MDEV-6076 Persistent AUTO_INCREMENT for InnoDB +# +# WL#6204 InnoDB persistent max value for autoinc columns +# +# Most of this test case is copied from the test innodb.autoinc_persist +# that was introduced in MySQL 8.0.0. The observable behaviour +# of MDEV-6076 is equivalent to WL#6204, with the exception that +# there is less buffering taking place and redo log checkpoints +# are not being treated specially. +# Due to less buffering, there is no debug instrumentation testing +# for MDEV-6076. +# +# Pre-create several tables +SET SQL_MODE='STRICT_ALL_TABLES'; +CREATE TABLE t1(a TINYINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t1 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31); +SELECT * FROM t1; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +CREATE TABLE t2(a TINYINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t2 VALUES(-5); +ERROR 22003: Out of range value for column 'a' at row 1 +INSERT INTO t2 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31); +SELECT * FROM t2; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +CREATE TABLE t3(a SMALLINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t3 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (1024), (4096); +SELECT * FROM t3; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +1024 +4096 +CREATE TABLE t4(a SMALLINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t4 VALUES(-5); +ERROR 22003: Out of range value for column 'a' at row 1 +INSERT INTO t4 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (1024), (4096); +SELECT * FROM t4; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +1024 +4096 +CREATE TABLE t5(a MEDIUMINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t5 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (1000000), (1000005); +SELECT * FROM t5; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +1000000 +1000005 +CREATE TABLE t6(a MEDIUMINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t6 VALUES(-5); +ERROR 22003: Out of range value for column 'a' at row 1 +INSERT INTO t6 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (1000000), (1000005); +SELECT * FROM t6; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +1000000 +1000005 +CREATE TABLE t7(a INT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t7 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (100000000), (100000008); +SELECT * FROM t7; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +100000000 +100000008 +CREATE TABLE t8(a INT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t8 VALUES(-5); +ERROR 22003: Out of range value for column 'a' at row 1 +INSERT INTO t8 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (100000000), (100000008); +SELECT * FROM t8; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +100000000 +100000008 +CREATE TABLE t9(a BIGINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t9 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (100000000000), (100000000006); +SELECT * FROM t9; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +100000000000 +100000000006 +CREATE TABLE t10(a BIGINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t10 VALUES(-5); +ERROR 22003: Out of range value for column 'a' at row 1 +INSERT INTO t10 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (100000000000), (100000000006); +SELECT * FROM t10; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +100000000000 +100000000006 +CREATE TABLE t11(a FLOAT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31); +SELECT * FROM t11; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +set global innodb_flush_log_at_trx_commit=1; +CREATE TABLE t12(a DOUBLE AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31); +SELECT * FROM t12; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +CREATE TABLE t13(a INT AUTO_INCREMENT PRIMARY KEY) ENGINE = InnoDB, +AUTO_INCREMENT = 1234; +# Scenario 1: Normal restart, to test if the counters are persisted +# We expect these results should be equal to above SELECTs +SELECT * FROM t1; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +SELECT * FROM t2; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +SELECT * FROM t3; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +1024 +4096 +SELECT * FROM t4; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +1024 +4096 +SELECT * FROM t5; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +1000000 +1000005 +SELECT * FROM t6; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +1000000 +1000005 +SELECT * FROM t7; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +100000000 +100000008 +SELECT * FROM t8; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +100000000 +100000008 +SELECT * FROM t9; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +100000000000 +100000000006 +SELECT * FROM t10; +a +1 +2 +3 +4 +8 +10 +11 +20 +30 +31 +100000000000 +100000000006 +SELECT * FROM t11; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +SELECT * FROM t12; +a +-10 +-1 +1 +2 +3 +4 +5 +20 +30 +31 +SELECT * FROM t13; +a +SHOW CREATE TABLE t13; +Table Create Table +t13 CREATE TABLE `t13` ( + `a` int(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=1234 DEFAULT CHARSET=latin1 +INSERT INTO t13 VALUES(0); +SELECT a AS `Expect 1234` FROM t13; +Expect 1234 +1234 +# Scenario 2: Delete some values, to test the counters should not be the +# one which is the largest in current table +set global innodb_flush_log_at_trx_commit=1; +DELETE FROM t1 WHERE a > 30; +SELECT MAX(a) AS `Expect 30` FROM t1; +Expect 30 +30 +DELETE FROM t3 WHERE a > 2000; +SELECT MAX(a) AS `Expect 2000` FROM t3; +Expect 2000 +1024 +DELETE FROM t5 WHERE a > 1000000; +SELECT MAX(a) AS `Expect 1000000` FROM t5; +Expect 1000000 +1000000 +DELETE FROM t7 WHERE a > 100000000; +SELECT MAX(a) AS `Expect 100000000` FROM t7; +Expect 100000000 +100000000 +DELETE FROM t9 WHERE a > 100000000000; +SELECT MAX(a) AS `Expect 100000000000` FROM t9; +Expect 100000000000 +100000000000 +INSERT INTO t1 VALUES(0), (0); +SELECT MAX(a) AS `Expect 33` FROM t1; +Expect 33 +33 +INSERT INTO t3 VALUES(0), (0); +SELECT MAX(a) AS `Expect 4098` FROM t3; +Expect 4098 +4098 +INSERT INTO t5 VALUES(0), (0); +SELECT MAX(a) AS `Expect 1000007` FROM t5; +Expect 1000007 +1000007 +INSERT INTO t7 VALUES(0), (0); +SELECT MAX(a) AS `Expect 100000010` FROM t7; +Expect 100000010 +100000010 +INSERT INTO t9 VALUES(0), (0); +SELECT MAX(a) AS `Expect 100000000008` FROM t9; +Expect 100000000008 +100000000008 +# Scenario 3: Insert some bigger counters, the next counter should start +# from there +INSERT INTO t1 VALUES(40), (0); +INSERT INTO t1 VALUES(42), (0); +SELECT a AS `Expect 43, 42` FROM t1 ORDER BY a DESC LIMIT 4; +Expect 43, 42 +43 +42 +41 +40 +INSERT INTO t3 VALUES(5000), (0); +INSERT INTO t3 VALUES(5010), (0); +SELECT a AS `Expect 5011, 5010` FROM t3 ORDER BY a DESC LIMIT 4; +Expect 5011, 5010 +5011 +5010 +5001 +5000 +INSERT INTO t5 VALUES(1000010), (0); +INSERT INTO t5 VALUES(1000020), (0); +SELECT a AS `Expect 1000021, 1000020` FROM t5 ORDER BY a DESC LIMIT 4; +Expect 1000021, 1000020 +1000021 +1000020 +1000011 +1000010 +INSERT INTO t7 VALUES(100000020), (0); +INSERT INTO t7 VALUES(100000030), (0); +SELECT a AS `Expect 100000031, 100000030` FROM t7 ORDER BY a DESC LIMIT 4; +Expect 100000031, 100000030 +100000031 +100000030 +100000021 +100000020 +INSERT INTO t9 VALUES(100000000010), (0); +INSERT INTO t9 VALUES(100000000020), (0); +SELECT a AS `Expect 100000000021, 100000000020` FROM t9 ORDER BY a DESC LIMIT 4; +Expect 100000000021, 100000000020 +100000000021 +100000000020 +100000000011 +100000000010 +# Scenario 4: Update some values, to test the counters should be updated +# to the bigger value, but not smaller value. +INSERT INTO t1 VALUES(50), (55); +UPDATE t1 SET a = 105 WHERE a = 5; +UPDATE t1 SET a = 100 WHERE a = 55; +# This should insert 102, 106, 107, and make next counter 109. +INSERT INTO t1 VALUES(102), (0), (0); +SELECT a AS `Expect 107, 106` FROM t1 ORDER BY a DESC LIMIT 2; +Expect 107, 106 +107 +106 +DELETE FROM t1 WHERE a > 105; +INSERT INTO t1 VALUES(0); +SELECT MAX(a) AS `Expect 109` FROM t1; +Expect 109 +109 +# Test the same things on t3, t5, t7, t9, to test if DDTableBuffer would +# be updated accordingly +INSERT INTO t3 VALUES(60), (65); +UPDATE t3 SET a = 6005 WHERE a = 5; +UPDATE t3 SET a = 6000 WHERE a = 60; +# This should insert 6002, 6006, 6007, and make next counter 6009. +INSERT INTO t3 VALUES(6002), (0), (0); +SELECT a AS `Expect 6007, 6006` FROM t3 ORDER BY a DESC LIMIT 2; +Expect 6007, 6006 +6007 +6006 +DELETE FROM t3 WHERE a > 6005; +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 6009` FROM t3; +Expect 6009 +6009 +INSERT INTO t5 VALUES(100), (200); +UPDATE t5 SET a = 1000105 WHERE a = 5; +UPDATE t5 SET a = 1000100 WHERE a = 100; +# This should insert 1000102, 1000106, 1000107, and make next counter +# 1000109. +INSERT INTO t5 VALUES(1000102), (0), (0); +SELECT a AS `Expect 1000107, 1000106` FROM t5 ORDER BY a DESC LIMIT 2; +Expect 1000107, 1000106 +1000107 +1000106 +DELETE FROM t5 WHERE a > 1000105; +INSERT INTO t5 VALUES(0); +SELECT MAX(a) AS `Expect 1000109` FROM t5; +Expect 1000109 +1000109 +INSERT INTO t7 VALUES(100), (200); +UPDATE t7 SET a = 100000105 WHERE a = 5; +UPDATE t7 SET a = 100000100 WHERE a = 100; +# This should insert 100000102, 1100000106, 100000107, and make next +# counter 100000109. +INSERT INTO t7 VALUES(100000102), (0), (0); +SELECT a AS `Expect 100000107, 100000106` FROM t7 ORDER BY a DESC LIMIT 2; +Expect 100000107, 100000106 +100000107 +100000106 +DELETE FROM t7 WHERE a > 100000105; +INSERT INTO t7 VALUES(0); +SELECT MAX(a) AS `Expect 100000109` FROM t7; +Expect 100000109 +100000109 +set global innodb_flush_log_at_trx_commit=1; +INSERT INTO t9 VALUES(100), (200); +UPDATE t9 SET a = 100000000105 WHERE a = 5; +UPDATE t9 SET a = 100000000100 WHERE a = 100; +# This should insert 100000000102, 100000000106, 100000000107, and make +# next counter 100000000109. +INSERT INTO t9 VALUES(100000000102), (0), (0); +SELECT a AS `Expect 100000000107, 100000000106` FROM t9 ORDER BY a DESC LIMIT 2; +Expect 100000000107, 100000000106 +100000000107 +100000000106 +DELETE FROM t9 WHERE a > 100000000105; +INSERT INTO t9 VALUES(0); +SELECT MAX(a) AS `Expect 100000000109` FROM t9; +Expect 100000000109 +100000000109 +INSERT INTO t1 VALUES(0), (0); +SELECT a AS `Expect 110, 111` FROM t1 ORDER BY a DESC LIMIT 2; +Expect 110, 111 +111 +110 +INSERT INTO t3 VALUES(0), (0); +SELECT a AS `Expect 6010, 6011` FROM t3 ORDER BY a DESC LIMIT 2; +Expect 6010, 6011 +6011 +6010 +INSERT INTO t5 VALUES(0), (0); +SELECT a AS `Expect 1100111, 1100110` FROM t5 ORDER BY a DESC LIMIT 2; +Expect 1100111, 1100110 +1000111 +1000110 +INSERT INTO t7 VALUES(0), (0); +SELECT a AS `Expect 100000111, 100000110` FROM t7 ORDER BY a DESC LIMIT 2; +Expect 100000111, 100000110 +100000111 +100000110 +INSERT INTO t9 VALUES(0), (0); +SELECT a AS `Expect 100000000111, 100000000110` FROM t9 ORDER BY a DESC LIMIT 2; +Expect 100000000111, 100000000110 +100000000111 +100000000110 +# Scenario 5: Test kill the server +INSERT INTO t1 VALUES(125); +DELETE FROM t1 WHERE a = 125; +INSERT INTO t3 VALUES(6100); +DELETE FROM t3 WHERE a = 6100; +INSERT INTO t5 VALUES(1100200); +DELETE FROM t5 WHERE a = 1100200; +INSERT INTO t7 VALUES(100000200); +DELETE FROM t7 WHERE a = 100000200; +set global innodb_flush_log_at_trx_commit=1; +INSERT INTO t9 VALUES(100000000200); +DELETE FROM t9 WHERE a = 100000000200; +# Kill and restart +INSERT INTO t1 VALUES(0); +SELECT a AS `Expect 126` FROM t1 ORDER BY a DESC LIMIT 1; +Expect 126 +126 +INSERT INTO t3 VALUES(0); +SELECT a AS `Expect 6101` FROM t3 ORDER BY a DESC LIMIT 1; +Expect 6101 +6101 +INSERT INTO t5 VALUES(0); +SELECT a AS `Expect 1100201` FROM t5 ORDER BY a DESC LIMIT 1; +Expect 1100201 +1100201 +INSERT INTO t7 VALUES(0); +SELECT a AS `Expect 100000201` FROM t7 ORDER BY a DESC LIMIT 1; +Expect 100000201 +100000201 +INSERT INTO t9 VALUES(0); +SELECT a AS `Expect 100000000201` FROM t9 ORDER BY a DESC LIMIT 1; +Expect 100000000201 +100000000201 +# Scenario 6: Test truncate will reset the counters to 0 +TRUNCATE TABLE t1; +TRUNCATE TABLE t3; +TRUNCATE TABLE t5; +TRUNCATE TABLE t7; +TRUNCATE TABLE t9; +INSERT INTO t1 VALUES(0), (0); +SELECT * FROM t1; +a +1 +2 +INSERT INTO t3 VALUES(0), (0); +SELECT * FROM t3; +a +1 +2 +INSERT INTO t5 VALUES(0), (0); +SELECT * FROM t5; +a +1 +2 +INSERT INTO t7 VALUES(0), (0); +SELECT * FROM t7; +a +1 +2 +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; +a +1 +2 +set global innodb_flush_log_at_trx_commit=1; +TRUNCATE TABLE t1; +TRUNCATE TABLE t3; +TRUNCATE TABLE t5; +TRUNCATE TABLE t7; +TRUNCATE TABLE t9; +# Kill and restart +INSERT INTO t1 VALUES(0), (0); +SELECT * FROM t1; +a +1 +2 +INSERT INTO t3 VALUES(0), (0); +SELECT * FROM t3; +a +1 +2 +INSERT INTO t5 VALUES(0), (0); +SELECT * FROM t5; +a +1 +2 +INSERT INTO t7 VALUES(0), (0); +SELECT * FROM t7; +a +1 +2 +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; +a +1 +2 +# Scenario 7: Test explicit rename table won't change the counter +set global innodb_flush_log_at_trx_commit=1; +RENAME TABLE t9 to t19; +INSERT INTO t19 VALUES(0), (0); +SELECT * FROM t19; +a +1 +2 +3 +4 +DELETE FROM t19 WHERE a = 4; +# Kill and restart +RENAME TABLE t19 to t9; +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; +a +1 +2 +3 +5 +6 +TRUNCATE TABLE t9; +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; +a +1 +2 +# Scenario 8: Test ALTER TABLE operations +INSERT INTO t3 VALUES(0), (0), (100), (200), (1000); +SELECT * FROM t3; +a +1 +2 +3 +4 +100 +200 +1000 +DELETE FROM t3 WHERE a > 300; +SELECT MAX(a) AS `Expect 200` FROM t3; +Expect 200 +200 +# This will not change the counter to 150, but to 201, which is the next +# of current max counter in the table +ALTER TABLE t3 AUTO_INCREMENT = 150; +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=latin1 +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 201` FROM t3; +Expect 201 +201 +# This will change the counter to 500, which is bigger than any counter +# in the table +ALTER TABLE t3 AUTO_INCREMENT = 500; +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=latin1 +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 500` FROM t3; +Expect 500 +500 +TRUNCATE TABLE t3; +ALTER TABLE t3 AUTO_INCREMENT = 100; +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=latin1 +INSERT INTO t3 VALUES(0), (0); +SELECT * FROM t3; +a +100 +101 +INSERT INTO t3 VALUES(150), (180); +UPDATE t3 SET a = 200 WHERE a = 150; +INSERT INTO t3 VALUES(220); +# This still fails to set to 120, but just 221 +ALTER TABLE t3 AUTO_INCREMENT = 120; +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=221 DEFAULT CHARSET=latin1 +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 221` FROM t3; +Expect 221 +221 +DELETE FROM t3 WHERE a > 120; +ALTER TABLE t3 AUTO_INCREMENT = 120; +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=120 DEFAULT CHARSET=latin1 +# MDEV-6076: Test adding an AUTO_INCREMENT COLUMN +CREATE TABLE mdev6076a (b INT) ENGINE=InnoDB; +INSERT INTO mdev6076a VALUES(2),(1); +CREATE TABLE mdev6076b (b INT) ENGINE=InnoDB; +INSERT INTO mdev6076b VALUES(2),(1); +ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, LOCK=NONE; +ERROR 0A000: LOCK=NONE is not supported. Reason: Adding an auto-increment column requires a lock. Try LOCK=SHARED +ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, ALGORITHM=INPLACE; +ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100, +ALGORITHM=INPLACE; +# Kill and restart +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 120` FROM t3; +Expect 120 +120 +INSERT INTO mdev6076a SET b=0; +SELECT * FROM mdev6076a; +a b +1 2 +2 1 +3 0 +INSERT INTO mdev6076b SET b=0; +SELECT * FROM mdev6076b; +a b +100 2 +101 1 +102 0 +DROP TABLE mdev6076a, mdev6076b; +set global innodb_flush_log_at_trx_commit=1; +INSERT INTO t3 VALUES(0), (0), (200), (210); +# Test the different algorithms in ALTER TABLE +CREATE TABLE t_inplace LIKE t3; +INSERT INTO t_inplace SELECT * FROM t3; +SELECT * FROM t_inplace; +a +100 +101 +120 +121 +122 +200 +210 +SHOW CREATE TABLE t_inplace; +Table Create Table +t_inplace CREATE TABLE `t_inplace` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=211 DEFAULT CHARSET=latin1 +# This will keep the autoinc counter +ALTER TABLE t_inplace AUTO_INCREMENT = 250, ALGORITHM = INPLACE; +# We expect the counter to be 250 +SHOW CREATE TABLE t_inplace; +Table Create Table +t_inplace CREATE TABLE `t_inplace` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=250 DEFAULT CHARSET=latin1 +# This should keep the autoinc counter as well +ALTER TABLE t_inplace ADD COLUMN b INT, ALGORITHM = INPLACE; +# We expect the counter to be 250 +SHOW CREATE TABLE t_inplace; +Table Create Table +t_inplace CREATE TABLE `t_inplace` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=250 DEFAULT CHARSET=latin1 +DELETE FROM t_inplace WHERE a > 150; +SELECT * FROM t_inplace; +a b +100 NULL +101 NULL +120 NULL +121 NULL +122 NULL +# This should reset the autoinc counter to the one specified +# Since it's smaller than current one but bigger than existing +# biggest counter in the table +ALTER TABLE t_inplace AUTO_INCREMENT = 180, ALGORITHM = INPLACE; +# We expect the counter to be 180 +SHOW CREATE TABLE t_inplace; +Table Create Table +t_inplace CREATE TABLE `t_inplace` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=180 DEFAULT CHARSET=latin1 +# This should reset the autoinc counter to the next value of +# current max counter in the table, since the specified value +# is smaller than the existing biggest value(50 < 123) +ALTER TABLE t_inplace DROP COLUMN b, AUTO_INCREMENT = 50, ALGORITHM = INPLACE; +# We expect the counter to be 123 +SHOW CREATE TABLE t_inplace; +Table Create Table +t_inplace CREATE TABLE `t_inplace` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=123 DEFAULT CHARSET=latin1 +INSERT INTO t_inplace VALUES(0), (0); +SELECT MAX(a) AS `Expect 124` FROM t_inplace; +Expect 124 +124 +OPTIMIZE TABLE t_inplace; +Table Op Msg_type Msg_text +test.t_inplace optimize note Table does not support optimize, doing recreate + analyze instead +test.t_inplace optimize status OK +SHOW CREATE TABLE t_inplace; +Table Create Table +t_inplace CREATE TABLE `t_inplace` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=latin1 +# We expect the counter to still be 125 +SHOW CREATE TABLE t_inplace; +Table Create Table +t_inplace CREATE TABLE `t_inplace` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=latin1 +DELETE FROM t_inplace WHERE a >= 123; +CREATE UNIQUE INDEX idx_aa ON t_inplace(a); +INSERT INTO t_inplace VALUES(0), (0); +SELECT MAX(a) AS `Expect 126` FROM t_inplace; +Expect 126 +126 +DROP TABLE t_inplace; +CREATE TABLE t_copy LIKE t3; +INSERT INTO t_copy SELECT * FROM t3; +SELECT * FROM t_copy; +a +100 +101 +120 +121 +122 +200 +210 +SHOW CREATE TABLE t_copy; +Table Create Table +t_copy CREATE TABLE `t_copy` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=211 DEFAULT CHARSET=latin1 +# This will keep the autoinc counter +ALTER TABLE t_copy AUTO_INCREMENT = 250, ALGORITHM = COPY; +# We expect the counter to be 250 +SHOW CREATE TABLE t_copy; +Table Create Table +t_copy CREATE TABLE `t_copy` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=250 DEFAULT CHARSET=latin1 +# This should keep the autoinc counter as well +ALTER TABLE t_copy ADD COLUMN b INT, ALGORITHM = COPY; +# We expect the counter to be 250 +SHOW CREATE TABLE t_copy; +Table Create Table +t_copy CREATE TABLE `t_copy` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=250 DEFAULT CHARSET=latin1 +DELETE FROM t_copy WHERE a > 150; +SELECT * FROM t_copy; +a b +100 NULL +101 NULL +120 NULL +121 NULL +122 NULL +# This should reset the autoinc counter to the one specified +# Since it's smaller than current one but bigger than existing +# biggest counter in the table +ALTER TABLE t_copy AUTO_INCREMENT = 180, ALGORITHM = COPY; +# We expect the counter to be 180 +SHOW CREATE TABLE t_copy; +Table Create Table +t_copy CREATE TABLE `t_copy` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=180 DEFAULT CHARSET=latin1 +# This should reset the autoinc counter to the next value of +# current max counter in the table, since the specified value +# is smaller than the existing biggest value(50 < 123) +ALTER TABLE t_copy DROP COLUMN b, AUTO_INCREMENT = 50, ALGORITHM = COPY; +# We expect the counter to be 123 +SHOW CREATE TABLE t_copy; +Table Create Table +t_copy CREATE TABLE `t_copy` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=123 DEFAULT CHARSET=latin1 +INSERT INTO t_copy VALUES(0), (0); +SELECT MAX(a) AS `Expect 124` FROM t_copy; +Expect 124 +124 +OPTIMIZE TABLE t_copy; +Table Op Msg_type Msg_text +test.t_copy optimize note Table does not support optimize, doing recreate + analyze instead +test.t_copy optimize status OK +SHOW CREATE TABLE t_copy; +Table Create Table +t_copy CREATE TABLE `t_copy` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=latin1 +# We expect the counter to still be 125 +SHOW CREATE TABLE t_copy; +Table Create Table +t_copy CREATE TABLE `t_copy` ( + `a` smallint(6) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=latin1 +DELETE FROM t_copy WHERE a >= 123; +CREATE UNIQUE INDEX idx_aa ON t_copy(a); +INSERT INTO t_copy VALUES(0), (0); +SELECT MAX(a) AS `Expect 126` FROM t_copy; +Expect 126 +126 +DROP TABLE t_copy; +# Scenario 9: Test the sql_mode = NO_AUTO_VALUE_ON_ZERO +CREATE TABLE t30 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT, key(b)) ENGINE = InnoDB; +set SQL_MODE = NO_AUTO_VALUE_ON_ZERO; +INSERT INTO t30 VALUES(NULL, 1), (200, 2), (0, 3); +INSERT INTO t30(b) VALUES(4), (5), (6), (7); +SELECT * FROM t30 ORDER BY b; +a b +1 1 +200 2 +0 3 +201 4 +202 5 +203 6 +204 7 +ALTER TABLE t30 MODIFY b MEDIUMINT; +SELECT * FROM t30 ORDER BY b; +a b +1 1 +200 2 +0 3 +201 4 +202 5 +203 6 +204 7 +set global innodb_flush_log_at_trx_commit=1; +CREATE TABLE t31 (a INT) ENGINE = InnoDB; +INSERT INTO t31 VALUES(1), (2); +ALTER TABLE t31 ADD b INT AUTO_INCREMENT PRIMARY KEY; +INSERT INTO t31 VALUES(3, 0), (4, NULL), (5, NULL); +INSERT INTO t31 VALUES(6, 0); +ERROR 23000: Duplicate entry '0' for key 'PRIMARY' +SELECT * FROM t31; +a b +3 0 +1 1 +2 2 +4 3 +5 4 +# Kill and restart +# This will not insert 0 +INSERT INTO t31(a) VALUES(6), (0); +SELECT * FROM t31; +a b +3 0 +1 1 +2 2 +4 3 +5 4 +6 5 +0 6 +DROP TABLE t31; +set SQL_MODE = NO_AUTO_VALUE_ON_ZERO; +DELETE FROM t30 WHERE a = 0; +UPDATE t30 set a = 0 where b = 5; +SELECT * FROM t30 ORDER BY b; +a b +1 1 +200 2 +201 4 +0 5 +203 6 +204 7 +DELETE FROM t30 WHERE a = 0; +UPDATE t30 SET a = NULL WHERE b = 6; +Warnings: +Warning 1048 Column 'a' cannot be null +UPDATE t30 SET a = 300 WHERE b = 7; +SELECT * FROM t30 ORDER BY b; +a b +1 1 +200 2 +201 4 +0 6 +300 7 +SET SQL_MODE = 0; +# Scenario 10: Rollback would not rollback the counter +CREATE TABLE t32 ( +a BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +INSERT INTO t32 VALUES(0), (0); +set global innodb_flush_log_at_trx_commit=1; +START TRANSACTION; +INSERT INTO t32 VALUES(0), (0); +SELECT MAX(a) AS `Expect 4` FROM t32; +Expect 4 +4 +DELETE FROM t32 WHERE a >= 2; +ROLLBACK; +# Kill and restart +SELECT MAX(a) AS `Expect 2` FROM t32; +Expect 2 +2 +INSERT INTO t32 VALUES(0), (0); +SELECT MAX(a) AS `Expect 6` FROM t32; +Expect 6 +6 +# Scenario 11: Test duplicate primary key/secondary key will not stop +# increasing the counter +CREATE TABLE t33 ( +a BIGINT NOT NULL PRIMARY KEY, +b BIGINT NOT NULL AUTO_INCREMENT, +KEY(b)) ENGINE = InnoDB; +INSERT INTO t33 VALUES(1, NULL); +INSERT INTO t33 VALUES(2, NULL); +INSERT INTO t33 VALUES(2, NULL); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +INSERT INTO t33 VALUES(3, NULL); +SELECT MAX(b) AS `Expect 4` FROM t33; +Expect 4 +4 +TRUNCATE TABLE t33; +INSERT INTO t33 VALUES(1, NULL); +INSERT INTO t33 VALUES(2, NULL); +set global innodb_flush_log_at_trx_commit=1; +START TRANSACTION; +UPDATE t33 SET a = 10 WHERE a = 1; +INSERT INTO t33 VALUES(2, NULL); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +COMMIT; +# Kill and restart +INSERT INTO t33 VALUES(3, NULL); +SELECT MAX(b) AS `Expect 4` FROM t33; +Expect 4 +4 +DROP TABLE t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t30, t32, t33; diff --git a/mysql-test/suite/innodb/r/innodb-autoinc-44030.result b/mysql-test/suite/innodb/r/innodb-autoinc-44030.result index cf3ca93db27..5ec1bd38d80 100644 --- a/mysql-test/suite/innodb/r/innodb-autoinc-44030.result +++ b/mysql-test/suite/innodb/r/innodb-autoinc-44030.result @@ -3,15 +3,24 @@ CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; INSERT INTO t1 VALUES (null); INSERT INTO t1 VALUES (null); ALTER TABLE t1 CHANGE c1 d1 INT NOT NULL AUTO_INCREMENT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 SELECT * FROM t1; d1 1 2 -SELECT * FROM t1; -d1 -1 -2 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `d1` int(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`d1`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 INSERT INTO t1 VALUES(null); +SELECT * FROM t1; +d1 +1 +2 +3 ALTER TABLE t1 AUTO_INCREMENT = 3; affected rows: 0 info: Records: 0 Duplicates: 0 Warnings: 0 diff --git a/mysql-test/suite/innodb/r/innodb.result b/mysql-test/suite/innodb/r/innodb.result index 213fb8a7831..029753bc3ee 100644 --- a/mysql-test/suite/innodb/r/innodb.result +++ b/mysql-test/suite/innodb/r/innodb.result @@ -2429,11 +2429,17 @@ drop table t1; create table t1 (a int not null auto_increment primary key, val int) engine=InnoDB; insert into t1 (val) values (1); update t1 set a=2 where a=1; -insert into t1 (val) values (1); +insert into t1 (val) values (3); +select * from t1; +a val +2 1 +3 3 +insert into t1 values (2, 2); ERROR 23000: Duplicate entry '2' for key 'PRIMARY' select * from t1; a val 2 1 +3 3 drop table t1; CREATE TABLE t1 (GRADE DECIMAL(4) NOT NULL, PRIMARY KEY (GRADE)) ENGINE=INNODB; INSERT INTO t1 (GRADE) VALUES (151),(252),(343); diff --git a/mysql-test/suite/innodb/t/autoinc_persist.test b/mysql-test/suite/innodb/t/autoinc_persist.test new file mode 100644 index 00000000000..e4ea2db6bd2 --- /dev/null +++ b/mysql-test/suite/innodb/t/autoinc_persist.test @@ -0,0 +1,533 @@ +--source include/have_innodb.inc +--source include/not_embedded.inc + +--echo # +--echo # MDEV-6076 Persistent AUTO_INCREMENT for InnoDB +--echo # +--echo # WL#6204 InnoDB persistent max value for autoinc columns +--echo # +--echo # Most of this test case is copied from the test innodb.autoinc_persist +--echo # that was introduced in MySQL 8.0.0. The observable behaviour +--echo # of MDEV-6076 is equivalent to WL#6204, with the exception that +--echo # there is less buffering taking place and redo log checkpoints +--echo # are not being treated specially. +--echo # Due to less buffering, there is no debug instrumentation testing +--echo # for MDEV-6076. +--echo # + +--echo # Pre-create several tables + +SET SQL_MODE='STRICT_ALL_TABLES'; + +CREATE TABLE t1(a TINYINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t1 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31); +SELECT * FROM t1; + +CREATE TABLE t2(a TINYINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +--error ER_WARN_DATA_OUT_OF_RANGE +INSERT INTO t2 VALUES(-5); +INSERT INTO t2 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31); +SELECT * FROM t2; + +CREATE TABLE t3(a SMALLINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t3 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (1024), (4096); +SELECT * FROM t3; + +CREATE TABLE t4(a SMALLINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +--error ER_WARN_DATA_OUT_OF_RANGE +INSERT INTO t4 VALUES(-5); +INSERT INTO t4 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (1024), (4096); +SELECT * FROM t4; + +CREATE TABLE t5(a MEDIUMINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t5 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (1000000), (1000005); +SELECT * FROM t5; + +CREATE TABLE t6(a MEDIUMINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +--error ER_WARN_DATA_OUT_OF_RANGE +INSERT INTO t6 VALUES(-5); +INSERT INTO t6 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (1000000), (1000005); +SELECT * FROM t6; + +CREATE TABLE t7(a INT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t7 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (100000000), (100000008); +SELECT * FROM t7; + +CREATE TABLE t8(a INT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +--error ER_WARN_DATA_OUT_OF_RANGE +INSERT INTO t8 VALUES(-5); +INSERT INTO t8 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (100000000), (100000008); +SELECT * FROM t8; + +CREATE TABLE t9(a BIGINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t9 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31), (100000000000), (100000000006); +SELECT * FROM t9; + +CREATE TABLE t10(a BIGINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +--error ER_WARN_DATA_OUT_OF_RANGE +INSERT INTO t10 VALUES(-5); +INSERT INTO t10 VALUES(0), (0), (0), (0), (8), (10), (0), +(20), (30), (31), (100000000000), (100000000006); +SELECT * FROM t10; + +CREATE TABLE t11(a FLOAT AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31); +SELECT * FROM t11; + +# Since autoinc counter is persisted by redo logs, we don't want to +# lose them on kill and restart, so to make the result after restart stable. +set global innodb_flush_log_at_trx_commit=1; + +CREATE TABLE t12(a DOUBLE AUTO_INCREMENT KEY) ENGINE = InnoDB; +INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0), +(20), (30), (31); +SELECT * FROM t12; + +CREATE TABLE t13(a INT AUTO_INCREMENT PRIMARY KEY) ENGINE = InnoDB, +AUTO_INCREMENT = 1234; + +--echo # Scenario 1: Normal restart, to test if the counters are persisted +--source include/restart_mysqld.inc + +--echo # We expect these results should be equal to above SELECTs +SELECT * FROM t1; +SELECT * FROM t2; +SELECT * FROM t3; +SELECT * FROM t4; +SELECT * FROM t5; +SELECT * FROM t6; +SELECT * FROM t7; +SELECT * FROM t8; +SELECT * FROM t9; +SELECT * FROM t10; +SELECT * FROM t11; +SELECT * FROM t12; + +SELECT * FROM t13; +SHOW CREATE TABLE t13; +INSERT INTO t13 VALUES(0); +SELECT a AS `Expect 1234` FROM t13; + +--echo # Scenario 2: Delete some values, to test the counters should not be the +--echo # one which is the largest in current table + +set global innodb_flush_log_at_trx_commit=1; + +DELETE FROM t1 WHERE a > 30; +SELECT MAX(a) AS `Expect 30` FROM t1; +DELETE FROM t3 WHERE a > 2000; +SELECT MAX(a) AS `Expect 2000` FROM t3; +DELETE FROM t5 WHERE a > 1000000; +SELECT MAX(a) AS `Expect 1000000` FROM t5; +DELETE FROM t7 WHERE a > 100000000; +SELECT MAX(a) AS `Expect 100000000` FROM t7; +DELETE FROM t9 WHERE a > 100000000000; +SELECT MAX(a) AS `Expect 100000000000` FROM t9; + +--source include/restart_mysqld.inc + +INSERT INTO t1 VALUES(0), (0); +SELECT MAX(a) AS `Expect 33` FROM t1; +INSERT INTO t3 VALUES(0), (0); +SELECT MAX(a) AS `Expect 4098` FROM t3; +INSERT INTO t5 VALUES(0), (0); +SELECT MAX(a) AS `Expect 1000007` FROM t5; +INSERT INTO t7 VALUES(0), (0); +SELECT MAX(a) AS `Expect 100000010` FROM t7; +INSERT INTO t9 VALUES(0), (0); +SELECT MAX(a) AS `Expect 100000000008` FROM t9; + +--echo # Scenario 3: Insert some bigger counters, the next counter should start +--echo # from there + +INSERT INTO t1 VALUES(40), (0); +INSERT INTO t1 VALUES(42), (0); +SELECT a AS `Expect 43, 42` FROM t1 ORDER BY a DESC LIMIT 4; +INSERT INTO t3 VALUES(5000), (0); +INSERT INTO t3 VALUES(5010), (0); +SELECT a AS `Expect 5011, 5010` FROM t3 ORDER BY a DESC LIMIT 4; +INSERT INTO t5 VALUES(1000010), (0); +INSERT INTO t5 VALUES(1000020), (0); +SELECT a AS `Expect 1000021, 1000020` FROM t5 ORDER BY a DESC LIMIT 4; +INSERT INTO t7 VALUES(100000020), (0); +INSERT INTO t7 VALUES(100000030), (0); +SELECT a AS `Expect 100000031, 100000030` FROM t7 ORDER BY a DESC LIMIT 4; +INSERT INTO t9 VALUES(100000000010), (0); +INSERT INTO t9 VALUES(100000000020), (0); +SELECT a AS `Expect 100000000021, 100000000020` FROM t9 ORDER BY a DESC LIMIT 4; + +--echo # Scenario 4: Update some values, to test the counters should be updated +--echo # to the bigger value, but not smaller value. + +INSERT INTO t1 VALUES(50), (55); +# Updating to bigger value will update the auto-increment counter +UPDATE t1 SET a = 105 WHERE a = 5; +# Updating to smaller value will not update the counter +UPDATE t1 SET a = 100 WHERE a = 55; +--echo # This should insert 102, 106, 107, and make next counter 109. +INSERT INTO t1 VALUES(102), (0), (0); +SELECT a AS `Expect 107, 106` FROM t1 ORDER BY a DESC LIMIT 2; +DELETE FROM t1 WHERE a > 105; +INSERT INTO t1 VALUES(0); +SELECT MAX(a) AS `Expect 109` FROM t1; + +--echo # Test the same things on t3, t5, t7, t9, to test if DDTableBuffer would +--echo # be updated accordingly + +INSERT INTO t3 VALUES(60), (65); +# Updating to bigger value will update the auto-increment counter +UPDATE t3 SET a = 6005 WHERE a = 5; +# Updating to smaller value will not update the counter +UPDATE t3 SET a = 6000 WHERE a = 60; +--echo # This should insert 6002, 6006, 6007, and make next counter 6009. +INSERT INTO t3 VALUES(6002), (0), (0); +SELECT a AS `Expect 6007, 6006` FROM t3 ORDER BY a DESC LIMIT 2; +DELETE FROM t3 WHERE a > 6005; +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 6009` FROM t3; + +INSERT INTO t5 VALUES(100), (200); +# Updating to bigger value will update the auto-increment counter +UPDATE t5 SET a = 1000105 WHERE a = 5; +# Updating to smaller value will not update the counter +UPDATE t5 SET a = 1000100 WHERE a = 100; +--echo # This should insert 1000102, 1000106, 1000107, and make next counter +--echo # 1000109. +INSERT INTO t5 VALUES(1000102), (0), (0); +SELECT a AS `Expect 1000107, 1000106` FROM t5 ORDER BY a DESC LIMIT 2; +DELETE FROM t5 WHERE a > 1000105; +INSERT INTO t5 VALUES(0); +SELECT MAX(a) AS `Expect 1000109` FROM t5; + +INSERT INTO t7 VALUES(100), (200); +# Updating to bigger value will update the auto-increment counter +UPDATE t7 SET a = 100000105 WHERE a = 5; +# Updating to smaller value will not update the counter +UPDATE t7 SET a = 100000100 WHERE a = 100; +--echo # This should insert 100000102, 1100000106, 100000107, and make next +--echo # counter 100000109. +INSERT INTO t7 VALUES(100000102), (0), (0); +SELECT a AS `Expect 100000107, 100000106` FROM t7 ORDER BY a DESC LIMIT 2; +DELETE FROM t7 WHERE a > 100000105; +INSERT INTO t7 VALUES(0); +SELECT MAX(a) AS `Expect 100000109` FROM t7; + +set global innodb_flush_log_at_trx_commit=1; + +INSERT INTO t9 VALUES(100), (200); +# Updating to bigger value will update the auto-increment counter +UPDATE t9 SET a = 100000000105 WHERE a = 5; +# Updating to smaller value will not update the counter +UPDATE t9 SET a = 100000000100 WHERE a = 100; +--echo # This should insert 100000000102, 100000000106, 100000000107, and make +--echo # next counter 100000000109. +INSERT INTO t9 VALUES(100000000102), (0), (0); +SELECT a AS `Expect 100000000107, 100000000106` FROM t9 ORDER BY a DESC LIMIT 2; +DELETE FROM t9 WHERE a > 100000000105; +INSERT INTO t9 VALUES(0); +SELECT MAX(a) AS `Expect 100000000109` FROM t9; + +--source include/restart_mysqld.inc + +INSERT INTO t1 VALUES(0), (0); +SELECT a AS `Expect 110, 111` FROM t1 ORDER BY a DESC LIMIT 2; + +INSERT INTO t3 VALUES(0), (0); +SELECT a AS `Expect 6010, 6011` FROM t3 ORDER BY a DESC LIMIT 2; + +INSERT INTO t5 VALUES(0), (0); +SELECT a AS `Expect 1100111, 1100110` FROM t5 ORDER BY a DESC LIMIT 2; + +INSERT INTO t7 VALUES(0), (0); +SELECT a AS `Expect 100000111, 100000110` FROM t7 ORDER BY a DESC LIMIT 2; + +INSERT INTO t9 VALUES(0), (0); +SELECT a AS `Expect 100000000111, 100000000110` FROM t9 ORDER BY a DESC LIMIT 2; + +--echo # Scenario 5: Test kill the server + +INSERT INTO t1 VALUES(125); +DELETE FROM t1 WHERE a = 125; + +INSERT INTO t3 VALUES(6100); +DELETE FROM t3 WHERE a = 6100; + +INSERT INTO t5 VALUES(1100200); +DELETE FROM t5 WHERE a = 1100200; + +INSERT INTO t7 VALUES(100000200); +DELETE FROM t7 WHERE a = 100000200; + +set global innodb_flush_log_at_trx_commit=1; + +INSERT INTO t9 VALUES(100000000200); +DELETE FROM t9 WHERE a = 100000000200; + +--source include/kill_and_restart_mysqld.inc + +INSERT INTO t1 VALUES(0); +SELECT a AS `Expect 126` FROM t1 ORDER BY a DESC LIMIT 1; + +INSERT INTO t3 VALUES(0); +SELECT a AS `Expect 6101` FROM t3 ORDER BY a DESC LIMIT 1; + +INSERT INTO t5 VALUES(0); +SELECT a AS `Expect 1100201` FROM t5 ORDER BY a DESC LIMIT 1; + +INSERT INTO t7 VALUES(0); +SELECT a AS `Expect 100000201` FROM t7 ORDER BY a DESC LIMIT 1; + +INSERT INTO t9 VALUES(0); +SELECT a AS `Expect 100000000201` FROM t9 ORDER BY a DESC LIMIT 1; + +--echo # Scenario 6: Test truncate will reset the counters to 0 + +TRUNCATE TABLE t1; +TRUNCATE TABLE t3; +TRUNCATE TABLE t5; +TRUNCATE TABLE t7; +TRUNCATE TABLE t9; + +INSERT INTO t1 VALUES(0), (0); +SELECT * FROM t1; + +INSERT INTO t3 VALUES(0), (0); +SELECT * FROM t3; + +INSERT INTO t5 VALUES(0), (0); +SELECT * FROM t5; + +INSERT INTO t7 VALUES(0), (0); +SELECT * FROM t7; + +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; + +set global innodb_flush_log_at_trx_commit=1; + +TRUNCATE TABLE t1; +TRUNCATE TABLE t3; +TRUNCATE TABLE t5; +TRUNCATE TABLE t7; +TRUNCATE TABLE t9; + +--source include/kill_and_restart_mysqld.inc + +INSERT INTO t1 VALUES(0), (0); +SELECT * FROM t1; + +INSERT INTO t3 VALUES(0), (0); +SELECT * FROM t3; + +INSERT INTO t5 VALUES(0), (0); +SELECT * FROM t5; + +INSERT INTO t7 VALUES(0), (0); +SELECT * FROM t7; + +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; + +--echo # Scenario 7: Test explicit rename table won't change the counter + +set global innodb_flush_log_at_trx_commit=1; + +RENAME TABLE t9 to t19; +INSERT INTO t19 VALUES(0), (0); +SELECT * FROM t19; +DELETE FROM t19 WHERE a = 4; + +--source include/kill_and_restart_mysqld.inc + +RENAME TABLE t19 to t9; +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; + +TRUNCATE TABLE t9; + +INSERT INTO t9 VALUES(0), (0); +SELECT * FROM t9; + +--echo # Scenario 8: Test ALTER TABLE operations + +INSERT INTO t3 VALUES(0), (0), (100), (200), (1000); +SELECT * FROM t3; +DELETE FROM t3 WHERE a > 300; +SELECT MAX(a) AS `Expect 200` FROM t3; +--echo # This will not change the counter to 150, but to 201, which is the next +--echo # of current max counter in the table +ALTER TABLE t3 AUTO_INCREMENT = 150; +SHOW CREATE TABLE t3; +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 201` FROM t3; +--echo # This will change the counter to 500, which is bigger than any counter +--echo # in the table +ALTER TABLE t3 AUTO_INCREMENT = 500; +SHOW CREATE TABLE t3; +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 500` FROM t3; + +TRUNCATE TABLE t3; +ALTER TABLE t3 AUTO_INCREMENT = 100; +SHOW CREATE TABLE t3; +INSERT INTO t3 VALUES(0), (0); +SELECT * FROM t3; + +INSERT INTO t3 VALUES(150), (180); +UPDATE t3 SET a = 200 WHERE a = 150; +INSERT INTO t3 VALUES(220); +--echo # This still fails to set to 120, but just 221 +ALTER TABLE t3 AUTO_INCREMENT = 120; +SHOW CREATE TABLE t3; +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 221` FROM t3; + +DELETE FROM t3 WHERE a > 120; + +ALTER TABLE t3 AUTO_INCREMENT = 120; +SHOW CREATE TABLE t3; + +--echo # MDEV-6076: Test adding an AUTO_INCREMENT COLUMN +CREATE TABLE mdev6076a (b INT) ENGINE=InnoDB; +INSERT INTO mdev6076a VALUES(2),(1); +CREATE TABLE mdev6076b (b INT) ENGINE=InnoDB; +INSERT INTO mdev6076b VALUES(2),(1); +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, LOCK=NONE; +ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, ALGORITHM=INPLACE; +ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100, +ALGORITHM=INPLACE; + +--source include/kill_and_restart_mysqld.inc + +INSERT INTO t3 VALUES(0); +SELECT MAX(a) AS `Expect 120` FROM t3; + +INSERT INTO mdev6076a SET b=0; +SELECT * FROM mdev6076a; +INSERT INTO mdev6076b SET b=0; +SELECT * FROM mdev6076b; +DROP TABLE mdev6076a, mdev6076b; + +set global innodb_flush_log_at_trx_commit=1; + +INSERT INTO t3 VALUES(0), (0), (200), (210); + +--echo # Test the different algorithms in ALTER TABLE + +--let $template = t3 +--let $algorithm = INPLACE +--let $table = t_inplace +--source suite/innodb/include/autoinc_persist_alter.inc +--let $algorithm = COPY +--let $table = t_copy +--source suite/innodb/include/autoinc_persist_alter.inc + +--echo # Scenario 9: Test the sql_mode = NO_AUTO_VALUE_ON_ZERO + +CREATE TABLE t30 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT, key(b)) ENGINE = InnoDB; + +set SQL_MODE = NO_AUTO_VALUE_ON_ZERO; + +INSERT INTO t30 VALUES(NULL, 1), (200, 2), (0, 3); +INSERT INTO t30(b) VALUES(4), (5), (6), (7); +SELECT * FROM t30 ORDER BY b; +ALTER TABLE t30 MODIFY b MEDIUMINT; +SELECT * FROM t30 ORDER BY b; + +set global innodb_flush_log_at_trx_commit=1; + +CREATE TABLE t31 (a INT) ENGINE = InnoDB; +INSERT INTO t31 VALUES(1), (2); +ALTER TABLE t31 ADD b INT AUTO_INCREMENT PRIMARY KEY; +INSERT INTO t31 VALUES(3, 0), (4, NULL), (5, NULL); +--error ER_DUP_ENTRY +INSERT INTO t31 VALUES(6, 0); +SELECT * FROM t31; + +--source include/kill_and_restart_mysqld.inc + +--echo # This will not insert 0 +INSERT INTO t31(a) VALUES(6), (0); +SELECT * FROM t31; +DROP TABLE t31; + +set SQL_MODE = NO_AUTO_VALUE_ON_ZERO; + +DELETE FROM t30 WHERE a = 0; +UPDATE t30 set a = 0 where b = 5; +SELECT * FROM t30 ORDER BY b; +DELETE FROM t30 WHERE a = 0; + +UPDATE t30 SET a = NULL WHERE b = 6; +UPDATE t30 SET a = 300 WHERE b = 7; + +SELECT * FROM t30 ORDER BY b; + +SET SQL_MODE = 0; + +--echo # Scenario 10: Rollback would not rollback the counter +CREATE TABLE t32 ( +a BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; + +INSERT INTO t32 VALUES(0), (0); + +set global innodb_flush_log_at_trx_commit=1; + +START TRANSACTION; +INSERT INTO t32 VALUES(0), (0); +SELECT MAX(a) AS `Expect 4` FROM t32; +DELETE FROM t32 WHERE a >= 2; +ROLLBACK; + +--source include/kill_and_restart_mysqld.inc + +SELECT MAX(a) AS `Expect 2` FROM t32; +INSERT INTO t32 VALUES(0), (0); +SELECT MAX(a) AS `Expect 6` FROM t32; + +--echo # Scenario 11: Test duplicate primary key/secondary key will not stop +--echo # increasing the counter + +CREATE TABLE t33 ( +a BIGINT NOT NULL PRIMARY KEY, +b BIGINT NOT NULL AUTO_INCREMENT, +KEY(b)) ENGINE = InnoDB; + +INSERT INTO t33 VALUES(1, NULL); +INSERT INTO t33 VALUES(2, NULL); +--error ER_DUP_ENTRY +INSERT INTO t33 VALUES(2, NULL); + +INSERT INTO t33 VALUES(3, NULL); +SELECT MAX(b) AS `Expect 4` FROM t33; + +TRUNCATE TABLE t33; + +INSERT INTO t33 VALUES(1, NULL); +INSERT INTO t33 VALUES(2, NULL); + +set global innodb_flush_log_at_trx_commit=1; + +START TRANSACTION; +UPDATE t33 SET a = 10 WHERE a = 1; +--error ER_DUP_ENTRY +INSERT INTO t33 VALUES(2, NULL); +COMMIT; + +--source include/kill_and_restart_mysqld.inc + +INSERT INTO t33 VALUES(3, NULL); +SELECT MAX(b) AS `Expect 4` FROM t33; + +DROP TABLE t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t30, t32, t33; diff --git a/mysql-test/suite/innodb/t/innodb-autoinc-44030-master.opt b/mysql-test/suite/innodb/t/innodb-autoinc-44030-master.opt deleted file mode 100644 index 303ec1be1d0..00000000000 --- a/mysql-test/suite/innodb/t/innodb-autoinc-44030-master.opt +++ /dev/null @@ -1,3 +0,0 @@ ---default-storage-engine=MyISAM ---innodb-strict-mode=0 ---innodb-file-per-table=0 diff --git a/mysql-test/suite/innodb/t/innodb-autoinc-44030.test b/mysql-test/suite/innodb/t/innodb-autoinc-44030.test index 256e7d838ea..61c56127351 100644 --- a/mysql-test/suite/innodb/t/innodb-autoinc-44030.test +++ b/mysql-test/suite/innodb/t/innodb-autoinc-44030.test @@ -2,27 +2,24 @@ # embedded server does not support restarting -- source include/not_embedded.inc -# -# 44030: Error: (1500) Couldn't read the MAX(ID) autoinc value from +# Before MDEV-6076 Persistent AUTO_INCREMENT for InnoDB +# this was a test for +# Bug #44030: Error: (1500) Couldn't read the MAX(ID) autoinc value from # the index (PRIMARY) # This test requires a restart of the server SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; INSERT INTO t1 VALUES (null); INSERT INTO t1 VALUES (null); +--enable_info ALTER TABLE t1 CHANGE c1 d1 INT NOT NULL AUTO_INCREMENT; +--disable_info SELECT * FROM t1; -# Restart the server -- source include/restart_mysqld.inc -# The MySQL and InnoDB data dictionaries should now be out of sync. -# The select should print message to the error log -SELECT * FROM t1; -# MySQL have made a change (http://lists.mysql.com/commits/75268) that no -# longer results in the two data dictionaries being out of sync. If they -# revert their changes then this check for ER_AUTOINC_READ_FAILED will need -# to be enabled. Also, see http://bugs.mysql.com/bug.php?id=47621. -#-- error ER_AUTOINC_READ_FAILED,1467 +SHOW CREATE TABLE t1; INSERT INTO t1 VALUES(null); +SELECT * FROM t1; + # Before WL#5534, the following statement would copy the table, # and effectively set AUTO_INCREMENT to 4, because while copying # it would write values 1,2,3 to the column. diff --git a/mysql-test/suite/innodb/t/innodb.test b/mysql-test/suite/innodb/t/innodb.test index dfe1a0b4995..e5e4b45a861 100644 --- a/mysql-test/suite/innodb/t/innodb.test +++ b/mysql-test/suite/innodb/t/innodb.test @@ -1463,15 +1463,18 @@ select * from t1; drop table t1; # -# Test that update does not change internal auto-increment value +# Test that update does change internal auto-increment value # create table t1 (a int not null auto_increment primary key, val int) engine=InnoDB; insert into t1 (val) values (1); update t1 set a=2 where a=1; -# We should get the following error because InnoDB does not update the counter +# This should insert 3, since the counter has been updated to 2 already +insert into t1 (val) values (3); +select * from t1; +# We should get the following error because InnoDB does update the counter --error ER_DUP_ENTRY -insert into t1 (val) values (1); +insert into t1 values (2, 2); select * from t1; drop table t1; # diff --git a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result index 635942a1f34..a6af2b924c0 100644 --- a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result +++ b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result @@ -43,7 +43,7 @@ UPDATE t1 SET c1 = 40 WHERE c1 = 50; SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'; AUTO_INCREMENT -32 +52 UPDATE t1 SET c1 = NULL WHERE c1 = 4; Warnings: Warning 1048 Column 'c1' cannot be null @@ -62,10 +62,10 @@ c1 25 30 31 -32 -33 40 51 +52 +53 DROP TABLE t1; CREATE TABLE t1 ( c1 INT NOT NULL AUTO_INCREMENT, @@ -255,7 +255,7 @@ UPDATE t1 SET c1 = 140 WHERE c1 = 150; SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'; AUTO_INCREMENT -141 +152 UPDATE t1 SET c1 = NULL WHERE c1 = 4; Warnings: Warning 1048 Column 'c1' cannot be null @@ -279,9 +279,9 @@ c1 90 91 140 -141 -142 151 +152 +153 DROP TABLE t1; # Test with auto_increment_increment and auto_increment_offset. CREATE TABLE t1 ( diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index e5284ee802a..9b99da2a684 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -50,6 +50,7 @@ Created 6/2/1994 Heikki Tuuri #include "gis0geo.h" #include "ut0new.h" #include "dict0boot.h" +#include "row0sel.h" /* row_search_max_autoinc() */ /**************************************************************//** Checks if the page in the cursor can be merged with given page. @@ -1405,6 +1406,117 @@ btr_free( btr_free_root(block, &mtr); mtr.commit(); } + +/** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC. +@param[in,out] index clustered index +@return the last used AUTO_INCREMENT value +@retval 0 on error or if no AUTO_INCREMENT value was used yet */ +ib_uint64_t +btr_read_autoinc(dict_index_t* index) +{ + ut_ad(dict_index_is_clust(index)); + ut_ad(index->table->persistent_autoinc); + ut_ad(!dict_table_is_temporary(index->table)); + + if (fil_space_t* space = fil_space_acquire(index->space)) { + mtr_t mtr; + mtr.start(); + ib_uint64_t autoinc; + if (buf_block_t* block = buf_page_get( + page_id_t(index->space, index->page), + page_size_t(space->flags), + RW_S_LATCH, &mtr)) { + autoinc = page_get_autoinc(block->frame); + } else { + autoinc = 0; + } + mtr.commit(); + fil_space_release(space); + return(autoinc); + } + + return(0); +} + +/** 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) +{ + ut_ad(table->persistent_autoinc); + ut_ad(!dict_table_is_temporary(table)); + + dict_index_t* index = dict_table_get_first_index(table); + + if (index == NULL) { + } else if (fil_space_t* space = fil_space_acquire(index->space)) { + mtr_t mtr; + mtr.start(); + buf_block_t* block = buf_page_get( + page_id_t(index->space, index->page), + page_size_t(space->flags), + RW_S_LATCH, &mtr); + + ib_uint64_t autoinc = block + ? page_get_autoinc(block->frame) : 0; + const bool retry = block && autoinc == 0 + && !page_is_empty(block->frame); + mtr.commit(); + fil_space_release(space); + + 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 != NULL + && index->fields[0].col != autoinc_col) { + index = dict_table_get_next_index(index); + } + + if (index != NULL && index->space == space->id) { + autoinc = row_search_max_autoinc(index); + } + } + + return(autoinc); + } + + return(0); +} + +/** Write the next available AUTO_INCREMENT value to PAGE_ROOT_AUTO_INC. +@param[in,out] index clustered index +@param[in] autoinc the AUTO_INCREMENT value +@param[in] reset whether to reset the AUTO_INCREMENT + to a possibly smaller value than currently + exists in the page */ +void +btr_write_autoinc(dict_index_t* index, ib_uint64_t autoinc, bool reset) +{ + ut_ad(dict_index_is_clust(index)); + ut_ad(index->table->persistent_autoinc); + ut_ad(!dict_table_is_temporary(index->table)); + + if (fil_space_t* space = fil_space_acquire(index->space)) { + mtr_t mtr; + mtr.start(); + mtr.set_named_space(space); + page_set_autoinc(buf_page_get( + page_id_t(index->space, index->page), + page_size_t(space->flags), + RW_SX_LATCH, &mtr), + index, autoinc, &mtr, reset); + mtr.commit(); + fil_space_release(space); + } +} #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** @@ -1499,21 +1611,28 @@ btr_page_reorganize_low( page_get_infimum_rec(temp_page), index, mtr); - /* Multiple transactions cannot simultaneously operate on the - same temp-table in parallel. - max_trx_id is ignored for temp tables because it not required - for MVCC. */ - if (dict_index_is_sec_or_ibuf(index) - && page_is_leaf(page) - && !dict_table_is_temporary(index->table)) { - /* Copy max trx id to recreated page */ - trx_id_t max_trx_id = page_get_max_trx_id(temp_page); - page_set_max_trx_id(block, NULL, max_trx_id, mtr); - /* In crash recovery, dict_index_is_sec_or_ibuf() always - holds, even for clustered indexes. max_trx_id is - unused in clustered index pages. */ - ut_ad(max_trx_id != 0 || recovery); - } + /* Copy the PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC. */ + memcpy(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), + temp_page + (PAGE_HEADER + PAGE_MAX_TRX_ID), 8); + /* PAGE_MAX_TRX_ID is unused in clustered index pages, + non-leaf pages, and in temporary tables. It was always + zero-initialized in page_create() in all InnoDB versions. + PAGE_MAX_TRX_ID must be nonzero on dict_index_is_sec_or_ibuf() + leaf pages. + + During redo log apply, dict_index_is_sec_or_ibuf() always + holds, even for clustered indexes. */ + ut_ad(recovery || dict_table_is_temporary(index->table) + || !page_is_leaf(temp_page) + || !dict_index_is_sec_or_ibuf(index) + || page_get_max_trx_id(page) != 0); + /* PAGE_MAX_TRX_ID must be zero on non-leaf pages other than + clustered index root pages. */ + ut_ad(recovery + || page_get_max_trx_id(page) == 0 + || (dict_index_is_sec_or_ibuf(index) + ? page_is_leaf(temp_page) + : page_is_root(temp_page))); /* If innodb_log_compressed_pages is ON, page reorganize should log the compressed page image.*/ diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 0d72b9d2b92..d8a2e15d1eb 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -751,7 +751,9 @@ btr_cur_search_to_nth_level( RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr) /*!< in: mtr */ + mtr_t* mtr, /*!< in: mtr */ + ib_uint64_t autoinc)/*!< in: PAGE_ROOT_AUTO_INC to be written + (0 if none) */ { page_t* page = NULL; /* remove warning */ buf_block_t* block; @@ -888,6 +890,12 @@ btr_cur_search_to_nth_level( || latch_mode == BTR_SEARCH_TREE || latch_mode == BTR_MODIFY_LEAF); + ut_ad(autoinc == 0 || dict_index_is_clust(index)); + ut_ad(autoinc == 0 + || latch_mode == BTR_MODIFY_TREE + || latch_mode == BTR_MODIFY_LEAF); + ut_ad(autoinc == 0 || level == 0); + cursor->flag = BTR_CUR_BINARY; cursor->index = index; @@ -907,8 +915,7 @@ btr_cur_search_to_nth_level( # ifdef UNIV_SEARCH_PERF_STAT info->n_searches++; # endif - if (rw_lock_get_writer(btr_get_search_latch(index)) - == RW_LOCK_NOT_LOCKED + if (autoinc == 0 && latch_mode <= BTR_MODIFY_LEAF && info->last_hash_succ # ifdef MYSQL_INDEX_DISABLE_AHI @@ -922,8 +929,10 @@ btr_cur_search_to_nth_level( /* If !has_search_latch, we do a dirty read of btr_search_enabled below, and btr_search_guess_on_hash() will have to check it again. */ - && UNIV_LIKELY(btr_search_enabled) + && btr_search_enabled && !modify_external + && rw_lock_get_writer(btr_get_search_latch(index)) + == RW_LOCK_NOT_LOCKED && btr_search_guess_on_hash(index, info, tuple, mode, latch_mode, cursor, has_search_latch, mtr)) { @@ -1071,16 +1080,16 @@ search_loop: if (height != 0) { /* We are about to fetch the root or a non-leaf page. */ - if ((latch_mode != BTR_MODIFY_TREE - || height == level) + if ((latch_mode != BTR_MODIFY_TREE || height == level) && !retrying_for_search_prev) { /* If doesn't have SX or X latch of index, each pages should be latched before reading. */ - if (modify_external - && height == ULINT_UNDEFINED - && upper_rw_latch == RW_S_LATCH) { + if (height == ULINT_UNDEFINED + && upper_rw_latch == RW_S_LATCH + && (modify_external || autoinc)) { /* needs sx-latch of root page - for fseg operation */ + for fseg operation or for writing + PAGE_ROOT_AUTO_INC */ rw_latch = RW_SX_LATCH; } else { rw_latch = upper_rw_latch; @@ -1271,11 +1280,13 @@ retry_page_get: && page_is_leaf(page) && rw_latch != RW_NO_LATCH && rw_latch != root_leaf_rw_latch) { - /* We should retry to get the page, because the root page - is latched with different level as a leaf page. */ + /* The root page is also a leaf page (root_leaf). + We should reacquire the page, because the root page + is latched differently from leaf pages. */ ut_ad(root_leaf_rw_latch != RW_NO_LATCH); ut_ad(rw_latch == RW_S_LATCH || rw_latch == RW_SX_LATCH); - ut_ad(rw_latch == RW_S_LATCH || modify_external); + ut_ad(rw_latch == RW_S_LATCH || modify_external || autoinc); + ut_ad(!autoinc || root_leaf_rw_latch == RW_X_LATCH); ut_ad(n_blocks == 0); mtr_release_block_at_savepoint( @@ -1365,6 +1376,7 @@ retry_page_get: /* release upper blocks */ if (retrying_for_search_prev) { + ut_ad(!autoinc); for (; prev_n_releases < prev_n_blocks; prev_n_releases++) { @@ -1378,8 +1390,9 @@ retry_page_get: } for (; n_releases < n_blocks; n_releases++) { - if (n_releases == 0 && modify_external) { - /* keep latch of root page */ + if (n_releases == 0 + && (modify_external || autoinc)) { + /* keep the root page latch */ ut_ad(mtr_memo_contains_flagged( mtr, tree_blocks[n_releases], MTR_MEMO_PAGE_SX_FIX @@ -1903,6 +1916,8 @@ need_opposite_intention: } if (level != 0) { + ut_ad(!autoinc); + if (upper_rw_latch == RW_NO_LATCH) { /* latch the page */ buf_block_t* child_block; @@ -1951,6 +1966,11 @@ need_opposite_intention: cursor->up_match = up_match; cursor->up_bytes = up_bytes; + if (autoinc) { + page_set_autoinc(tree_blocks[0], + index, autoinc, mtr, false); + } + #ifdef BTR_CUR_ADAPT /* We do a dirty read of btr_search_enabled here. We will properly check btr_search_enabled again in diff --git a/storage/innobase/btr/btr0pcur.cc b/storage/innobase/btr/btr0pcur.cc index 73399f14081..377a332bd9f 100644 --- a/storage/innobase/btr/btr0pcur.cc +++ b/storage/innobase/btr/btr0pcur.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, 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 @@ -596,7 +597,7 @@ btr_pcur_open_on_user_rec_func( mtr_t* mtr) /*!< in: mtr */ { btr_pcur_open_low(index, 0, tuple, mode, latch_mode, cursor, - file, line, mtr); + file, line, 0, mtr); if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) { diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 4f77d2168f4..cabe649f0d4 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -810,20 +810,6 @@ dict_index_zip_pad_lock( mutex_enter(index->zip_pad.mutex); } - -/********************************************************************//** -Unconditionally set the autoinc counter. */ -void -dict_table_autoinc_initialize( -/*==========================*/ - dict_table_t* table, /*!< in/out: table */ - ib_uint64_t value) /*!< in: next value to assign to a row */ -{ - ut_ad(dict_table_autoinc_own(table)); - - table->autoinc = value; -} - /** Get all the FTS indexes on a table. @param[in] table table @param[out] indexes all FTS indexes on this table @@ -849,75 +835,6 @@ dict_table_get_all_fts_indexes( return(ib_vector_size(indexes)); } -/** Store autoinc value when the table is evicted. -@param[in] table table evicted */ -void -dict_table_autoinc_store( - const dict_table_t* table) -{ - ut_ad(mutex_own(&dict_sys->mutex)); - - if (table->autoinc != 0) { - ut_ad(dict_sys->autoinc_map->find(table->id) - == dict_sys->autoinc_map->end()); - - dict_sys->autoinc_map->insert( - std::pair( - table->id, table->autoinc)); - } -} - -/** Restore autoinc value when the table is loaded. -@param[in] table table loaded */ -void -dict_table_autoinc_restore( - dict_table_t* table) -{ - ut_ad(mutex_own(&dict_sys->mutex)); - - autoinc_map_t::iterator it; - it = dict_sys->autoinc_map->find(table->id); - - if (it != dict_sys->autoinc_map->end()) { - table->autoinc = it->second; - ut_ad(table->autoinc != 0); - - dict_sys->autoinc_map->erase(it); - } -} - -/********************************************************************//** -Reads the next autoinc value (== autoinc counter value), 0 if not yet -initialized. -@return value for a new row, or 0 */ -ib_uint64_t -dict_table_autoinc_read( -/*====================*/ - const dict_table_t* table) /*!< in: table */ -{ - ut_ad(dict_table_autoinc_own(table)); - - return(table->autoinc); -} - -/********************************************************************//** -Updates the autoinc counter if the value supplied is greater than the -current value. */ -void -dict_table_autoinc_update_if_greater( -/*=================================*/ - - dict_table_t* table, /*!< in/out: table */ - ib_uint64_t value) /*!< in: value which was assigned to a row */ -{ - ut_ad(dict_table_autoinc_own(table)); - - if (value > table->autoinc) { - - table->autoinc = value; - } -} - /********************************************************************//** Release the autoinc lock. */ void @@ -1212,8 +1129,6 @@ dict_init(void) } mutex_create(LATCH_ID_DICT_FOREIGN_ERR, &dict_foreign_err_mutex); - - dict_sys->autoinc_map = new autoinc_map_t(); } /**********************************************************************//** @@ -1477,8 +1392,6 @@ dict_table_add_to_cache( UT_LIST_ADD_FIRST(dict_sys->table_non_LRU, table); } - dict_table_autoinc_restore(table); - ut_ad(dict_lru_validate()); dict_sys->size += mem_heap_get_size(table->heap) @@ -2213,10 +2126,6 @@ dict_table_remove_from_cache_low( ut_ad(dict_lru_validate()); - if (lru_evict) { - dict_table_autoinc_store(table); - } - if (lru_evict && table->drop_aborted) { /* Do as dict_table_try_drop_aborted() does. */ @@ -7007,8 +6916,6 @@ dict_close(void) mutex_free(&dict_foreign_err_mutex); - delete dict_sys->autoinc_map; - ut_ad(dict_sys->size == 0); ut_free(dict_sys); diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index e73c6196386..b150c1eb7a0 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -159,12 +159,6 @@ dict_mem_table_create( /* lazy creation of table autoinc latch */ dict_table_autoinc_create_lazy(table); - table->autoinc = 0; - - /* The number of transactions that are either waiting on the - AUTOINC lock or have been granted the lock. */ - table->n_waiting_or_granted_auto_inc_locks = 0; - /* If the table has an FTS index or we are in the process of building one, create the table->fts */ if (dict_table_has_fts_index(table) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 83dd0c17eaa..53d9672808b 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6739,33 +6739,35 @@ innobase_get_int_col_max_value( return(max_value); } -/************************************************************************ -Set the autoinc column max value. This should only be called once from -ha_innobase::open(). Therefore there's no need for a covering lock. */ +/** Initialize the AUTO_INCREMENT column metadata. + +Since a partial table definition for a persistent table can already be +present in the InnoDB dict_sys cache before it is accessed from SQL, +we have to initialize the AUTO_INCREMENT counter on the first +ha_innobase::open(). + +@param[in,out] table persistent table +@param[in] field the AUTO_INCREMENT column */ +static void -ha_innobase::innobase_initialize_autoinc() -/*======================================*/ +initialize_auto_increment(dict_table_t* table, const Field* field) { - ulonglong auto_inc; - const Field* field = table->found_next_number_field; + ut_ad(!dict_table_is_temporary(table)); - if (field != NULL) { - auto_inc = innobase_get_int_col_max_value(field); - ut_ad(!innobase_is_v_fld(field)); + const unsigned col_no = innodb_col_no(field); - /* autoinc column cannot be virtual column */ - ut_ad(!innobase_is_v_fld(field)); - } else { - /* We have no idea what's been passed in to us as the - autoinc column. We set it to the 0, effectively disabling - updates to the table. */ - auto_inc = 0; + dict_table_autoinc_lock(table); - ib::info() << "Unable to determine the AUTOINC column name"; - } + table->persistent_autoinc = 1 + + dict_table_get_nth_col_pos(table, col_no, NULL); - if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { + if (table->autoinc) { + /* Already initialized. Our caller checked + table->persistent_autoinc without + dict_table_autoinc_lock(), 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. @@ -6776,70 +6778,16 @@ ha_innobase::innobase_initialize_autoinc() 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. */ - auto_inc = 0; - } else if (field == NULL) { - /* This is a far more serious error, best to avoid - opening the table and return failure. */ - my_error(ER_AUTOINC_READ_FAILED, MYF(0)); - } else { - dict_index_t* index; - const char* col_name; - ib_uint64_t read_auto_inc; - ulint err; - - update_thd(ha_thd()); - - col_name = field->field_name; - - index = innobase_get_index(table->s->next_number_index); - - /* Execute SELECT MAX(col_name) FROM TABLE; */ - err = row_search_max_autoinc(index, col_name, &read_auto_inc); - - switch (err) { - case DB_SUCCESS: { - ulonglong col_max_value; - - col_max_value = innobase_get_int_col_max_value(field); - - /* At the this stage we do not know the increment - nor the offset, so use a default increment of 1. */ - - auto_inc = innobase_next_autoinc( - read_auto_inc, 1, 1, 0, col_max_value); - - break; - } - case DB_RECORD_NOT_FOUND: - ib::error() << "MariaDB and InnoDB data dictionaries are" - " out of sync. Unable to find the AUTOINC" - " column " << col_name << " in the InnoDB" - " table " << index->table->name << ". We set" - " the next AUTOINC column value to 0, in" - " effect disabling the AUTOINC next value" - " generation."; - - ib::info() << "You can either set the next AUTOINC" - " value explicitly using ALTER TABLE or fix" - " the data dictionary by recreating the" - " table."; - - /* This will disable the AUTOINC generation. */ - auto_inc = 0; - - /* We want the open to succeed, so that the user can - take corrective action. ie. reads should succeed but - updates should fail. */ - err = DB_SUCCESS; - break; - default: - /* row_search_max_autoinc() should only return - one of DB_SUCCESS or DB_RECORD_NOT_FOUND. */ - ut_error; - } + } 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)); } - dict_table_autoinc_initialize(m_prebuilt->table, auto_inc); + dict_table_autoinc_unlock(table); } /*****************************************************************//** @@ -7199,22 +7147,12 @@ ha_innobase::open( dict_table_get_format(m_prebuilt->table)); } - /* Only if the table has an AUTOINC column. */ - if (m_prebuilt->table != NULL - && !m_prebuilt->table->ibd_file_missing - && table->found_next_number_field != NULL) { - dict_table_autoinc_lock(m_prebuilt->table); - - /* Since a table can already be "open" in InnoDB's internal - data dictionary, we only init the autoinc counter once, the - first time the table is loaded. We can safely reuse the - autoinc value from a previous MySQL open. */ - if (dict_table_autoinc_read(m_prebuilt->table) == 0) { - - innobase_initialize_autoinc(); - } - - dict_table_autoinc_unlock(m_prebuilt->table); + if (m_prebuilt->table == NULL + || dict_table_is_temporary(m_prebuilt->table) + || m_prebuilt->table->persistent_autoinc + || m_prebuilt->table->ibd_file_missing) { + } else if (const Field* ai = table->found_next_number_field) { + initialize_auto_increment(m_prebuilt->table, ai); } /* Set plugin parser for fulltext index */ @@ -7270,6 +7208,24 @@ ha_innobase::open( DBUG_RETURN(0); } +/** Convert MySQL column number to dict_table_t::cols[] offset. +@param[in] field non-virtual column +@return column number relative to dict_table_t::cols[] */ +unsigned +innodb_col_no(const Field* field) +{ + ut_ad(!innobase_is_s_fld(field)); + const TABLE* table = field->table; + unsigned col_no = 0; + ut_ad(field == table->field[field->field_index]); + for (unsigned i = 0; i < field->field_index; i++) { + if (table->field[i]->stored_in_db()) { + col_no++; + } + } + return(col_no); +} + /** Opens dictionary table object using table name. For partition, we need to try alternative lower/upper case names to support moving data files across platforms. @@ -9250,28 +9206,32 @@ innodb_fill_old_vcol_val( return(buf); } -/**********************************************************************//** -Checks which fields have changed in a row and stores information -of them to an update vector. +/** Calculate an update vector corresponding to the changes +between old_row and new_row. +@param[out] uvect update vector +@param[in] old_row current row in MySQL format +@param[in] new_row intended updated row in MySQL format +@param[in] table MySQL table handle +@param[in,out] upd_buff buffer to use for converted values +@param[in] buff_len length of upd_buff +@param[in,out] prebuilt InnoDB execution context +@param[out] auto_inc updated AUTO_INCREMENT value, or 0 if none @return DB_SUCCESS or error code */ static dberr_t calc_row_difference( -/*================*/ - upd_t* uvect, /*!< in/out: update vector */ - const uchar* old_row, /*!< in: old row in MySQL format */ - uchar* new_row, /*!< in: new row in MySQL format */ - TABLE* table, /*!< in: table in MySQL data - dictionary */ - uchar* upd_buff, /*!< in: buffer to use */ - ulint buff_len, /*!< in: buffer length */ - row_prebuilt_t* prebuilt, /*!< in: InnoDB prebuilt struct */ - THD* thd) /*!< in: user thread */ + upd_t* uvect, + const uchar* old_row, + uchar* new_row, + TABLE* table, + uchar* upd_buff, + ulint buff_len, + row_prebuilt_t* prebuilt, + ib_uint64_t& auto_inc) { uchar* original_upd_buff = upd_buff; Field* field; enum_field_types field_mysql_type; - uint n_fields; ulint o_len; ulint n_len; ulint col_pack_len; @@ -9288,19 +9248,19 @@ calc_row_difference( uint i; ibool changes_fts_column = FALSE; ibool changes_fts_doc_col = FALSE; - trx_t* trx = thd_to_trx(thd); + trx_t* const trx = prebuilt->trx; doc_id_t doc_id = FTS_NULL_DOC_ID; ulint num_v = 0; ut_ad(!srv_read_only_mode); - n_fields = table->s->fields; clust_index = dict_table_get_first_index(prebuilt->table); + auto_inc = 0; /* We use upd_buff to convert changed fields */ buf = (byte*) upd_buff; - for (i = 0; i < n_fields; i++) { + for (i = 0; i < table->s->fields; i++) { field = table->field[i]; bool is_virtual = innobase_is_v_fld(field); dict_col_t* col; @@ -9521,11 +9481,22 @@ calc_row_difference( dfield_set_null(vfield); } num_v++; + ut_ad(field != table->found_next_number_field); } else { ufield->field_no = dict_col_get_clust_pos( &prebuilt->table->cols[i - num_v], clust_index); ufield->old_v_val = NULL; + if (field != table->found_next_number_field + || dfield_is_null(&ufield->new_val)) { + } else { + auto_inc = row_parse_int( + static_cast( + ufield->new_val.data), + ufield->new_val.len, + col->mtype, + col->prtype & DATA_UNSIGNED); + } } n_changed++; @@ -9576,7 +9547,7 @@ calc_row_difference( other changes. We piggy back our changes on the normal UPDATE to reduce processing and IO overhead. */ if (!prebuilt->table->fts) { - trx->fts_next_doc_id = 0; + trx->fts_next_doc_id = 0; } else if (changes_fts_column || changes_fts_doc_col) { dict_table_t* innodb_table = prebuilt->table; @@ -9787,20 +9758,15 @@ ha_innobase::update_row( } } - upd_t* uvect; - - if (m_prebuilt->upd_node) { - uvect = m_prebuilt->upd_node->update; - } else { - uvect = row_get_prebuilt_update_vector(m_prebuilt); - } + upd_t* uvect = row_get_prebuilt_update_vector(m_prebuilt); + ib_uint64_t autoinc; /* Build an update vector from the modified fields in the rows (uses m_upd_buf of the handle) */ error = calc_row_difference( uvect, old_row, new_row, table, m_upd_buf, m_upd_buf_size, - m_prebuilt, m_user_thd); + m_prebuilt, autoinc); if (error != DB_SUCCESS) { goto func_exit; @@ -9821,42 +9787,29 @@ ha_innobase::update_row( error = row_update_for_mysql((byte*) old_row, m_prebuilt); - /* We need to do some special AUTOINC handling for the following case: + if (error == DB_SUCCESS && autoinc) { + /* A value for an AUTO_INCREMENT column + was specified in the UPDATE statement. */ - INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE ... + autoinc = innobase_next_autoinc( + autoinc, 1, + m_prebuilt->autoinc_increment, + m_prebuilt->autoinc_offset, + innobase_get_int_col_max_value( + table->found_next_number_field)); - We need to use the AUTOINC counter that was actually used by - MySQL in the UPDATE statement, which can be different from the - value used in the INSERT statement. */ + error = innobase_set_max_autoinc(autoinc); - if (error == DB_SUCCESS - && table->next_number_field - && new_row == table->record[0] - && thd_sql_command(m_user_thd) == SQLCOM_INSERT - && trx->duplicates) { - - ulonglong auto_inc; - ulonglong col_max_value; - - auto_inc = table->next_number_field->val_int(); - - /* We need the upper limit of the col type to check for - whether we update the table autoinc counter or not. */ - col_max_value = - innobase_get_int_col_max_value(table->next_number_field); - - if (auto_inc <= col_max_value && auto_inc != 0) { - - ulonglong offset; - ulonglong increment; - - offset = m_prebuilt->autoinc_offset; - increment = m_prebuilt->autoinc_increment; - - auto_inc = innobase_next_autoinc( - auto_inc, 1, increment, offset, col_max_value); - - error = innobase_set_max_autoinc(auto_inc); + if (m_prebuilt->table->persistent_autoinc) { + /* Update the PAGE_ROOT_AUTO_INC. Yes, we do + this even if dict_table_t::autoinc already was + greater than autoinc, because we cannot know + if any INSERT actually used (and wrote to + PAGE_ROOT_AUTO_INC) a value bigger than our + autoinc. */ + btr_write_autoinc(dict_table_get_first_index( + m_prebuilt->table), + autoinc); } } @@ -14271,34 +14224,40 @@ create_table_info_t::create_table_update_dict() } } - /* Note: We can't call update_thd() as m_prebuilt will not be - setup at this stage and so we use thd. */ + if (const Field* ai = m_form->found_next_number_field) { + ut_ad(!innobase_is_v_fld(ai)); - /* We need to copy the AUTOINC value from the old table if - this is an ALTER|OPTIMIZE TABLE or CREATE INDEX because CREATE INDEX - does a table copy too. If query was one of : + ib_uint64_t autoinc = m_create_info->auto_increment_value; - CREATE TABLE ...AUTO_INCREMENT = x; or - ALTER TABLE...AUTO_INCREMENT = x; or - OPTIMIZE TABLE t; or - CREATE INDEX x on t(...); - - Find out a table definition from the dictionary and get - the current value of the auto increment field. Set a new - value to the auto increment field if the value is greater - than the maximum value in the column. */ - - if (((m_create_info->used_fields & HA_CREATE_USED_AUTO) - || thd_sql_command(m_thd) == SQLCOM_ALTER_TABLE - || thd_sql_command(m_thd) == SQLCOM_OPTIMIZE - || thd_sql_command(m_thd) == SQLCOM_CREATE_INDEX) - && m_create_info->auto_increment_value > 0) { - ib_uint64_t auto_inc_value; - - auto_inc_value = m_create_info->auto_increment_value; + if (autoinc == 0) { + autoinc = 1; + } dict_table_autoinc_lock(innobase_table); - dict_table_autoinc_initialize(innobase_table, auto_inc_value); + dict_table_autoinc_initialize(innobase_table, autoinc); + + if (dict_table_is_temporary(innobase_table)) { + /* AUTO_INCREMENT is not persistent for + TEMPORARY TABLE. Temporary tables are never + evicted. Keep the counter in memory only. */ + } else { + const unsigned col_no = innodb_col_no(ai); + + innobase_table->persistent_autoinc = 1 + + dict_table_get_nth_col_pos( + innobase_table, col_no, NULL); + + /* Persist the "last used" value, which + typically is AUTO_INCREMENT - 1. + In btr_create(), the value 0 was already written. */ + if (--autoinc) { + btr_write_autoinc( + dict_table_get_first_index( + innobase_table), + autoinc); + } + } + dict_table_autoinc_unlock(innobase_table); } @@ -16412,18 +16371,7 @@ ha_innobase::info_low( } if ((flag & HA_STATUS_AUTO) && table->found_next_number_field) { - - ulonglong auto_inc_val = innobase_peek_autoinc(); - /* Initialize autoinc value if not set. */ - if (auto_inc_val == 0) { - - dict_table_autoinc_lock(m_prebuilt->table); - innobase_initialize_autoinc(); - dict_table_autoinc_unlock(m_prebuilt->table); - - auto_inc_val = innobase_peek_autoinc(); - } - stats.auto_increment_value = auto_inc_val; + stats.auto_increment_value = innobase_peek_autoinc(); } func_exit: diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 99d50d82fd1..cef39e594b9 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -457,7 +457,6 @@ protected: int end_stmt(); dberr_t innobase_get_autoinc(ulonglong* value); - void innobase_initialize_autoinc(); dberr_t innobase_lock_autoinc(); ulonglong innobase_peek_autoinc(); dberr_t innobase_set_max_autoinc(ulonglong auto_inc); @@ -1142,6 +1141,13 @@ innobase_build_v_templ_callback( the table virtual columns' template */ typedef void (*my_gcolumn_templatecallback_t)(const TABLE*, void*); +/** Convert MySQL column number to dict_table_t::cols[] offset. +@param[in] field non-virtual column +@return column number relative to dict_table_t::cols[] */ +unsigned +innodb_col_no(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/ha_innopart.cc b/storage/innobase/handler/ha_innopart.cc index 5fd02dfa016..09a60be1577 100644 --- a/storage/innobase/handler/ha_innopart.cc +++ b/storage/innobase/handler/ha_innopart.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2016, 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 @@ -827,17 +828,6 @@ ha_innopart::alter_table_flags( return(HA_PARTITION_FUNCTION_SUPPORTED | HA_FAST_CHANGE_PARTITION); } -/** Internally called for initializing auto increment value. -Only called from ha_innobase::discard_or_import_table_space() -and should not do anything, since it is ha_innopart will initialize -it on first usage. */ -int -ha_innopart::innobase_initialize_autoinc() -{ - ut_ad(0); - return(0); -} - /** Set the autoinc column max value. This should only be called once from ha_innobase::open(). Therefore there's no need for a covering lock. @@ -892,78 +882,33 @@ ha_innopart::initialize_auto_increment( my_error(ER_AUTOINC_READ_FAILED, MYF(0)); error = HA_ERR_AUTOINC_READ_FAILED; } else { - dict_index_t* index; - const char* col_name; - ib_uint64_t read_auto_inc; - ib_uint64_t max_auto_inc = 0; - ulint err; - dict_table_t* ib_table; - ulonglong col_max_value; - - col_max_value = field->get_max_int_value(); + ib_uint64_t col_max_value = field->get_max_int_value(); update_thd(ha_thd()); - col_name = field->field_name; for (uint part = 0; part < m_tot_parts; part++) { - ib_table = m_part_share->get_table_part(part); + dict_table_t* ib_table + = m_part_share->get_table_part(part); dict_table_autoinc_lock(ib_table); - read_auto_inc = dict_table_autoinc_read(ib_table); - if (read_auto_inc != 0) { - set_if_bigger(max_auto_inc, read_auto_inc); - dict_table_autoinc_unlock(ib_table); - continue; - } - /* Execute SELECT MAX(col_name) FROM TABLE; */ - index = m_part_share->get_index( - part, table->s->next_number_index); - err = row_search_max_autoinc( - index, col_name, &read_auto_inc); + ut_ad(ib_table->persistent_autoinc); + ib_uint64_t read_auto_inc + = dict_table_autoinc_read(ib_table); + if (read_auto_inc == 0) { + read_auto_inc = btr_read_autoinc( + dict_table_get_first_index(ib_table)); - switch (err) { - case DB_SUCCESS: { /* At the this stage we do not know the increment nor the offset, so use a default increment of 1. */ - auto_inc = innobase_next_autoinc( + read_auto_inc = innobase_next_autoinc( read_auto_inc, 1, 1, 0, col_max_value); - set_if_bigger(max_auto_inc, auto_inc); dict_table_autoinc_initialize(ib_table, - auto_inc); - break; - } - case DB_RECORD_NOT_FOUND: - ib::error() << "MySQL and InnoDB data" - " dictionaries are out of sync. Unable" - " to find the AUTOINC column " - << col_name << " in the InnoDB table " - << index->table->name << ". We set the" - " next AUTOINC column value to 0, in" - " effect disabling the AUTOINC next" - " value generation."; - - ib::info() << "You can either set the next" - " AUTOINC value explicitly using ALTER" - " TABLE or fix the data dictionary by" - " recreating the table."; - - /* We want the open to succeed, so that the - user can take corrective action. ie. reads - should succeed but updates should fail. */ - - /* This will disable the AUTOINC generation. */ - auto_inc = 0; - goto done; - default: - /* row_search_max_autoinc() should only return - one of DB_SUCCESS or DB_RECORD_NOT_FOUND. */ - - ut_error; + read_auto_inc); } + set_if_bigger(auto_inc, read_auto_inc); dict_table_autoinc_unlock(ib_table); } - auto_inc = max_auto_inc; } done: @@ -1578,22 +1523,6 @@ ha_innopart::update_partition( DBUG_VOID_RETURN; } -/** Save currently highest auto increment value. -@param[in] nr Auto increment value to save. */ -void -ha_innopart::save_auto_increment( - ulonglong nr) -{ - - /* Store it in the shared dictionary of the partition. - TODO: When the new DD is done, store it in the table and make it - persistent! */ - - dict_table_autoinc_lock(m_prebuilt->table); - dict_table_autoinc_update_if_greater(m_prebuilt->table, nr + 1); - dict_table_autoinc_unlock(m_prebuilt->table); -} - /** Was the last returned row semi consistent read. In an UPDATE or DELETE, if the row under the cursor was locked by another transaction, and the engine used an optimistic read of the last diff --git a/storage/innobase/handler/ha_innopart.h b/storage/innobase/handler/ha_innopart.h index 8caa9cdd8d2..5f3f8eaa318 100644 --- a/storage/innobase/handler/ha_innopart.h +++ b/storage/innobase/handler/ha_innopart.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, 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 @@ -723,12 +724,6 @@ private: int next_partition_index(); - /** Internally called for initializing auto increment value. - Should never be called, but defined to catch such errors. - @return 0 on success else error code. */ - int - innobase_initialize_autoinc(); - /** Get the index for the current partition @param[in] keynr MySQL index number. @return InnoDB index or NULL. */ @@ -772,12 +767,6 @@ private: initialize_auto_increment( bool /* no_lock */); - /** Save currently highest auto increment value. - @param[in] nr Auto increment value to save. */ - void - save_auto_increment( - ulonglong nr); - /** Setup the ordered record buffer and the priority queue. @param[in] used_parts Number of used partitions in query. @return false for success, else true. */ diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 51aef715ee2..e09ed99937a 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -173,8 +173,6 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx const dtuple_t* add_cols; /** autoinc sequence to use */ ib_sequence_t sequence; - /** maximum auto-increment value */ - ulonglong max_autoinc; /** temporary table name to use for old table when renaming tables */ const char* tmp_name; /** whether the order of the clustered index is unchanged */ @@ -224,7 +222,6 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx add_cols (0), sequence(prebuilt->trx->mysql_thd, autoinc_col_min_value_arg, autoinc_col_max_value_arg), - max_autoinc (0), tmp_name (0), skip_pk_sort(false), num_to_add_vcol(0), @@ -4975,6 +4972,23 @@ new_clustered_failed: DBUG_EXECUTE_IF("innodb_alter_table_pk_assert_no_sort", DBUG_ASSERT(ctx->skip_pk_sort);); + DBUG_ASSERT(!ctx->new_table->persistent_autoinc); + if (const Field* ai = altered_table->found_next_number_field) { + const unsigned col_no = innodb_col_no(ai); + + ctx->new_table->persistent_autoinc = 1 + + dict_table_get_nth_col_pos( + ctx->new_table, col_no, NULL); + + /* Initialize the AUTO_INCREMENT sequence + to the rebuilt table from the old one. */ + if (!old_table->found_next_number_field) { + } else if (ib_uint64_t autoinc + = btr_read_autoinc(clust_index)) { + btr_write_autoinc(new_clust_index, autoinc); + } + } + if (ctx->online) { /* Allocate a log for online table rebuild. */ rw_lock_x_lock(&clust_index->lock); @@ -7400,83 +7414,120 @@ innobase_rename_or_enlarge_columns_cache( } } -/** Get the auto-increment value of the table on commit. +/** Set the auto-increment value of the table on commit. @param ha_alter_info Data used during in-place alter @param ctx In-place ALTER TABLE context @param altered_table MySQL table that is being altered -@param old_table MySQL table as it is before the ALTER operation -@return the next auto-increment value (0 if not present) */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) -ulonglong -commit_get_autoinc( -/*===============*/ +@param old_table MySQL table as it is before the ALTER operation */ +static MY_ATTRIBUTE((nonnull)) +void +commit_set_autoinc( Alter_inplace_info* ha_alter_info, ha_innobase_inplace_ctx*ctx, const TABLE* altered_table, const TABLE* old_table) { - ulonglong max_autoinc; - DBUG_ENTER("commit_get_autoinc"); if (!altered_table->found_next_number_field) { /* There is no AUTO_INCREMENT column in the table after the ALTER operation. */ - max_autoinc = 0; } else if (ctx->add_autoinc != ULINT_UNDEFINED) { + ut_ad(ctx->need_rebuild()); /* An AUTO_INCREMENT column was added. Get the last value from the sequence, which may be based on a supplied AUTO_INCREMENT value. */ - max_autoinc = ctx->sequence.last(); + ib_uint64_t autoinc = ctx->sequence.last(); + ctx->new_table->autoinc = autoinc; + /* Bulk index creation does not update + PAGE_ROOT_AUTO_INC, so we must persist the "last used" + value here. */ + btr_write_autoinc(dict_table_get_first_index(ctx->new_table), + autoinc - 1, true); } else if ((ha_alter_info->handler_flags & Alter_inplace_info::CHANGE_CREATE_OPTION) && (ha_alter_info->create_info->used_fields & HA_CREATE_USED_AUTO)) { - /* An AUTO_INCREMENT value was supplied, but the table was not - rebuilt. Get the user-supplied value or the last value from the - sequence. */ - ib_uint64_t max_value_table; - dberr_t err; + /* An AUTO_INCREMENT value was supplied by the user. + It must be persisted to the data file. */ + const Field* ai = old_table->found_next_number_field; + ut_ad(!strcmp(dict_table_get_col_name(ctx->old_table, + innodb_col_no(ai)), + ai->field_name)); - Field* autoinc_field = - old_table->found_next_number_field; - - dict_index_t* index = dict_table_get_index_on_first_col( - ctx->old_table, autoinc_field->field_index, - autoinc_field->field_name); - - max_autoinc = ha_alter_info->create_info->auto_increment_value; - - dict_table_autoinc_lock(ctx->old_table); - - err = row_search_max_autoinc( - index, autoinc_field->field_name, &max_value_table); - - if (err != DB_SUCCESS) { - ut_ad(0); - max_autoinc = 0; - } else if (max_autoinc <= max_value_table) { - ulonglong col_max_value; - ulonglong offset; - - col_max_value = innobase_get_int_col_max_value(autoinc_field); - - offset = ctx->prebuilt->autoinc_offset; - max_autoinc = innobase_next_autoinc( - max_value_table, 1, 1, offset, - col_max_value); + ib_uint64_t autoinc + = ha_alter_info->create_info->auto_increment_value; + if (autoinc == 0) { + autoinc = 1; } - dict_table_autoinc_unlock(ctx->old_table); - } else { - /* An AUTO_INCREMENT value was not specified. - Read the old counter value from the table. */ - ut_ad(old_table->found_next_number_field); - dict_table_autoinc_lock(ctx->old_table); - max_autoinc = ctx->old_table->autoinc; - dict_table_autoinc_unlock(ctx->old_table); + + if (autoinc >= ctx->old_table->autoinc) { + /* Persist the predecessor of the + AUTO_INCREMENT value as the last used one. */ + ctx->new_table->autoinc = autoinc--; + } else { + /* Mimic ALGORITHM=COPY in the following scenario: + + CREATE TABLE t (a SERIAL); + INSERT INTO t SET a=100; + ALTER TABLE t AUTO_INCREMENT = 1; + INSERT INTO t SET a=NULL; + SELECT * FROM t; + + By default, ALGORITHM=INPLACE would reset the + sequence to 1, while after ALGORITHM=COPY, the + last INSERT would use a value larger than 100. + + We could only search the tree to know current + max counter in the table and compare. */ + 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); + } + + ut_ad(index); + + ib_uint64_t max_in_table = index + ? row_search_max_autoinc(index) + : 0; + + if (autoinc <= max_in_table) { + ctx->new_table->autoinc = innobase_next_autoinc( + max_in_table, 1, + ctx->prebuilt->autoinc_increment, + ctx->prebuilt->autoinc_offset, + innobase_get_int_col_max_value(ai)); + /* Persist the maximum value as the + last used one. */ + autoinc = max_in_table; + } else { + /* Persist the predecessor of the + AUTO_INCREMENT value as the last used one. */ + ctx->new_table->autoinc = autoinc--; + } + } + + btr_write_autoinc(dict_table_get_first_index(ctx->new_table), + autoinc, true); + } else if (ctx->need_rebuild()) { + /* No AUTO_INCREMENT value was specified. + Copy it from the old table. */ + ctx->new_table->autoinc = ctx->old_table->autoinc; + /* The persistent value was already copied in + prepare_inplace_alter_table_dict() when ctx->new_table + was created. If this was a LOCK=NONE operation, the + AUTO_INCREMENT values would be updated during + row_log_table_apply(). If this was LOCK!=NONE, + the table contents could not possibly have changed + between prepare_inplace and commit_inplace. */ } - DBUG_RETURN(max_autoinc); + DBUG_VOID_RETURN; } /** Add or drop foreign key constraints to the data dictionary tables, @@ -8557,8 +8608,7 @@ ha_innobase::commit_inplace_alter_table( DBUG_ASSERT(new_clustered == ctx->need_rebuild()); - ctx->max_autoinc = commit_get_autoinc( - ha_alter_info, ctx, altered_table, table); + commit_set_autoinc(ha_alter_info, ctx, altered_table, table); if (ctx->need_rebuild()) { ctx->tmp_name = dict_mem_create_temporary_tablename( @@ -8896,14 +8946,6 @@ foreign_fail: (*pctx); DBUG_ASSERT(ctx->need_rebuild() == new_clustered); - if (altered_table->found_next_number_field) { - dict_table_t* t = ctx->new_table; - - dict_table_autoinc_lock(t); - dict_table_autoinc_initialize(t, ctx->max_autoinc); - dict_table_autoinc_unlock(t); - } - bool add_fts = false; /* Publish the created fulltext index, if any. diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index c177f23824f..c6abd02536d 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -2,7 +2,7 @@ Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2014, 2015, MariaDB Corporation. All Rights Reserved. +Copyright (c) 2014, 2016, MariaDB Corporation. All Rights Reserved. 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 @@ -353,6 +353,34 @@ btr_free( const page_id_t& page_id, const page_size_t& page_size); +/** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC. +@param[in,out] index clustered index +@return the last used AUTO_INCREMENT value +@retval 0 on error or if no AUTO_INCREMENT value was used yet */ +ib_uint64_t +btr_read_autoinc(dict_index_t* index) + MY_ATTRIBUTE((nonnull, warn_unused_result)); + +/** 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)); + +/** Write the next available AUTO_INCREMENT value to PAGE_ROOT_AUTO_INC. +@param[in,out] index clustered index +@param[in] autoinc the AUTO_INCREMENT value +@param[in] reset whether to reset the AUTO_INCREMENT + to a possibly smaller value than currently + exists in the page */ +void +btr_write_autoinc(dict_index_t* index, ib_uint64_t autoinc, bool reset = false) + MY_ATTRIBUTE((nonnull)); + /*************************************************************//** Makes tree one level higher by splitting the root, and inserts the tuple. It is assumed that mtr contains an x-latch on the tree. diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index e445331b60c..579f725f6bd 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -190,7 +190,10 @@ btr_cur_search_to_nth_level( RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr); /*!< in: mtr */ + mtr_t* mtr, /*!< in/out: mini-transaction */ + ib_uint64_t autoinc = 0); + /*!< in: PAGE_ROOT_AUTO_INC to be written + (0 if none) */ /*****************************************************************//** Opens a cursor at either end of an index. diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index 02f4faf24a5..947316f0e4d 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -114,9 +114,11 @@ btr_pcur_open_low( btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ + ib_uint64_t autoinc,/*!< in: PAGE_ROOT_AUTO_INC to be written + (0 if none) */ mtr_t* mtr); /*!< in: mtr */ #define btr_pcur_open(i,t,md,l,c,m) \ - btr_pcur_open_low(i,0,t,md,l,c,__FILE__,__LINE__,m) + btr_pcur_open_low(i,0,t,md,l,c,__FILE__,__LINE__,0,m) /**************************************************************//** Opens an persistent cursor to an index tree without initializing the cursor. */ diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic index e7ae85dd730..60790bc1316 100644 --- a/storage/innobase/include/btr0pcur.ic +++ b/storage/innobase/include/btr0pcur.ic @@ -434,6 +434,8 @@ btr_pcur_open_low( btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ + ib_uint64_t autoinc,/*!< in: PAGE_ROOT_AUTO_INC to be written + (0 if none) */ mtr_t* mtr) /*!< in: mtr */ { btr_cur_t* btr_cursor; @@ -454,7 +456,7 @@ btr_pcur_open_low( err = btr_cur_search_to_nth_level( index, level, tuple, mode, latch_mode, - btr_cursor, 0, file, line, mtr); + btr_cursor, 0, file, line, mtr, autoinc); if (err != DB_SUCCESS) { ib::warn() << " Error code: " << err diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 6ac696e75eb..d42a5f6ee4b 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -2,7 +2,7 @@ Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2015, MariaDB Corporation. +Copyright (c) 2013, 2016, 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 @@ -327,46 +327,52 @@ dict_table_autoinc_lock( /*====================*/ dict_table_t* table) /*!< in/out: table */ MY_ATTRIBUTE((nonnull)); -/********************************************************************//** -Unconditionally set the autoinc counter. */ +/** Unconditionally set the AUTO_INCREMENT counter. +@param[in,out] table table or partition +@param[in] value next available AUTO_INCREMENT value */ +MY_ATTRIBUTE((nonnull)) +UNIV_INLINE void -dict_table_autoinc_initialize( -/*==========================*/ - dict_table_t* table, /*!< in/out: table */ - ib_uint64_t value) /*!< in: next value to assign to a row */ - MY_ATTRIBUTE((nonnull)); +dict_table_autoinc_initialize(dict_table_t* table, ib_uint64_t value) +{ + ut_ad(dict_table_autoinc_own(table)); + table->autoinc = value; +} -/** Store autoinc value when the table is evicted. -@param[in] table table evicted */ -void -dict_table_autoinc_store( - const dict_table_t* table); - -/** Restore autoinc value when the table is loaded. -@param[in] table table loaded */ -void -dict_table_autoinc_restore( - dict_table_t* table); - -/********************************************************************//** -Reads the next autoinc value (== autoinc counter value), 0 if not yet -initialized. -@return value for a new row, or 0 */ +/** +@param[in] table table or partition +@return the next AUTO_INCREMENT counter value +@retval 0 if AUTO_INCREMENT is not yet initialized */ +MY_ATTRIBUTE((nonnull, warn_unused_result)) +UNIV_INLINE ib_uint64_t -dict_table_autoinc_read( -/*====================*/ - const dict_table_t* table) /*!< in: table */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); -/********************************************************************//** -Updates the autoinc counter if the value supplied is greater than the -current value. */ -void -dict_table_autoinc_update_if_greater( -/*=================================*/ +dict_table_autoinc_read(const dict_table_t* table) +{ + ut_ad(dict_table_autoinc_own(table)); + return(table->autoinc); +} + +/** Update the AUTO_INCREMENT sequence if the value supplied is greater +than the current value. +@param[in,out] table table or partition +@param[in] value AUTO_INCREMENT value that was assigned to a row +@return whether the AUTO_INCREMENT sequence was updated */ +MY_ATTRIBUTE((nonnull)) +UNIV_INLINE +bool +dict_table_autoinc_update_if_greater(dict_table_t* table, ib_uint64_t value) +{ + ut_ad(dict_table_autoinc_own(table)); + + if (value > table->autoinc) { + + table->autoinc = value; + return(true); + } + + return(false); +} - dict_table_t* table, /*!< in/out: table */ - ib_uint64_t value) /*!< in: value which was assigned to a row */ - MY_ATTRIBUTE((nonnull)); /********************************************************************//** Release the autoinc lock. */ void @@ -412,8 +418,9 @@ void dict_table_remove_from_cache_low( /*=============================*/ dict_table_t* table, /*!< in, own: table */ - ibool lru_evict); /*!< in: TRUE if table being evicted + ibool lru_evict) /*!< in: TRUE if table being evicted to make room in the table LRU list */ + MY_ATTRIBUTE((nonnull)); /**********************************************************************//** Renames a table object. @return TRUE if success */ @@ -1746,8 +1753,6 @@ extern dict_sys_t* dict_sys; /** the data dictionary rw-latch protecting dict_sys */ extern rw_lock_t* dict_operation_lock; -typedef std::map autoinc_map_t; - /* Dictionary system struct */ struct dict_sys_t{ DictSysMutex mutex; /*!< mutex protecting the data @@ -1783,8 +1788,6 @@ struct dict_sys_t{ UT_LIST_BASE_NODE_T(dict_table_t) table_non_LRU; /*!< List of tables that can't be evicted from the cache */ - autoinc_map_t* autoinc_map; /*!< Map to store table id and autoinc - when table is evicted */ }; #endif /* !UNIV_HOTBACKUP */ @@ -2052,18 +2055,6 @@ dict_index_node_ptr_max_size( /*=========================*/ const dict_index_t* index) /*!< in: index */ MY_ATTRIBUTE((warn_unused_result)); -/*****************************************************************//** -Get index by first field of the index -@return index which is having first field matches -with the field present in field_index position of table */ -UNIV_INLINE -dict_index_t* -dict_table_get_index_on_first_col( -/*==============================*/ - const dict_table_t* table, /*!< in: table */ - ulint col_index, /*!< in: position of column - in table */ - const char* field_name); /*!< in: field name */ /** Check if a column is a virtual column @param[in] col column @return true if it is a virtual column, false otherwise */ diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index c44dc156aaa..9bf6b8df4bc 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -1859,42 +1859,6 @@ dict_table_is_file_per_table( return(is_file_per_table ); } -/**********************************************************************//** -Get index by first field of the index -@return index which is having first field matches -with the field present in field_index position of table */ -UNIV_INLINE -dict_index_t* -dict_table_get_index_on_first_col( -/*==============================*/ - const dict_table_t* table, /*!< in: table */ - ulint col_index, /*!< in: position of column - in table */ - const char* field_name) /*!< in: field name */ -{ - ut_ad(col_index < table->n_cols); - - dict_col_t* column = dict_table_get_nth_col(table, col_index); - - for (dict_index_t* index = dict_table_get_first_index(table); - index != NULL; index = dict_table_get_next_index(index)) { - - if (index->fields[0].col == column) { - return(index); - } - } - - /* If not yet found use field_name */ - for (dict_index_t* index = dict_table_get_first_index(table); - index != NULL; index = dict_table_get_next_index(index)) { - if (!strcmp(index->fields[0].name, field_name)) { - return (index); - } - } - ut_error; - return(0); -} - /** Get reference count. @return current value of n_ref_count */ inline diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index c81d893ed5a..422404394be 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -1466,6 +1466,11 @@ struct dict_table_t { /** Number of virtual columns. */ unsigned n_v_cols:10; + /** 1 + the position of autoinc counter field in clustered + index, or 0 if there is no persistent AUTO_INCREMENT column in + the table. */ + unsigned persistent_autoinc:10; + /** TRUE if it's not an InnoDB system table or a table that has no FK relationships. */ unsigned can_be_evicted:1; diff --git a/storage/innobase/include/page0page.h b/storage/innobase/include/page0page.h index 3bb622127fb..b64602fe077 100644 --- a/storage/innobase/include/page0page.h +++ b/storage/innobase/include/page0page.h @@ -1,5 +1,4 @@ /***************************************************************************** - Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2013, 2016, MariaDB Corporation @@ -70,10 +69,12 @@ typedef byte page_header_t; #define PAGE_N_DIRECTION 14 /* number of consecutive inserts to the same direction */ #define PAGE_N_RECS 16 /* number of user records on the page */ -#define PAGE_MAX_TRX_ID 18 /* highest id of a trx which may have modified - a record on the page; trx_id_t; defined only - in secondary indexes and in the insert buffer - tree */ +/** The largest DB_TRX_ID that may have modified a record on the page; +Defined only in secondary index leaf pages and in change buffer leaf pages. +Otherwise written as 0. @see PAGE_ROOT_AUTO_INC */ +#define PAGE_MAX_TRX_ID 18 +/** The AUTO_INCREMENT value (on persistent clustered index root pages). */ +#define PAGE_ROOT_AUTO_INC PAGE_MAX_TRX_ID #define PAGE_HEADER_PRIV_END 26 /* end of private data structure of the page header which are set in a page create */ /*----*/ @@ -213,6 +214,32 @@ page_update_max_trx_id( uncompressed part will be updated, or NULL */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr); /*!< in/out: mini-transaction */ + +/** Persist the AUTO_INCREMENT value on a clustered index root page. +@param[in,out] block clustered index root page +@param[in] index clustered index +@param[in] autoinc next available AUTO_INCREMENT value +@param[in,out] mtr mini-transaction +@param[in] reset whether to reset the AUTO_INCREMENT + to a possibly smaller value than currently + exists in the page */ +void +page_set_autoinc( + buf_block_t* block, + const dict_index_t* index MY_ATTRIBUTE((unused)), + ib_uint64_t autoinc, + mtr_t* mtr, + bool reset) + MY_ATTRIBUTE((nonnull)); + +/** Read the AUTO_INCREMENT value from a clustered index root page. +@param[in] page clustered index root page +@return the persisted AUTO_INCREMENT value */ +MY_ATTRIBUTE((nonnull, warn_unused_result)) +UNIV_INLINE +ib_uint64_t +page_get_autoinc(const page_t* page); + /*************************************************************//** Returns the RTREE SPLIT SEQUENCE NUMBER (FIL_RTREE_SPLIT_SEQ_NUM). @return SPLIT SEQUENCE NUMBER */ diff --git a/storage/innobase/include/page0page.ic b/storage/innobase/include/page0page.ic index 0a0ff41774c..a3bbc09e60f 100644 --- a/storage/innobase/include/page0page.ic +++ b/storage/innobase/include/page0page.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, 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 @@ -108,6 +109,17 @@ page_update_max_trx_id( } } +/** Read the AUTO_INCREMENT value from a clustered index root page. +@param[in] page clustered index root page +@return the persisted AUTO_INCREMENT value */ +UNIV_INLINE +ib_uint64_t +page_get_autoinc(const page_t* page) +{ + ut_ad(page_is_root(page)); + return(mach_read_from_8(PAGE_HEADER + PAGE_ROOT_AUTO_INC + page)); +} + /*************************************************************//** Returns the RTREE SPLIT SEQUENCE NUMBER (FIL_RTREE_SPLIT_SEQ_NUM). @return SPLIT SEQUENCE NUMBER */ diff --git a/storage/innobase/include/row0row.h b/storage/innobase/include/row0row.h index 93ff90d020e..9ae1387810a 100644 --- a/storage/innobase/include/row0row.h +++ b/storage/innobase/include/row0row.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, 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 @@ -315,6 +316,22 @@ row_get_clust_rec( mtr_t* mtr) /*!< in: mtr */ MY_ATTRIBUTE((nonnull, warn_unused_result)); +/** Parse the integer data from specified data, which could be +DATA_INT, DATA_FLOAT or DATA_DOUBLE. If the value is less than 0 +and the type is not unsigned then we reset the value to 0 +@param[in] data data to read +@param[in] len length of data +@param[in] mtype mtype of data +@param[in] unsigned_type if the data is unsigned +@return the integer value from the data */ +inline +ib_uint64_t +row_parse_int( + const byte* data, + ulint len, + ulint mtype, + bool unsigned_type); + /** Result of row_search_index_entry */ enum row_search_result { ROW_FOUND = 0, /*!< the record was found */ diff --git a/storage/innobase/include/row0row.ic b/storage/innobase/include/row0row.ic index 08c0f18e95b..9243fae839a 100644 --- a/storage/innobase/include/row0row.ic +++ b/storage/innobase/include/row0row.ic @@ -173,3 +173,52 @@ row_build_row_ref_fast( } } } + +/** Parse the integer data from specified data, which could be +DATA_INT, DATA_FLOAT or DATA_DOUBLE. If the value is less than 0 +and the type is not unsigned then we reset the value to 0 +@param[in] data data to read +@param[in] len length of data +@param[in] mtype mtype of data +@param[in] unsigned_type if the data is unsigned +@return the integer value from the data */ +ib_uint64_t +row_parse_int( + const byte* data, + ulint len, + ulint mtype, + bool unsigned_type) +{ + ib_uint64_t value = 0; + + switch (mtype) { + case DATA_INT: + + ut_a(len <= sizeof value); + value = mach_read_int_type(data, len, unsigned_type); + break; + + case DATA_FLOAT: + + ut_a(len == sizeof(float)); + value = mach_float_read(data); + break; + + case DATA_DOUBLE: + + ut_a(len == sizeof(double)); + value = mach_double_read(data); + break; + + default: + ut_error; + + } + + if (!unsigned_type && static_cast(value) < 0) { + value = 0; + } + + return(value); +} + diff --git a/storage/innobase/include/row0sel.h b/storage/innobase/include/row0sel.h index 1186aa6f26e..3f87d78e25a 100644 --- a/storage/innobase/include/row0sel.h +++ b/storage/innobase/include/row0sel.h @@ -230,15 +230,12 @@ row_search_check_if_query_cache_permitted( trx_t* trx, /*!< in: transaction object */ const char* norm_name); /*!< in: concatenation of database name, '/' char, table name */ -/*******************************************************************//** -Read the max AUTOINC value from an index. -@return DB_SUCCESS if all OK else error code */ -dberr_t -row_search_max_autoinc( -/*===================*/ - dict_index_t* index, /*!< in: index to search */ - const char* col_name, /*!< in: autoinc column name */ - ib_uint64_t* value) /*!< out: AUTOINC value read */ +/** Read the max AUTOINC value from an index. +@param[in] index index starting with an AUTO_INCREMENT column +@return the largest AUTO_INCREMENT value +@retval 0 if no records were found */ +ib_uint64_t +row_search_max_autoinc(dict_index_t* index) MY_ATTRIBUTE((nonnull, warn_unused_result)); /** A structure for caching column values for prefetched rows */ diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc index 89669d09e89..d207371b213 100644 --- a/storage/innobase/page/page0page.cc +++ b/storage/innobase/page/page0page.cc @@ -228,6 +228,40 @@ page_set_max_trx_id( } } +/** Persist the AUTO_INCREMENT value on a clustered index root page. +@param[in,out] block clustered index root page +@param[in] index clustered index +@param[in] autoinc next available AUTO_INCREMENT value +@param[in,out] mtr mini-transaction +@param[in] reset whether to reset the AUTO_INCREMENT + to a possibly smaller value than currently + exists in the page */ +void +page_set_autoinc( + buf_block_t* block, + const dict_index_t* index MY_ATTRIBUTE((unused)), + ib_uint64_t autoinc, + mtr_t* mtr, + bool reset) +{ + ut_ad(mtr_memo_contains_flagged( + mtr, block, MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); + ut_ad(dict_index_is_clust(index)); + ut_ad(index->page == block->page.id.page_no()); + ut_ad(index->space == block->page.id.space()); + + byte* field = PAGE_HEADER + PAGE_ROOT_AUTO_INC + + buf_block_get_frame(block); + if (!reset && mach_read_from_8(field) >= autoinc) { + /* nothing to update */ + } else if (page_zip_des_t* page_zip = buf_block_get_page_zip(block)) { + mach_write_to_8(field, autoinc); + page_zip_write_header(page_zip, field, 8, mtr); + } else { + mlog_write_ull(field, autoinc, mtr); + } +} + /************************************************************//** Allocates a block of memory from the heap of an index page. @return pointer to start of allocated buffer, or NULL if allocation fails */ diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc index 277a47bbad5..621540afcef 100644 --- a/storage/innobase/page/page0zip.cc +++ b/storage/innobase/page/page0zip.cc @@ -4753,19 +4753,19 @@ page_zip_reorganize( page_get_infimum_rec(temp_page), index, mtr); - /* Temp-Tables are not shared across connection and so we avoid - locking of temp-tables as there would be no 2 trx trying to - operate on same temp-table in parallel. - max_trx_id is use to track which all trxs wrote to the page - in parallel but in case of temp-table this can is not needed. */ - if (!dict_index_is_clust(index) - && !dict_table_is_temporary(index->table) - && page_is_leaf(temp_page)) { - /* Copy max trx id to recreated page */ - trx_id_t max_trx_id = page_get_max_trx_id(temp_page); - page_set_max_trx_id(block, NULL, max_trx_id, NULL); - ut_ad(max_trx_id != 0); - } + /* Copy the PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC. */ + memcpy(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), + temp_page + (PAGE_HEADER + PAGE_MAX_TRX_ID), 8); + /* PAGE_MAX_TRX_ID must be set on secondary index leaf pages. */ + ut_ad(dict_index_is_clust(index) || !page_is_leaf(temp_page) + || dict_table_is_temporary(index->table) + || page_get_max_trx_id(page) != 0); + /* PAGE_MAX_TRX_ID must be zero on non-leaf pages other than + clustered index root pages. */ + ut_ad(page_get_max_trx_id(page) == 0 + || (dict_index_is_clust(index) + ? page_is_root(temp_page) + : page_is_leaf(temp_page))); /* Restore logging. */ mtr_set_log_mode(mtr, log_mode); diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 1ae11204f69..4cdcf137203 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -4009,18 +4009,15 @@ row_import_for_mysql( table->ibd_file_missing = false; table->flags2 &= ~DICT_TF2_DISCARDED; - /* Set autoinc value read from cfg file. The value is set to zero - if the cfg file is missing and is initialized later from table - column value. */ - ib::info() << table->name << " autoinc value set to " - << autoinc; + /* Set autoinc value read from .cfg file, if one was specified. + Otherwise, keep the PAGE_ROOT_AUTO_INC as is. */ + if (autoinc) { + ib::info() << table->name << " autoinc value set to " + << autoinc; - dict_table_autoinc_lock(table); - dict_table_autoinc_initialize(table, autoinc); - dict_table_autoinc_unlock(table); - - ut_a(err == DB_SUCCESS); + table->autoinc = autoinc--; + btr_write_autoinc(dict_table_get_first_index(table), autoinc); + } return(row_import_cleanup(prebuilt, trx, err)); } - diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 271d70d4da9..1f884017dd3 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, 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 @@ -2473,6 +2474,7 @@ row_ins_clust_index_entry_low( dberr_t err = DB_SUCCESS; big_rec_t* big_rec = NULL; mtr_t mtr; + ib_uint64_t auto_inc = 0; mem_heap_t* offsets_heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; @@ -2487,7 +2489,6 @@ row_ins_clust_index_entry_low( ut_ad(!thr_get_trx(thr)->in_rollback); mtr_start(&mtr); - mtr.set_named_space(index->space); if (dict_table_is_temporary(index->table)) { /* Disable REDO logging as the lifetime of temp-tables is @@ -2496,23 +2497,41 @@ row_ins_clust_index_entry_low( Disable locking as temp-tables are local to a connection. */ ut_ad(flags & BTR_NO_LOCKING_FLAG); + ut_ad(!dict_index_is_online_ddl(index)); + ut_ad(!index->table->persistent_autoinc); mtr.set_log_mode(MTR_LOG_NO_REDO); - } + } else { + mtr.set_named_space(index->space); - if (mode == BTR_MODIFY_LEAF && dict_index_is_online_ddl(index)) { - mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED; - mtr_s_lock(dict_index_get_lock(index), &mtr); + if (mode == BTR_MODIFY_LEAF + && dict_index_is_online_ddl(index)) { + mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED; + mtr_s_lock(dict_index_get_lock(index), &mtr); + } + + if (unsigned ai = index->table->persistent_autoinc) { + /* Prepare to persist the AUTO_INCREMENT value + from the index entry to PAGE_ROOT_AUTO_INC. */ + const dfield_t* dfield = dtuple_get_nth_field( + entry, ai - 1); + auto_inc = dfield_is_null(dfield) + ? 0 + : row_parse_int(static_cast( + dfield->data), + dfield->len, + dfield->type.mtype, + dfield->type.prtype + & DATA_UNSIGNED); + } } /* Note that we use PAGE_CUR_LE as the search mode, because then the function will return in both low_match and up_match of the cursor sensible values */ - btr_pcur_open(index, entry, PAGE_CUR_LE, mode, &pcur, &mtr); + btr_pcur_open_low(index, 0, entry, PAGE_CUR_LE, mode, &pcur, + __FILE__, __LINE__, auto_inc, &mtr); cursor = btr_pcur_get_btr_cur(&pcur); - - if (cursor) { - cursor->thr = thr; - } + cursor->thr = thr; #ifdef UNIV_DEBUG { diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 100e1bcb708..d2b0dbc0576 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1705,24 +1705,18 @@ row_get_prebuilt_update_vector( row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in MySQL handle */ { - dict_table_t* table = prebuilt->table; - upd_node_t* node; - - ut_ad(prebuilt && table && prebuilt->trx); - if (prebuilt->upd_node == NULL) { /* Not called before for this handle: create an update node and query graph to the prebuilt struct */ - node = row_create_update_node_for_mysql(table, prebuilt->heap); - - prebuilt->upd_node = node; + prebuilt->upd_node = row_create_update_node_for_mysql( + prebuilt->table, prebuilt->heap); prebuilt->upd_graph = static_cast( que_node_get_parent( pars_complete_graph_for_exec( - static_cast(node), + prebuilt->upd_node, prebuilt->trx, prebuilt->heap, prebuilt))); diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 5daa26319a5..967d0ac98cd 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -6045,29 +6045,7 @@ row_search_autoinc_read_column( data = rec_get_nth_field(rec, offsets, col_no, &len); - switch (mtype) { - case DATA_INT: - ut_a(len <= sizeof value); - value = mach_read_int_type(data, len, unsigned_type); - break; - - case DATA_FLOAT: - ut_a(len == sizeof(float)); - value = (ib_uint64_t) mach_float_read(data); - break; - - case DATA_DOUBLE: - ut_a(len == sizeof(double)); - value = (ib_uint64_t) mach_double_read(data); - break; - - default: - ut_error; - } - - if (!unsigned_type && static_cast(value) < 0) { - value = 0; - } + value = row_parse_int(data, len, mtype, unsigned_type); func_exit: if (UNIV_LIKELY_NULL(heap)) { @@ -6113,42 +6091,27 @@ row_search_get_max_rec( return(rec); } -/*******************************************************************//** -Read the max AUTOINC value from an index. -@return DB_SUCCESS if all OK else error code, DB_RECORD_NOT_FOUND if -column name can't be found in index */ -dberr_t -row_search_max_autoinc( -/*===================*/ - dict_index_t* index, /*!< in: index to search */ - const char* col_name, /*!< in: name of autoinc column */ - ib_uint64_t* value) /*!< out: AUTOINC value read */ +/** Read the max AUTOINC value from an index. +@param[in] index index starting with an AUTO_INCREMENT column +@return the largest AUTO_INCREMENT value +@retval 0 if no records were found */ +ib_uint64_t +row_search_max_autoinc(dict_index_t* index) { - dict_field_t* dfield = dict_index_get_nth_field(index, 0); - dberr_t error = DB_SUCCESS; - *value = 0; + const dict_field_t* dfield = dict_index_get_nth_field(index, 0); - if (strcmp(col_name, dfield->name) != 0) { - error = DB_RECORD_NOT_FOUND; - } else { - mtr_t mtr; - const rec_t* rec; + ib_uint64_t value = 0; - mtr_start(&mtr); + mtr_t mtr; + mtr.start(); - rec = row_search_get_max_rec(index, &mtr); - - if (rec != NULL) { - ibool unsigned_type = ( - dfield->col->prtype & DATA_UNSIGNED); - - *value = row_search_autoinc_read_column( - index, rec, 0, - dfield->col->mtype, unsigned_type); - } - - mtr_commit(&mtr); + if (const rec_t* rec = row_search_get_max_rec(index, &mtr)) { + value = row_search_autoinc_read_column( + index, rec, 0, + dfield->col->mtype, + dfield->col->prtype & DATA_UNSIGNED); } - return(error); + mtr.commit(); + return(value); }