mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
MDEV-6076 Persistent AUTO_INCREMENT for InnoDB
This should be functionally equivalent to WL#6204 in MySQL 8.0.0, with the notable difference that the file format changes are limited to repurposing a previously unused data field in B-tree pages. For persistent InnoDB tables, write the last used AUTO_INCREMENT value to the root page of the clustered index, in the previously unused (0) PAGE_MAX_TRX_ID field, now aliased as PAGE_ROOT_AUTO_INC. Unlike some other previously unused InnoDB data fields, this one was actually always zero-initialized, at least since MySQL 3.23.49. The writes to PAGE_ROOT_AUTO_INC are protected by SX or X latch on the root page. The SX latch will allow concurrent read access to the root page. (The field PAGE_ROOT_AUTO_INC will only be read on the first-time call to ha_innobase::open() from the SQL layer. The PAGE_ROOT_AUTO_INC can only be updated when executing SQL, so read/write races are not possible.) During INSERT, the PAGE_ROOT_AUTO_INC is updated by the low-level function btr_cur_search_to_nth_level(), adding no extra page access. [Adaptive hash index lookup will be disabled during INSERT.] If some rare UPDATE modifies an AUTO_INCREMENT column, the PAGE_ROOT_AUTO_INC will be adjusted in a separate mini-transaction in ha_innobase::update_row(). When a page is reorganized, we have to preserve the PAGE_ROOT_AUTO_INC field. During ALTER TABLE, the initial AUTO_INCREMENT value will be copied from the table. ALGORITHM=COPY and online log apply in LOCK=NONE will update PAGE_ROOT_AUTO_INC in real time. innodb_col_no(): Determine the dict_table_t::cols[] element index corresponding to a Field of a non-virtual column. (The MySQL 5.7 implementation of virtual columns breaks the 1:1 relationship between Field::field_index and dict_table_t::cols[]. Virtual columns are omitted from dict_table_t::cols[]. Therefore, we must translate the field_index of AUTO_INCREMENT columns into an index of dict_table_t::cols[].) Upgrade from old data files: By default, the AUTO_INCREMENT sequence in old data files would appear to be reset, because PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC would contain the value 0 in each clustered index page. In new data files, PAGE_ROOT_AUTO_INC can only be 0 if the table is empty or does not contain any AUTO_INCREMENT column. For backward compatibility, we use the old method of SELECT MAX(auto_increment_column) for initializing the sequence. btr_read_autoinc(): Read the AUTO_INCREMENT sequence from a new-format data file. btr_read_autoinc_with_fallback(): A variant of btr_read_autoinc() that will resort to reading MAX(auto_increment_column) for data files that did not use AUTO_INCREMENT yet. It was manually tested that during the execution of innodb.autoinc_persist the compatibility logic is not activated (for new files, PAGE_ROOT_AUTO_INC is never 0 in nonempty clustered index root pages). initialize_auto_increment(): Replaces ha_innobase::innobase_initialize_autoinc(). This initializes the AUTO_INCREMENT metadata. Only called from ha_innobase::open(). ha_innobase::info_low(): Do not try to lazily initialize dict_table_t::autoinc. It must already have been initialized by ha_innobase::open() or ha_innobase::create(). Note: The adjustments to class ha_innopart were not tested, because the source code (native InnoDB partitioning) is not being compiled.
This commit is contained in:
19
mysql-test/include/kill_and_restart_mysqld.inc
Normal file
19
mysql-test/include/kill_and_restart_mysqld.inc
Normal file
@@ -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
|
61
mysql-test/suite/innodb/include/autoinc_persist_alter.inc
Normal file
61
mysql-test/suite/innodb/include/autoinc_persist_alter.inc
Normal file
@@ -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;
|
1131
mysql-test/suite/innodb/r/autoinc_persist.result
Normal file
1131
mysql-test/suite/innodb/r/autoinc_persist.result
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
@@ -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);
|
||||
|
533
mysql-test/suite/innodb/t/autoinc_persist.test
Normal file
533
mysql-test/suite/innodb/t/autoinc_persist.test
Normal file
@@ -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;
|
@@ -1,3 +0,0 @@
|
||||
--default-storage-engine=MyISAM
|
||||
--innodb-strict-mode=0
|
||||
--innodb-file-per-table=0
|
@@ -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.
|
||||
|
@@ -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;
|
||||
#
|
||||
|
@@ -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 (
|
||||
|
@@ -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.*/
|
||||
|
@@ -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
|
||||
|
@@ -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)) {
|
||||
|
||||
|
@@ -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_t, ib_uint64_t>(
|
||||
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);
|
||||
|
@@ -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)
|
||||
|
@@ -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<const byte*>(
|
||||
ufield->new_val.data),
|
||||
ufield->new_val.len,
|
||||
col->mtype,
|
||||
col->prtype & DATA_UNSIGNED);
|
||||
}
|
||||
}
|
||||
n_changed++;
|
||||
|
||||
@@ -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:
|
||||
|
@@ -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. */
|
||||
|
@@ -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
|
||||
|
@@ -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. */
|
||||
|
@@ -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);
|
||||
|
||||
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 {
|
||||
/* 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);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
DBUG_RETURN(max_autoinc);
|
||||
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_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.
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
@@ -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. */
|
||||
|
@@ -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
|
||||
|
@@ -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<table_id_t, ib_uint64_t> 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 */
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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<int64_t>(value) < 0) {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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. */
|
||||
/* 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));
|
||||
}
|
||||
|
||||
|
@@ -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)) {
|
||||
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<const byte*>(
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
{
|
||||
|
@@ -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_fork_t*>(
|
||||
que_node_get_parent(
|
||||
pars_complete_graph_for_exec(
|
||||
static_cast<upd_node_t*>(node),
|
||||
prebuilt->upd_node,
|
||||
prebuilt->trx, prebuilt->heap,
|
||||
prebuilt)));
|
||||
|
||||
|
@@ -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<int64_t>(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);
|
||||
|
||||
ib_uint64_t value = 0;
|
||||
|
||||
if (strcmp(col_name, dfield->name) != 0) {
|
||||
error = DB_RECORD_NOT_FOUND;
|
||||
} else {
|
||||
mtr_t mtr;
|
||||
const rec_t* rec;
|
||||
mtr.start();
|
||||
|
||||
mtr_start(&mtr);
|
||||
|
||||
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(
|
||||
if (const rec_t* rec = row_search_get_max_rec(index, &mtr)) {
|
||||
value = row_search_autoinc_read_column(
|
||||
index, rec, 0,
|
||||
dfield->col->mtype, unsigned_type);
|
||||
dfield->col->mtype,
|
||||
dfield->col->prtype & DATA_UNSIGNED);
|
||||
}
|
||||
|
||||
mtr_commit(&mtr);
|
||||
}
|
||||
|
||||
return(error);
|
||||
mtr.commit();
|
||||
return(value);
|
||||
}
|
||||
|
Reference in New Issue
Block a user