MDEV-31957 Concurrent ALTER and ANALYZE collecting statistics can result in stale statistical data
Example of what causes the problem: T1: ANALYZE TABLE starts to collect statistics T2: ALTER TABLE starts by deleting statistics for all changed fields, then creates a temp table and copies data to it. T1: ANALYZE ends and writes to the statistics tables. T2: ALTER TABLE renames temp table in place of the old table. Now the statistics from analyze matches the old deleted tables. Fixed by waiting to delete old statistics until ALTER TABLE is the only one using the old table and ensure that rename of columns can handle swapping of column names. rename_columns_in_stat_table() (former rename_column_in_stat_tables()) now takes a list of columns to rename. It uses the following algorithm to update column_stats to be able to handle circular renames - While there are columns to be renamed and it is the first loop or last rename loop did change something. - Loop over all columns to be renamed - Change column name in column_stat - If fail because of duplicate key - If this is first change attempt for this column - Change column name to a temporary column name - If there was a conflicting row, replace it with the current row. else - Remove entry from column list - Loop over all remaining columns in the list - Remove the conflicting row - Change column from temporary name to final name in column_stat Other things: - Don't flush tables for every operation. Only flush when all updates are done. - Rename of columns was not handled in case of ALGORITHM=copy (old bug). - Fixed that we do not collect statistics for hidden hash columns used by UNIQUE constraint on long values. - Fixed that we do not collect statistics for blob columns referred by generated virtual columns. This was achieved by storing the fields for which we want to have statistics in table->has_value_set instead of in table->read_set. - Rename of indexes was not handled for persistent statistics. - This is now handled similar as rename of columns. Renamed columns are now stored in 'rename_stat_indexes' and handled in Alter_info::delete_statistics() together with drooped indexes. - ALTER TABLE .. ADD INDEX may instead of creating a new index rename an existing generated foreign key index. This was not reflected in the index_stats table because this was handled in mysql_prepare_create_table instead instead of in the mysql_alter() code. Fixed by adding a call in mysql_prepare_create_table() to drop the changed index. I also had to change the code that 'marked the index' to be ignored with code that would not destroy the original index name. Reviewer: Sergei Petrunia <sergey@mariadb.com>
This commit is contained in:
parent
388296a1e6
commit
e3b36b8f1b
@ -97,5 +97,290 @@ set use_stat_tables=default;
|
||||
set histogram_type=default;
|
||||
set histogram_size=default;
|
||||
#
|
||||
# MDEV-31957 Concurrent ALTER and ANALYZE collecting statistics can
|
||||
# result in stale statistical data
|
||||
#
|
||||
CREATE TABLE t1 (a INT, b VARCHAR(128));
|
||||
INSERT INTO t1 SELECT seq, CONCAT('s',seq) FROM seq_1_to_100;
|
||||
connect con1,localhost,root,,;
|
||||
ALTER TABLE t1 MODIFY b BLOB;
|
||||
connection default;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
connection con1;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
connection default;
|
||||
disconnect con1;
|
||||
select db_name,table_name,column_name from mysql.column_stats;
|
||||
db_name table_name column_name
|
||||
test t1 a
|
||||
drop table t1;
|
||||
#
|
||||
# Testing swapping columns
|
||||
#
|
||||
create or replace table t1 (a int primary key, b varchar(100), c varchar(100), d varchar(100)) engine=innodb;
|
||||
insert into t1 select seq, repeat('b',seq),repeat('c',mod(seq,5)), repeat('d',mod(seq,10)) from seq_1_to_100;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 b 50.5000
|
||||
test t1 c 2.0000
|
||||
test t1 d 4.5000
|
||||
alter table t1 change b c varchar(200), change c b varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 b 2.0000
|
||||
test t1 c 50.5000
|
||||
test t1 d 4.5000
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d b varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 b 4.5000
|
||||
test t1 c 2.0000
|
||||
test t1 d 50.5000
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d e varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 c 4.5000
|
||||
test t1 d 2.0000
|
||||
test t1 e 50.5000
|
||||
alter table t1 change e d varchar(200), drop column d;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 c 4.5000
|
||||
test t1 d 50.5000
|
||||
# Test having non existing column in column_stats
|
||||
insert into mysql.column_stats (db_name,table_name,column_name) values ("test","t1","b");
|
||||
alter table t1 change c d varchar(200), change d b varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 b 50.5000
|
||||
test t1 d 4.5000
|
||||
# Test having a conflicting temporary name
|
||||
insert into mysql.column_stats (db_name,table_name,column_name) values ("test","t1",concat("#sql_tmp_name#1",char(0)));
|
||||
alter table t1 change d b varchar(200), change b d varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 b 4.5000
|
||||
test t1 d 50.5000
|
||||
drop table t1;
|
||||
truncate table mysql.column_stats;
|
||||
create or replace table t1 (a int primary key, b varchar(100), c varchar(100), d varchar(100)) engine=myisam;
|
||||
insert into t1 select seq, repeat('b',seq),repeat('c',mod(seq,5)), repeat('d',mod(seq,10)) from seq_1_to_100;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 b 50.5000
|
||||
test t1 c 2.0000
|
||||
test t1 d 4.5000
|
||||
alter table t1 change b c varchar(200), change c b varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 d 4.5000
|
||||
analyze table t1 persistent for columns(b,c) indexes all;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status Table is already up to date
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d b varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 b 50.5000
|
||||
test t1 c 2.0000
|
||||
analyze table t1 persistent for columns(d) indexes all;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status Table is already up to date
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d e varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 c 50.5000
|
||||
test t1 d 2.0000
|
||||
test t1 e 50.5000
|
||||
alter table t1 change e d varchar(200), drop column d;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
db_name table_name column_name avg_length
|
||||
test t1 a 4.0000
|
||||
test t1 c 50.5000
|
||||
test t1 d 50.5000
|
||||
drop table t1;
|
||||
truncate table mysql.column_stats;
|
||||
create table t1 (a int, b blob, unique(b)) engine= innodb;
|
||||
analyze table t1 persistent for all;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze Warning Engine-independent statistics are not collected for column 'b'
|
||||
test.t1 analyze status OK
|
||||
select column_name from mysql.column_stats where table_name = 't1';
|
||||
column_name
|
||||
a
|
||||
drop table t1;
|
||||
create table t1 (a int, b blob, c int generated always as (length(b)) virtual) engine= innodb;
|
||||
analyze table t1 persistent for all;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze Warning Engine-independent statistics are not collected for column 'b'
|
||||
test.t1 analyze status OK
|
||||
select column_name from mysql.column_stats where table_name = 't1';
|
||||
column_name
|
||||
a
|
||||
c
|
||||
drop table t1;
|
||||
CREATE or replace TABLE t1 (a INT, b CHAR(8));
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status Table is already up to date
|
||||
ALTER TABLE t1 CHANGE b c INT, ORDER BY b;
|
||||
SELECT db_name, table_name, column_name FROM mysql.column_stats where table_name = 't1';
|
||||
db_name table_name column_name
|
||||
test t1 a
|
||||
test t1 c
|
||||
drop table t1;
|
||||
CREATE or replace TABLE t1 (a INT, b CHAR(8));
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status Table is already up to date
|
||||
ALTER TABLE t1 RENAME COLUMN b to c, ALGORITHM=COPY;
|
||||
SELECT db_name, table_name, column_name FROM mysql.column_stats where table_name = 't1';
|
||||
db_name table_name column_name
|
||||
test t1 a
|
||||
test t1 c
|
||||
drop table t1;
|
||||
#
|
||||
# Testing swapping indexes
|
||||
#
|
||||
create or replace table t1 (a int primary key, b varchar(100), c varchar(100), d varchar(100), index (b), index(c), index(d,b)) engine=innodb;
|
||||
insert into t1 select seq, repeat('b',seq),repeat('c',mod(seq,5)), repeat('d',mod(seq,10)) from seq_1_to_100;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
db_name table_name index_name prefix_arity avg_frequency
|
||||
test t1 PRIMARY 1 1.0000
|
||||
test t1 b 1 1.0000
|
||||
test t1 b 2 1.0000
|
||||
test t1 c 1 20.0000
|
||||
test t1 c 2 1.0000
|
||||
test t1 d 1 10.0000
|
||||
test t1 d 2 1.0000
|
||||
test t1 d 3 1.0000
|
||||
alter table t1 rename index b to c, rename index c to d, rename index d to b;
|
||||
select * from mysql.index_stats order by index_name;
|
||||
db_name table_name index_name prefix_arity avg_frequency
|
||||
test t1 PRIMARY 1 1.0000
|
||||
test t1 b 1 10.0000
|
||||
test t1 b 2 1.0000
|
||||
test t1 b 3 1.0000
|
||||
test t1 c 1 1.0000
|
||||
test t1 c 2 1.0000
|
||||
test t1 d 1 20.0000
|
||||
test t1 d 2 1.0000
|
||||
alter table t1 rename index b to c, rename index c to d, rename index d to e;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
db_name table_name index_name prefix_arity avg_frequency
|
||||
test t1 PRIMARY 1 1.0000
|
||||
test t1 c 1 10.0000
|
||||
test t1 c 2 1.0000
|
||||
test t1 c 3 1.0000
|
||||
test t1 d 1 1.0000
|
||||
test t1 d 2 1.0000
|
||||
test t1 e 1 20.0000
|
||||
test t1 e 2 1.0000
|
||||
alter table t1 rename index e to b;
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d e varchar(200) ;
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`a` int(11) NOT NULL,
|
||||
`c` varchar(200) DEFAULT NULL,
|
||||
`d` varchar(200) DEFAULT NULL,
|
||||
`e` varchar(200) DEFAULT NULL,
|
||||
PRIMARY KEY (`a`),
|
||||
KEY `d` (`c`),
|
||||
KEY `b` (`d`),
|
||||
KEY `c` (`e`,`c`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
db_name table_name index_name prefix_arity avg_frequency
|
||||
test t1 PRIMARY 1 1.0000
|
||||
test t1 b 1 20.0000
|
||||
test t1 b 2 1.0000
|
||||
test t1 c 1 10.0000
|
||||
test t1 c 2 1.0000
|
||||
test t1 c 3 1.0000
|
||||
test t1 d 1 1.0000
|
||||
test t1 d 2 1.0000
|
||||
# Test having a conflicting temporary name
|
||||
insert into mysql.index_stats (db_name,table_name,index_name,prefix_arity) values ("test","t1",concat("#sql_tmp_name#1",char(0)),1);
|
||||
alter table t1 rename index c to d, rename index d to c;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
db_name table_name index_name prefix_arity avg_frequency
|
||||
test t1 PRIMARY 1 1.0000
|
||||
test t1 b 1 20.0000
|
||||
test t1 b 2 1.0000
|
||||
test t1 c 1 1.0000
|
||||
test t1 c 2 1.0000
|
||||
test t1 d 1 10.0000
|
||||
test t1 d 2 1.0000
|
||||
test t1 d 3 1.0000
|
||||
drop table t1;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
db_name table_name index_name prefix_arity avg_frequency
|
||||
#
|
||||
# Test of adding key that replaces foreign key
|
||||
#
|
||||
CREATE TABLE t1 (aaaa INT, b INT, KEY(b), FOREIGN KEY(aaaa) REFERENCES t1(b)) ENGINE=InnoDB;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
SELECT index_name FROM mysql.index_stats WHERE table_name = 't1' order by index_name;
|
||||
index_name
|
||||
aaaa
|
||||
b
|
||||
ALTER TABLE t1 ADD KEY idx(aaaa);
|
||||
SHOW CREATE TABLE t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`aaaa` int(11) DEFAULT NULL,
|
||||
`b` int(11) DEFAULT NULL,
|
||||
KEY `b` (`b`),
|
||||
KEY `idx` (`aaaa`),
|
||||
CONSTRAINT `t1_ibfk_1` FOREIGN KEY (`aaaa`) REFERENCES `t1` (`b`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
|
||||
SELECT index_name FROM mysql.index_stats WHERE table_name = 't1' order by index_name;
|
||||
index_name
|
||||
b
|
||||
truncate table mysql.index_stats;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
SELECT index_name FROM mysql.index_stats WHERE table_name = 't1' order by index_name;
|
||||
index_name
|
||||
b
|
||||
idx
|
||||
ALTER TABLE t1 DROP KEY idx;
|
||||
ERROR HY000: Cannot drop index 'idx': needed in a foreign key constraint
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# End of 10.6 tests
|
||||
#
|
||||
|
@ -1,4 +1,5 @@
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_innodb.inc
|
||||
|
||||
#
|
||||
# Bug #10901 Analyze Table on new table destroys table
|
||||
@ -111,6 +112,148 @@ set use_stat_tables=default;
|
||||
set histogram_type=default;
|
||||
set histogram_size=default;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-31957 Concurrent ALTER and ANALYZE collecting statistics can
|
||||
--echo # result in stale statistical data
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (a INT, b VARCHAR(128));
|
||||
INSERT INTO t1 SELECT seq, CONCAT('s',seq) FROM seq_1_to_100;
|
||||
|
||||
# We have to disable query log as the ANALYZE TABLE can be run in different
|
||||
# order. The important thing is what is finally in column_stats
|
||||
--disable_result_log
|
||||
--connect (con1,localhost,root,,)
|
||||
--send ALTER TABLE t1 MODIFY b BLOB
|
||||
|
||||
--connection default
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
|
||||
--connection con1
|
||||
--reap
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
--connection default
|
||||
--disconnect con1
|
||||
--enable_result_log
|
||||
select db_name,table_name,column_name from mysql.column_stats;
|
||||
drop table t1;
|
||||
|
||||
--echo #
|
||||
--echo # Testing swapping columns
|
||||
--echo #
|
||||
|
||||
create or replace table t1 (a int primary key, b varchar(100), c varchar(100), d varchar(100)) engine=innodb;
|
||||
insert into t1 select seq, repeat('b',seq),repeat('c',mod(seq,5)), repeat('d',mod(seq,10)) from seq_1_to_100;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
alter table t1 change b c varchar(200), change c b varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d b varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d e varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
alter table t1 change e d varchar(200), drop column d;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
|
||||
--echo # Test having non existing column in column_stats
|
||||
|
||||
insert into mysql.column_stats (db_name,table_name,column_name) values ("test","t1","b");
|
||||
alter table t1 change c d varchar(200), change d b varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
|
||||
--echo # Test having a conflicting temporary name
|
||||
insert into mysql.column_stats (db_name,table_name,column_name) values ("test","t1",concat("#sql_tmp_name#1",char(0)));
|
||||
alter table t1 change d b varchar(200), change b d varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
|
||||
drop table t1;
|
||||
truncate table mysql.column_stats;
|
||||
|
||||
create or replace table t1 (a int primary key, b varchar(100), c varchar(100), d varchar(100)) engine=myisam;
|
||||
insert into t1 select seq, repeat('b',seq),repeat('c',mod(seq,5)), repeat('d',mod(seq,10)) from seq_1_to_100;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
alter table t1 change b c varchar(200), change c b varchar(200);
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
analyze table t1 persistent for columns(b,c) indexes all;
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d b varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
analyze table t1 persistent for columns(d) indexes all;
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d e varchar(200) ;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
alter table t1 change e d varchar(200), drop column d;
|
||||
select db_name,table_name,column_name,avg_length from mysql.column_stats order by column_name;
|
||||
drop table t1;
|
||||
truncate table mysql.column_stats;
|
||||
|
||||
create table t1 (a int, b blob, unique(b)) engine= innodb;
|
||||
analyze table t1 persistent for all;
|
||||
select column_name from mysql.column_stats where table_name = 't1';
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a int, b blob, c int generated always as (length(b)) virtual) engine= innodb;
|
||||
analyze table t1 persistent for all;
|
||||
select column_name from mysql.column_stats where table_name = 't1';
|
||||
drop table t1;
|
||||
|
||||
CREATE or replace TABLE t1 (a INT, b CHAR(8));
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
ALTER TABLE t1 CHANGE b c INT, ORDER BY b;
|
||||
SELECT db_name, table_name, column_name FROM mysql.column_stats where table_name = 't1';
|
||||
drop table t1;
|
||||
|
||||
CREATE or replace TABLE t1 (a INT, b CHAR(8));
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
ALTER TABLE t1 RENAME COLUMN b to c, ALGORITHM=COPY;
|
||||
SELECT db_name, table_name, column_name FROM mysql.column_stats where table_name = 't1';
|
||||
drop table t1;
|
||||
|
||||
--echo #
|
||||
--echo # Testing swapping indexes
|
||||
--echo #
|
||||
|
||||
create or replace table t1 (a int primary key, b varchar(100), c varchar(100), d varchar(100), index (b), index(c), index(d,b)) engine=innodb;
|
||||
insert into t1 select seq, repeat('b',seq),repeat('c',mod(seq,5)), repeat('d',mod(seq,10)) from seq_1_to_100;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
alter table t1 rename index b to c, rename index c to d, rename index d to b;
|
||||
select * from mysql.index_stats order by index_name;
|
||||
|
||||
alter table t1 rename index b to c, rename index c to d, rename index d to e;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
alter table t1 rename index e to b;
|
||||
alter table t1 change b c varchar(200), change c d varchar(200), change d e varchar(200) ;
|
||||
show create table t1;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
|
||||
--echo # Test having a conflicting temporary name
|
||||
insert into mysql.index_stats (db_name,table_name,index_name,prefix_arity) values ("test","t1",concat("#sql_tmp_name#1",char(0)),1);
|
||||
alter table t1 rename index c to d, rename index d to c;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
drop table t1;
|
||||
select * from mysql.index_stats order by index_name, prefix_arity;
|
||||
|
||||
--echo #
|
||||
--echo # Test of adding key that replaces foreign key
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (aaaa INT, b INT, KEY(b), FOREIGN KEY(aaaa) REFERENCES t1(b)) ENGINE=InnoDB;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
|
||||
SELECT index_name FROM mysql.index_stats WHERE table_name = 't1' order by index_name;
|
||||
|
||||
ALTER TABLE t1 ADD KEY idx(aaaa);
|
||||
SHOW CREATE TABLE t1;
|
||||
|
||||
SELECT index_name FROM mysql.index_stats WHERE table_name = 't1' order by index_name;
|
||||
|
||||
truncate table mysql.index_stats;
|
||||
ANALYZE TABLE t1 PERSISTENT FOR ALL;
|
||||
SELECT index_name FROM mysql.index_stats WHERE table_name = 't1' order by index_name;
|
||||
|
||||
--error ER_DROP_INDEX_FK
|
||||
ALTER TABLE t1 DROP KEY idx;
|
||||
DROP TABLE t1;
|
||||
--echo #
|
||||
--echo # End of 10.6 tests
|
||||
--echo #
|
||||
|
@ -552,6 +552,13 @@ test t1 PRIMARY 1 1.0000
|
||||
test t1 idx2 1 7.0000
|
||||
test t1 idx2 2 2.3846
|
||||
test t1 idx3 1 8.5000
|
||||
ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(x) INDEXES();
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
SELECT * FROM mysql.column_stats where column_name="x";
|
||||
db_name table_name column_name min_value max_value nulls_ratio avg_length avg_frequency hist_size hist_type histogram
|
||||
test t1 x vvvvvvvvvvvvv zzzzzzzzzzzzzzzzzz 0.2000 17.1250 6.4000 0 NULL NULL
|
||||
ALTER TABLE t1 CHANGE COLUMN x b varchar(32);
|
||||
SHOW CREATE TABLE t1;
|
||||
Table Create Table
|
||||
@ -668,8 +675,8 @@ test t1 PRIMARY 1 1.0000
|
||||
test t1 idx2 1 7.0000
|
||||
test t1 idx2 2 2.3846
|
||||
test t1 idx3 1 8.5000
|
||||
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/save_column_stats'
|
||||
INTO TABLE mysql.column_stats
|
||||
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/save_column_stats' IGNORE
|
||||
INTO TABLE mysql.column_stats
|
||||
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n';
|
||||
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/save_index_stats'
|
||||
INTO TABLE mysql.index_stats
|
||||
@ -1106,12 +1113,17 @@ test t2 idx4 1 6.2000
|
||||
test t2 idx4 2 1.7222
|
||||
test t2 idx4 3 1.1154
|
||||
test t2 idx4 4 1.0000
|
||||
SELECT * FROM mysql.column_stats where column_name="b";
|
||||
db_name table_name column_name min_value max_value nulls_ratio avg_length avg_frequency hist_size hist_type histogram
|
||||
test t2 b vvvvvvvvvvvvv zzzzzzzzzzzzzzzzzz 0.2000 17.1250 6.4000 0 NULL NULL
|
||||
ALTER TABLE t2 CHANGE COLUMN b b varchar(30);
|
||||
SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name;
|
||||
db_name table_name index_name prefix_arity avg_frequency
|
||||
test t2 idx2 1 7.0000
|
||||
test t2 idx2 2 2.3846
|
||||
test t2 idx3 1 8.5000
|
||||
SELECT * FROM mysql.column_stats where column_name="b";
|
||||
db_name table_name column_name min_value max_value nulls_ratio avg_length avg_frequency hist_size hist_type histogram
|
||||
ANALYZE TABLE t2 PERSISTENT FOR COLUMNS ALL INDEXES ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t2 analyze status Engine-independent statistics collected
|
||||
@ -1147,6 +1159,9 @@ test t2 idx4 1 6.2000
|
||||
test t2 idx4 2 1.7222
|
||||
test t2 idx4 3 1.1154
|
||||
test t2 idx4 4 1.0000
|
||||
SELECT * FROM mysql.column_stats where column_name="b";
|
||||
db_name table_name column_name min_value max_value nulls_ratio avg_length avg_frequency hist_size hist_type histogram
|
||||
test t2 b 0 zzzzzzzzzzzzzzzzzz 0.0000 13.9000 6.6667 0 NULL NULL
|
||||
ANALYZE TABLE t2 PERSISTENT FOR COLUMNS ALL INDEXES ALL;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t2 analyze status Engine-independent statistics collected
|
||||
|
@ -305,6 +305,10 @@ SELECT * FROM mysql.column_stats;
|
||||
--sorted_result
|
||||
SELECT * FROM mysql.index_stats;
|
||||
|
||||
# Add 'x' back that was deleted above
|
||||
ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(x) INDEXES();
|
||||
SELECT * FROM mysql.column_stats where column_name="x";
|
||||
|
||||
ALTER TABLE t1 CHANGE COLUMN x b varchar(32);
|
||||
SHOW CREATE TABLE t1;
|
||||
--sorted_result
|
||||
@ -347,7 +351,7 @@ SELECT * FROM mysql.index_stats;
|
||||
|
||||
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
|
||||
eval
|
||||
LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/save_column_stats'
|
||||
LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/save_column_stats' IGNORE
|
||||
INTO TABLE mysql.column_stats
|
||||
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n';
|
||||
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
|
||||
@ -495,14 +499,17 @@ SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name;
|
||||
ANALYZE TABLE t2 PERSISTENT FOR COLUMNS() INDEXES ALL;
|
||||
SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name;
|
||||
|
||||
SELECT * FROM mysql.column_stats where column_name="b";
|
||||
ALTER TABLE t2 CHANGE COLUMN b b varchar(30);
|
||||
SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name;
|
||||
SELECT * FROM mysql.column_stats where column_name="b";
|
||||
|
||||
ANALYZE TABLE t2 PERSISTENT FOR COLUMNS ALL INDEXES ALL;
|
||||
SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name;
|
||||
|
||||
ALTER TABLE t2 CHANGE COLUMN b b varchar(32);
|
||||
SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name;
|
||||
SELECT * FROM mysql.column_stats where column_name="b";
|
||||
|
||||
ANALYZE TABLE t2 PERSISTENT FOR COLUMNS ALL INDEXES ALL;
|
||||
SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name;
|
||||
|
@ -517,8 +517,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
List<Item> field_list;
|
||||
Protocol *protocol= thd->protocol;
|
||||
LEX *lex= thd->lex;
|
||||
int result_code;
|
||||
int compl_result_code;
|
||||
bool need_repair_or_alter= 0;
|
||||
wait_for_commit* suspended_wfc;
|
||||
bool is_table_modified= false;
|
||||
@ -562,7 +560,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
bool collect_eis= FALSE;
|
||||
bool open_for_modify= org_open_for_modify;
|
||||
Recreate_info recreate_info;
|
||||
int compl_result_code, result_code;
|
||||
|
||||
compl_result_code= result_code= HA_ADMIN_FAILED;
|
||||
storage_engine_name[0]= 0; // Marker that's not used
|
||||
|
||||
DBUG_PRINT("admin", ("table: '%s'.'%s'", db, table->table_name.str));
|
||||
@ -880,6 +880,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
|
||||
}
|
||||
|
||||
/* Note: compl_result_code can be different from result_code here */
|
||||
if (compl_result_code == HA_ADMIN_OK && collect_eis)
|
||||
{
|
||||
if (result_code == HA_ERR_TABLE_READONLY)
|
||||
@ -920,19 +921,35 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
Field **field_ptr= tab->field;
|
||||
if (!lex->column_list)
|
||||
{
|
||||
/* Fields we have to read from the engine */
|
||||
bitmap_clear_all(tab->read_set);
|
||||
/* Fields we want to have statistics for */
|
||||
bitmap_clear_all(&tab->has_value_set);
|
||||
|
||||
for (uint fields= 0; *field_ptr; field_ptr++, fields++)
|
||||
{
|
||||
Field *field= *field_ptr;
|
||||
if (field->flags & LONG_UNIQUE_HASH_FIELD)
|
||||
{
|
||||
/*
|
||||
No point in doing statistic for hash fields that should be
|
||||
unique
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
Note that type() always return MYSQL_TYPE_BLOB for
|
||||
all blob types. Another function needs to be added
|
||||
if we in the future want to distingush between blob
|
||||
types here.
|
||||
*/
|
||||
enum enum_field_types type= (*field_ptr)->type();
|
||||
enum enum_field_types type= field->type();
|
||||
if (type < MYSQL_TYPE_TINY_BLOB ||
|
||||
type > MYSQL_TYPE_BLOB)
|
||||
tab->field[fields]->register_field_in_read_map();
|
||||
{
|
||||
field->register_field_in_read_map();
|
||||
bitmap_set_bit(&tab->has_value_set, field->field_index);
|
||||
}
|
||||
else
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_NO_EIS_FOR_FIELD,
|
||||
@ -946,9 +963,15 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
LEX_STRING *column_name;
|
||||
List_iterator_fast<LEX_STRING> it(*lex->column_list);
|
||||
|
||||
/* Fields we have to read from the engine */
|
||||
bitmap_clear_all(tab->read_set);
|
||||
/* Fields we want to have statistics for */
|
||||
bitmap_clear_all(&tab->has_value_set);
|
||||
|
||||
while ((column_name= it++))
|
||||
{
|
||||
Field *field;
|
||||
enum enum_field_types type;
|
||||
if (tab->s->fieldnames.type_names == 0 ||
|
||||
(pos= find_type(&tab->s->fieldnames, column_name->str,
|
||||
column_name->length, 1)) <= 0)
|
||||
@ -957,10 +980,15 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
break;
|
||||
}
|
||||
pos--;
|
||||
enum enum_field_types type= tab->field[pos]->type();
|
||||
if (type < MYSQL_TYPE_TINY_BLOB ||
|
||||
type > MYSQL_TYPE_BLOB)
|
||||
tab->field[pos]->register_field_in_read_map();
|
||||
field= tab->field[pos];
|
||||
type= field->type();
|
||||
if (!(field->flags & LONG_UNIQUE_HASH_FIELD) &&
|
||||
(type < MYSQL_TYPE_TINY_BLOB ||
|
||||
type > MYSQL_TYPE_BLOB))
|
||||
{
|
||||
field->register_field_in_read_map();
|
||||
bitmap_set_bit(&tab->has_value_set, field->field_index);
|
||||
}
|
||||
else
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_NO_EIS_FOR_FIELD,
|
||||
@ -991,12 +1019,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
}
|
||||
}
|
||||
/* Ensure that number of records are updated */
|
||||
table->table->file->info(HA_STATUS_VARIABLE);
|
||||
tab->file->info(HA_STATUS_VARIABLE);
|
||||
if (!(compl_result_code=
|
||||
alloc_statistics_for_table(thd, table->table)) &&
|
||||
alloc_statistics_for_table(thd, tab,
|
||||
&tab->has_value_set)) &&
|
||||
!(compl_result_code=
|
||||
collect_statistics_for_table(thd, table->table)))
|
||||
compl_result_code= update_statistics_for_table(thd, table->table);
|
||||
collect_statistics_for_table(thd, tab)))
|
||||
compl_result_code= update_statistics_for_table(thd, tab);
|
||||
}
|
||||
else
|
||||
compl_result_code= HA_ADMIN_FAILED;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "sql_parse.h" // check_access
|
||||
#include "sql_table.h" // mysql_alter_table,
|
||||
// mysql_exchange_partition
|
||||
#include "sql_statistics.h" // delete_statistics_for_column
|
||||
#include "sql_alter.h"
|
||||
#include "wsrep_mysqld.h"
|
||||
|
||||
@ -31,6 +32,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
|
||||
check_constraint_list(rhs.check_constraint_list, mem_root),
|
||||
flags(rhs.flags), partition_flags(rhs.partition_flags),
|
||||
keys_onoff(rhs.keys_onoff),
|
||||
original_table(0),
|
||||
partition_names(rhs.partition_names, mem_root),
|
||||
num_parts(rhs.num_parts),
|
||||
requested_algorithm(rhs.requested_algorithm),
|
||||
@ -297,6 +299,79 @@ uint Alter_info::check_vcol_field(Item_field *item) const
|
||||
}
|
||||
|
||||
|
||||
bool Alter_info::collect_renamed_fields(THD *thd)
|
||||
{
|
||||
List_iterator_fast<Create_field> new_field_it;
|
||||
Create_field *new_field;
|
||||
DBUG_ENTER("Alter_info::collect_renamed_fields");
|
||||
|
||||
new_field_it.init(create_list);
|
||||
while ((new_field= new_field_it++))
|
||||
{
|
||||
Field *field= new_field->field;
|
||||
|
||||
if (new_field->field &&
|
||||
cmp(&field->field_name, &new_field->field_name))
|
||||
{
|
||||
field->flags|= FIELD_IS_RENAMED;
|
||||
if (add_stat_rename_field(field,
|
||||
&new_field->field_name,
|
||||
thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Delete duplicate index found during mysql_prepare_create_table()
|
||||
|
||||
Notes:
|
||||
- In case of temporary generated foreign keys, the key_name may not
|
||||
be set! These keys are ignored.
|
||||
*/
|
||||
|
||||
bool Alter_info::add_stat_drop_index(THD *thd, const LEX_CSTRING *key_name)
|
||||
{
|
||||
if (original_table && key_name->length) // If from alter table
|
||||
{
|
||||
KEY *key_info= original_table->key_info;
|
||||
for (uint i= 0; i < original_table->s->keys; i++, key_info++)
|
||||
{
|
||||
if (key_info->name.length &&
|
||||
!lex_string_cmp(system_charset_info, &key_info->name,
|
||||
key_name))
|
||||
return add_stat_drop_index(key_info, false, thd->mem_root);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Alter_info::apply_statistics_deletes_renames(THD *thd, TABLE *table)
|
||||
{
|
||||
List_iterator<Field> it_drop_field(drop_stat_fields);
|
||||
List_iterator<RENAME_COLUMN_STAT_PARAMS> it_rename_field(rename_stat_fields);
|
||||
List_iterator<DROP_INDEX_STAT_PARAMS> it_drop_index(drop_stat_indexes);
|
||||
List_iterator<RENAME_INDEX_STAT_PARAMS> it_rename_index(rename_stat_indexes);
|
||||
|
||||
while (Field *field= it_drop_field++)
|
||||
delete_statistics_for_column(thd, table, field);
|
||||
|
||||
if (!rename_stat_fields.is_empty())
|
||||
(void) rename_columns_in_stat_table(thd, table, &rename_stat_fields);
|
||||
|
||||
while (DROP_INDEX_STAT_PARAMS *key= it_drop_index++)
|
||||
(void) delete_statistics_for_index(thd, table, key->key,
|
||||
key->ext_prefixes_only);
|
||||
|
||||
if (!rename_stat_indexes.is_empty())
|
||||
(void) rename_indexes_in_stat_table(thd, table, &rename_stat_indexes);
|
||||
}
|
||||
|
||||
|
||||
Alter_table_ctx::Alter_table_ctx()
|
||||
: db(null_clex_str), table_name(null_clex_str), alias(null_clex_str),
|
||||
new_db(null_clex_str), new_name(null_clex_str), new_alias(null_clex_str)
|
||||
|
@ -105,10 +105,88 @@ public:
|
||||
ulong partition_flags;
|
||||
// Enable or disable keys.
|
||||
enum_enable_or_disable keys_onoff;
|
||||
// Used only in add_stat_drop_index()
|
||||
TABLE *original_table;
|
||||
// List of partitions.
|
||||
List<const char> partition_names;
|
||||
// Number of partitions.
|
||||
uint num_parts;
|
||||
|
||||
/* List of fields that we should delete statistics from */
|
||||
List<Field> drop_stat_fields;
|
||||
|
||||
struct DROP_INDEX_STAT_PARAMS
|
||||
{
|
||||
KEY *key;
|
||||
bool ext_prefixes_only;
|
||||
};
|
||||
|
||||
struct RENAME_COLUMN_STAT_PARAMS
|
||||
{
|
||||
Field *field;
|
||||
LEX_CSTRING *name;
|
||||
uint duplicate_counter; // For temporary names
|
||||
};
|
||||
struct RENAME_INDEX_STAT_PARAMS
|
||||
{
|
||||
const KEY *key;
|
||||
const LEX_CSTRING *name;
|
||||
uint duplicate_counter; // For temporary names
|
||||
uint usage_count; // How many rename entries
|
||||
};
|
||||
|
||||
/* List of index that we should delete statistics from */
|
||||
List<DROP_INDEX_STAT_PARAMS> drop_stat_indexes;
|
||||
|
||||
List<RENAME_COLUMN_STAT_PARAMS> rename_stat_fields;
|
||||
|
||||
List<RENAME_INDEX_STAT_PARAMS> rename_stat_indexes;
|
||||
|
||||
bool add_stat_drop_index(KEY *key, bool ext_prefixes_only,
|
||||
MEM_ROOT *mem_root)
|
||||
{
|
||||
DROP_INDEX_STAT_PARAMS *param;
|
||||
if (!(param= (DROP_INDEX_STAT_PARAMS*)
|
||||
alloc_root(mem_root, sizeof(*param))))
|
||||
return true;
|
||||
param->key= key;
|
||||
param->ext_prefixes_only= ext_prefixes_only;
|
||||
return drop_stat_indexes.push_back(param, mem_root);
|
||||
}
|
||||
|
||||
bool add_stat_drop_index(THD *thd, const LEX_CSTRING *key_name);
|
||||
|
||||
bool add_stat_rename_index(const KEY *key, const LEX_CSTRING *name,
|
||||
MEM_ROOT *mem_root)
|
||||
{
|
||||
RENAME_INDEX_STAT_PARAMS *param;
|
||||
if (!(param= (RENAME_INDEX_STAT_PARAMS*)
|
||||
alloc_root(mem_root, sizeof(*param))))
|
||||
return true;
|
||||
param->key= key;
|
||||
param->name= name;
|
||||
param->usage_count= 0;
|
||||
return rename_stat_indexes.push_back(param, mem_root);
|
||||
}
|
||||
|
||||
bool add_stat_rename_field(Field *field, LEX_CSTRING *name,
|
||||
MEM_ROOT *mem_root)
|
||||
{
|
||||
RENAME_COLUMN_STAT_PARAMS *param;
|
||||
if (!(param= (RENAME_COLUMN_STAT_PARAMS*)
|
||||
alloc_root(mem_root, sizeof(*param))))
|
||||
return true;
|
||||
param->field= field;
|
||||
param->name= name;
|
||||
param->duplicate_counter= 0;
|
||||
return rename_stat_fields.push_back(param, mem_root);
|
||||
}
|
||||
|
||||
bool collect_renamed_fields(THD *thd);
|
||||
|
||||
/* Delete/update statistics in EITS tables */
|
||||
void apply_statistics_deletes_renames(THD *thd, TABLE *table);
|
||||
|
||||
private:
|
||||
// Type of ALTER TABLE algorithm.
|
||||
enum_alter_table_algorithm requested_algorithm;
|
||||
@ -135,6 +213,10 @@ public:
|
||||
create_list.empty();
|
||||
alter_index_ignorability_list.empty();
|
||||
check_constraint_list.empty();
|
||||
drop_stat_fields.empty();
|
||||
drop_stat_indexes.empty();
|
||||
rename_stat_fields.empty();
|
||||
rename_stat_indexes.empty();
|
||||
flags= 0;
|
||||
partition_flags= 0;
|
||||
keys_onoff= LEAVE_AS_IS;
|
||||
|
@ -444,7 +444,8 @@ private:
|
||||
|
||||
class Key :public Sql_alloc, public DDL_options {
|
||||
public:
|
||||
enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY};
|
||||
enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY,
|
||||
IGNORE_KEY};
|
||||
enum Keytype type;
|
||||
KEY_CREATE_INFO key_create_info;
|
||||
List<Key_part_spec> columns;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -119,16 +119,21 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables);
|
||||
int read_statistics_for_tables(THD *thd, TABLE_LIST *tables,
|
||||
bool force_reload);
|
||||
int collect_statistics_for_table(THD *thd, TABLE *table);
|
||||
int alloc_statistics_for_table(THD *thd, TABLE *table);
|
||||
int alloc_statistics_for_table(THD *thd, TABLE *table, MY_BITMAP *stat_fields);
|
||||
int update_statistics_for_table(THD *thd, TABLE *table);
|
||||
int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab);
|
||||
int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db,
|
||||
const LEX_CSTRING *tab);
|
||||
int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col);
|
||||
int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
|
||||
bool ext_prefixes_only);
|
||||
int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab,
|
||||
const LEX_CSTRING *new_db, const LEX_CSTRING *new_tab);
|
||||
int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
|
||||
const char *new_name);
|
||||
int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db,
|
||||
const LEX_CSTRING *tab,
|
||||
const LEX_CSTRING *new_db,
|
||||
const LEX_CSTRING *new_tab);
|
||||
int rename_columns_in_stat_table(THD *thd, TABLE *tab,
|
||||
List<Alter_info::RENAME_COLUMN_STAT_PARAMS> *fields);
|
||||
int rename_indexes_in_stat_table(THD *thd, TABLE *tab,
|
||||
List<Alter_info::RENAME_INDEX_STAT_PARAMS> *indexes);
|
||||
void set_statistics_for_table(THD *thd, TABLE *table);
|
||||
|
||||
double get_column_avg_frequency(Field * field);
|
||||
|
120
sql/sql_table.cc
120
sql/sql_table.cc
@ -2803,8 +2803,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
bool primary_key=0,unique_key=0;
|
||||
Key *key, *key2;
|
||||
uint tmp, key_number;
|
||||
/* special marker for keys to be ignored */
|
||||
static char ignore_key[1];
|
||||
|
||||
/* Calculate number of key segements */
|
||||
*key_count= 0;
|
||||
@ -2852,17 +2850,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
both, is 'generated', and a generated key is a prefix of the other
|
||||
key. Then we do not need the generated shorter key.
|
||||
*/
|
||||
if (key2->type != Key::FOREIGN_KEY && key2->name.str != ignore_key &&
|
||||
if (key2->type != Key::FOREIGN_KEY && key2->type != Key::IGNORE_KEY &&
|
||||
is_foreign_key_prefix(key, key2))
|
||||
{
|
||||
/* mark that the generated key should be ignored */
|
||||
if (!key2->generated ||
|
||||
(key->generated && key->columns.elements <
|
||||
key2->columns.elements))
|
||||
key->name.str= ignore_key;
|
||||
key->type= Key::IGNORE_KEY;
|
||||
else
|
||||
{
|
||||
key2->name.str= ignore_key;
|
||||
key2->type= Key::IGNORE_KEY;
|
||||
key_parts-= key2->columns.elements;
|
||||
(*key_count)--;
|
||||
}
|
||||
@ -2870,7 +2868,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key->name.str != ignore_key)
|
||||
if (key->type != Key::IGNORE_KEY)
|
||||
key_parts+=key->columns.elements;
|
||||
else
|
||||
(*key_count)--;
|
||||
@ -2900,7 +2898,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
key_iterator.rewind();
|
||||
while ((key=key_iterator++))
|
||||
{
|
||||
if (key->name.str == ignore_key || key->type == Key::FOREIGN_KEY)
|
||||
if (key->type == Key::IGNORE_KEY)
|
||||
{
|
||||
/* The key was replaced by another key */
|
||||
if (alter_info->add_stat_drop_index(thd, &key->name))
|
||||
DBUG_RETURN(true);
|
||||
continue;
|
||||
}
|
||||
if (key->type == Key::FOREIGN_KEY)
|
||||
continue;
|
||||
/* Create the key name based on the first column (if not given) */
|
||||
if (key->type == Key::PRIMARY)
|
||||
@ -2962,12 +2967,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
Key_part_spec *column;
|
||||
|
||||
is_hash_field_needed= false;
|
||||
if (key->name.str == ignore_key)
|
||||
if (key->type == Key::IGNORE_KEY)
|
||||
{
|
||||
/* ignore redundant keys */
|
||||
do
|
||||
key=key_iterator++;
|
||||
while (key && key->name.str == ignore_key);
|
||||
while (key && key->type == Key::IGNORE_KEY);
|
||||
if (!key)
|
||||
break;
|
||||
}
|
||||
@ -2995,6 +3000,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
case Key::FOREIGN_KEY:
|
||||
key_number--; // Skip this key
|
||||
continue;
|
||||
case Key::IGNORE_KEY:
|
||||
DBUG_ASSERT(0);
|
||||
break;
|
||||
default:
|
||||
key_info->flags = HA_NOSAME;
|
||||
break;
|
||||
@ -3198,6 +3206,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
key_add_part_check_null(file, key_info, sql_field, column))
|
||||
DBUG_RETURN(TRUE);
|
||||
break;
|
||||
case Key::IGNORE_KEY:
|
||||
break;
|
||||
}
|
||||
|
||||
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
|
||||
@ -6445,7 +6455,9 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
|
||||
|
||||
if (table->s->tmp_table == NO_TMP_TABLE)
|
||||
{
|
||||
delete_statistics_for_column(thd, table, field);
|
||||
if (alter_info->drop_stat_fields.push_back(field, thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
|
||||
KEY *key_info= table->key_info;
|
||||
for (uint i= 0; i < table->s->keys; i++, key_info++)
|
||||
{
|
||||
@ -6457,9 +6469,10 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
|
||||
{
|
||||
if (key_info->key_part[j].fieldnr - 1 == field->field_index)
|
||||
{
|
||||
delete_statistics_for_index(
|
||||
thd, table, key_info,
|
||||
j >= key_info->user_defined_key_parts);
|
||||
if (alter_info->add_stat_drop_index(key_info,
|
||||
j >= key_info->user_defined_key_parts,
|
||||
thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -6516,13 +6529,17 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
|
||||
ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
|
||||
}
|
||||
|
||||
/* Check if field was renamed (case-sensitive for detecting case change) */
|
||||
/*
|
||||
Check if field was renamed (case-sensitive for detecting case change)
|
||||
*/
|
||||
if (cmp(&field->field_name, &new_field->field_name))
|
||||
{
|
||||
field->flags|= FIELD_IS_RENAMED;
|
||||
ha_alter_info->handler_flags|= ALTER_COLUMN_NAME;
|
||||
rename_column_in_stat_tables(thd, table, field,
|
||||
new_field->field_name.str);
|
||||
if (alter_info->add_stat_rename_field(field,
|
||||
&new_field->field_name,
|
||||
thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
/* Check that NULL behavior is same for old and new fields */
|
||||
@ -7481,6 +7498,8 @@ static bool mysql_inplace_alter_table(THD *thd,
|
||||
in case of crash it should use the new one and log the query
|
||||
to the binary log.
|
||||
*/
|
||||
ha_alter_info->alter_info->apply_statistics_deletes_renames(thd, table);
|
||||
|
||||
ddl_log_update_phase(ddl_log_state, DDL_ALTER_TABLE_PHASE_INPLACE_COPIED);
|
||||
debug_crash_here("ddl_log_alter_after_log");
|
||||
|
||||
@ -7585,6 +7604,7 @@ static bool mysql_inplace_alter_table(THD *thd,
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
maximum possible length for certain blob types.
|
||||
|
||||
@ -7833,7 +7853,10 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
||||
create_info->used_fields|=HA_CREATE_USED_AUTO;
|
||||
}
|
||||
if (table->s->tmp_table == NO_TMP_TABLE)
|
||||
(void) delete_statistics_for_column(thd, table, field);
|
||||
{
|
||||
if (alter_info->drop_stat_fields.push_back(field, thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
dropped_sys_vers_fields|= field->flags;
|
||||
drop_it.remove();
|
||||
dropped_fields= &table->tmp_set;
|
||||
@ -7845,13 +7868,19 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
||||
{
|
||||
vers_system_invisible= true;
|
||||
}
|
||||
/* invisible versioning column is dropped automatically on DROP SYSTEM VERSIONING */
|
||||
/*
|
||||
invisible versioning column is dropped automatically on
|
||||
DROP SYSTEM VERSIONING
|
||||
*/
|
||||
if (!drop && field->invisible >= INVISIBLE_SYSTEM &&
|
||||
field->flags & VERS_SYSTEM_FIELD &&
|
||||
alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING)
|
||||
{
|
||||
if (table->s->tmp_table == NO_TMP_TABLE)
|
||||
(void) delete_statistics_for_column(thd, table, field);
|
||||
{
|
||||
if (alter_info->drop_stat_fields.push_back(field, thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -8214,19 +8243,24 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
||||
{
|
||||
if (table->s->tmp_table == NO_TMP_TABLE)
|
||||
{
|
||||
(void) delete_statistics_for_index(thd, table, key_info, FALSE);
|
||||
if (alter_info->add_stat_drop_index(key_info, FALSE, thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
if (primary_key)
|
||||
{
|
||||
KEY *tab_key_info= table->key_info;
|
||||
for (uint j=0; j < table->s->keys; j++, tab_key_info++)
|
||||
{
|
||||
if (tab_key_info->user_defined_key_parts !=
|
||||
if (tab_key_info != key_info &&
|
||||
tab_key_info->user_defined_key_parts !=
|
||||
tab_key_info->ext_key_parts)
|
||||
(void) delete_statistics_for_index(thd, table, tab_key_info,
|
||||
TRUE);
|
||||
}
|
||||
{
|
||||
if (alter_info->add_stat_drop_index(tab_key_info, TRUE,
|
||||
thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drop_it.remove();
|
||||
continue;
|
||||
}
|
||||
@ -8263,8 +8297,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
||||
goto err;
|
||||
}
|
||||
|
||||
key_name= rename_key->new_name.str;
|
||||
key_name= rename_key->new_name.str; // New name of current key_info
|
||||
rename_key_it.remove();
|
||||
alter_info->add_stat_rename_index(key_info, &rename_key->new_name,
|
||||
thd->mem_root);
|
||||
|
||||
/*
|
||||
If the user has explicitly renamed the key, we should no longer
|
||||
treat it as generated. Otherwise this key might be automatically
|
||||
@ -8374,10 +8411,17 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
||||
if (table->s->tmp_table == NO_TMP_TABLE)
|
||||
{
|
||||
if (delete_index_stat)
|
||||
(void) delete_statistics_for_index(thd, table, key_info, FALSE);
|
||||
{
|
||||
if (alter_info->add_stat_drop_index(key_info, FALSE, thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
else if (alter_ctx->modified_primary_key &&
|
||||
key_info->user_defined_key_parts != key_info->ext_key_parts)
|
||||
(void) delete_statistics_for_index(thd, table, key_info, TRUE);
|
||||
{
|
||||
if (alter_info->add_stat_drop_index(key_info, FALSE,
|
||||
thd->mem_root))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!user_keyparts && key_parts.elements)
|
||||
@ -10196,6 +10240,13 @@ do_continue:;
|
||||
alter_info->flags|= ALTER_INDEX_ORDER;
|
||||
create_info->alias= alter_ctx.table_name;
|
||||
thd->abort_on_warning= !ignore && thd->is_strict_mode();
|
||||
|
||||
/*
|
||||
This is to be able to call Alter_info::add_stat_drop_index(thd, key_name)
|
||||
from mysql_prepare_create_table()
|
||||
*/
|
||||
alter_info->original_table= table;
|
||||
|
||||
/*
|
||||
Create the .frm file for the new table. Storage engine table will not be
|
||||
created at this stage.
|
||||
@ -10466,6 +10517,16 @@ do_continue:;
|
||||
thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
|
||||
thd->cuted_fields=0L;
|
||||
|
||||
/*
|
||||
Collect fields that was renamed.
|
||||
We do not do that if fill_alter_inplace_info() has
|
||||
already collected renamed fields.
|
||||
*/
|
||||
if (alter_info->flags & (ALTER_CHANGE_COLUMN | ALTER_RENAME_COLUMN) &&
|
||||
alter_info->rename_stat_fields.is_empty())
|
||||
if (alter_info->collect_renamed_fields(thd))
|
||||
goto err_new_table_cleanup;
|
||||
|
||||
/*
|
||||
We do not copy data for MERGE tables. Only the children have data.
|
||||
MERGE tables have HA_NO_COPY_ON_ALTER set.
|
||||
@ -10662,6 +10723,9 @@ do_continue:;
|
||||
if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
|
||||
goto err_new_table_cleanup;
|
||||
|
||||
/* Now we are the only user. Update the data in EITS tables */
|
||||
alter_info->apply_statistics_deletes_renames(thd, table);
|
||||
|
||||
close_all_tables_for_name(thd, table->s,
|
||||
alter_ctx.is_table_renamed() ?
|
||||
HA_EXTRA_PREPARE_FOR_RENAME:
|
||||
|
@ -95,11 +95,11 @@ class engine_option_value;
|
||||
struct ha_index_option_struct;
|
||||
|
||||
typedef struct st_key {
|
||||
uint key_length; /* total length of user defined key parts */
|
||||
ulong flags; /* dupp key and pack flags */
|
||||
uint user_defined_key_parts; /* How many key_parts */
|
||||
uint usable_key_parts; /* Should normally be = user_defined_key_parts */
|
||||
uint ext_key_parts; /* Number of key parts in extended key */
|
||||
uint key_length; /* total length of user defined key parts */
|
||||
ulong flags; /* dupp key and pack flags */
|
||||
uint user_defined_key_parts; /* How many key_parts */
|
||||
uint usable_key_parts; /* Should normally be = user_defined_key_parts */
|
||||
uint ext_key_parts; /* Number of key parts in extended key */
|
||||
ulong ext_key_flags; /* Flags for extended key */
|
||||
/*
|
||||
Parts of primary key that are in the extension of this index.
|
||||
|
Loading…
x
Reference in New Issue
Block a user