diff --git a/mysql-test/suite/versioning/r/delete_history.result b/mysql-test/suite/versioning/r/delete_history.result index cb865a835b3..b3086c867d1 100644 --- a/mysql-test/suite/versioning/r/delete_history.result +++ b/mysql-test/suite/versioning/r/delete_history.result @@ -154,3 +154,15 @@ select * from t1; a 1 drop table t1; +# +# MDEV-17554 Auto-create new partition for system versioned tables with history partitioned by INTERVAL/LIMIT +# +# Don't auto-create new partition on DELETE HISTORY: +create or replace table t (a int) +with system versioning +partition by system_time limit 1000; +delete history from t; +select count(*) - 1 as hist_partitions from information_schema.partitions where table_name = 't' and table_schema = 'test'; +hist_partitions +1 +drop table t; diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 038051055e7..87d7c112f3e 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -1,4 +1,7 @@ call mtr.add_suppression("need more HISTORY partitions"); +call mtr.add_suppression("Duplicate partition name"); +call mtr.add_suppression("Wrong partitioning type"); +call mtr.add_suppression("Table 'test.t1' doesn't exist"); set system_versioning_alter_history=keep; # Check conventional partitioning on temporal tables create or replace table t1 ( @@ -237,7 +240,9 @@ select @ts0 = @ts1; select @ts2 = @ts3; @ts2 = @ts3 1 -## rotation by LIMIT +# +# Rotation by LIMIT +# create or replace table t1 (x int) with system versioning partition by system_time limit 0 partitions 3; @@ -289,7 +294,37 @@ x 6 7 8 -## rotation by INTERVAL +# Auto-create history partitions +create or replace table t1 (x int) with system versioning +partition by system_time limit 1000 auto_increment; +lock tables t1 write; +insert into t1 values (1); +update t1 set x= x + 1; +select state, info from information_schema.processlist where info like 'ALTER TABLE %'; +state info +Waiting for table metadata lock ALTER TABLE `test`.`t1` ADD PARTITION (PARTITION `p1` HISTORY) +unlock tables; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME LIMIT 1000 AUTO_INCREMENT +(PARTITION `p0` HISTORY ENGINE = DEFAULT_ENGINE, + PARTITION `p1` HISTORY ENGINE = DEFAULT_ENGINE, + PARTITION `pn` CURRENT ENGINE = DEFAULT_ENGINE) +### Test ALTER failure +set @lock_wait_timeout0= @@global.lock_wait_timeout; +set global lock_wait_timeout= 1; +create or replace table t1 (x int) with system versioning partition by system_time limit 1000; +lock tables t1 write; +insert t1 values (1); +insert t1 values (1); +unlock tables; +set global lock_wait_timeout= @lock_wait_timeout0; +# +# Rotation by INTERVAL +# create or replace table t1 (x int) with system versioning partition by system_time interval 0 second partitions 3; @@ -302,7 +337,7 @@ partition by system_time interval 10 year partitions 3; ERROR 22003: TIMESTAMP value is out of range in 'INTERVAL' # INTERVAL and ALTER TABLE create or replace table t1 (i int) with system versioning -partition by system_time interval 1 hour; +partition by system_time interval 59 minute; set @ts=(select partition_description from information_schema.partitions where table_schema='test' and table_name='t1' and partition_name='p0'); alter table t1 add column b int; @@ -316,16 +351,16 @@ alter table t1 add partition (partition p1 history, partition p2 history); select partition_name,partition_ordinal_position,partition_method,timediff(partition_description, @ts) from information_schema.partitions where table_schema='test' and table_name='t1'; partition_name partition_ordinal_position partition_method timediff(partition_description, @ts) p0 1 SYSTEM_TIME 00:00:00.000000 -p1 2 SYSTEM_TIME 01:00:00.000000 -p2 3 SYSTEM_TIME 02:00:00.000000 +p1 2 SYSTEM_TIME 00:59:00.000000 +p2 3 SYSTEM_TIME 01:58:00.000000 pn 4 SYSTEM_TIME NULL Warnings: Warning 1292 Incorrect time value: 'CURRENT' alter table t1 drop partition p0; select partition_name,partition_ordinal_position,partition_method,timediff(partition_description, @ts) from information_schema.partitions where table_schema='test' and table_name='t1'; partition_name partition_ordinal_position partition_method timediff(partition_description, @ts) -p1 1 SYSTEM_TIME 01:00:00.000000 -p2 2 SYSTEM_TIME 02:00:00.000000 +p1 1 SYSTEM_TIME 00:59:00.000000 +p2 2 SYSTEM_TIME 01:58:00.000000 pn 3 SYSTEM_TIME NULL Warnings: Warning 1292 Incorrect time value: 'CURRENT' @@ -333,8 +368,8 @@ alter table t1 drop partition p2; ERROR HY000: Can only drop oldest partitions when rotating by INTERVAL select partition_name,partition_ordinal_position,partition_method,timediff(partition_description, @ts) from information_schema.partitions where table_schema='test' and table_name='t1'; partition_name partition_ordinal_position partition_method timediff(partition_description, @ts) -p1 1 SYSTEM_TIME 01:00:00.000000 -p2 2 SYSTEM_TIME 02:00:00.000000 +p1 1 SYSTEM_TIME 00:59:00.000000 +p2 2 SYSTEM_TIME 01:58:00.000000 pn 3 SYSTEM_TIME NULL Warnings: Warning 1292 Incorrect time value: 'CURRENT' @@ -343,6 +378,7 @@ create or replace table t1 (i int) with system versioning partition by system_time interval 1 day subpartition by key (i) subpartitions 2 (partition p1 history, partition pn current); +lock tables t1 write; set timestamp=unix_timestamp('2001-02-03 10:20:40'); insert t1 values (1); delete from t1; @@ -353,28 +389,51 @@ Warning 4114 Versioned table `test`.`t1`: last HISTORY partition (`p1`) is out o delete from t1; Warnings: Warning 4114 Versioned table `test`.`t1`: last HISTORY partition (`p1`) is out of INTERVAL, need more HISTORY partitions -select subpartition_name,partition_description,table_rows from information_schema.partitions where table_schema='test' and table_name='t1'; -subpartition_name partition_description table_rows -p1sp0 2001-02-04 00:00:00 1 -p1sp1 2001-02-04 00:00:00 1 -pnsp0 CURRENT 0 -pnsp1 CURRENT 0 +select subpartition_name, partition_description from information_schema.partitions where table_schema='test' and table_name='t1'; +subpartition_name partition_description +p1sp0 2001-02-04 00:00:00 +p1sp1 2001-02-04 00:00:00 +pnsp0 CURRENT +pnsp1 CURRENT +select * from t1 partition (p1); +i +1 +2 set timestamp=unix_timestamp('2001-02-04 10:20:55'); alter table t1 add partition (partition p0 history, partition p2 history); set timestamp=unix_timestamp('2001-02-04 10:30:00'); insert t1 values (4),(5); set timestamp=unix_timestamp('2001-02-04 10:30:10'); update t1 set i=6 where i=5; -select subpartition_name,partition_description,table_rows from information_schema.partitions where table_schema='test' and table_name='t1'; -subpartition_name partition_description table_rows -p1sp0 2001-02-04 00:00:00 1 -p1sp1 2001-02-04 00:00:00 0 -p0sp0 2001-02-05 00:00:00 1 -p0sp1 2001-02-05 00:00:00 1 -p2sp0 2001-02-06 00:00:00 0 -p2sp1 2001-02-06 00:00:00 0 -pnsp0 CURRENT 0 -pnsp1 CURRENT 2 +select subpartition_name, partition_description from information_schema.partitions where table_schema='test' and table_name='t1'; +subpartition_name partition_description +p1sp0 2001-02-04 00:00:00 +p1sp1 2001-02-04 00:00:00 +p0sp0 2001-02-05 00:00:00 +p0sp1 2001-02-05 00:00:00 +p2sp0 2001-02-06 00:00:00 +p2sp1 2001-02-06 00:00:00 +pnsp0 CURRENT +pnsp1 CURRENT +select * from t1 partition (p1); +i +1 +2 +select * from t1 partition (p0); +i +5 +select * from t1 partition (p2); +i +alter table t1 rebuild partition p0, p1, p2; +select * from t1 partition (p1); +i +1 +select * from t1 partition (p0); +i +5 +2 +select * from t1 partition (p2); +i ## pruning check set @ts=(select partition_description from information_schema.partitions where table_schema='test' and table_name='t1' and partition_name='p0' limit 1); @@ -395,6 +454,7 @@ i explain partitions select * from t1 for system_time all where row_end = @ts; id select_type table partitions type possible_keys key key_len ref rows Extra 1 SIMPLE t1 p1_p1sp0,p1_p1sp1 # NULL NULL NULL NULL # # +unlock tables; ## INTERVAL ... STARTS create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts 'a'; @@ -542,6 +602,9 @@ set timestamp= unix_timestamp('2001-01-01 00:00:00'); create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts '2000-01-01 00:00:00' partitions 3; +lock tables t1 write; +Warnings: +Warning 4114 Versioned table `test`.`t1`: last HISTORY partition (`p1`) is out of INTERVAL, need more HISTORY partitions # we are warned when we push to present: insert into t1 values (0); Warnings: @@ -584,11 +647,13 @@ i row_end select *, row_end from t1 partition (p1); i row_end 3 2000-01-04 00:00:01.000000 -set timestamp= unix_timestamp('2000-01-01 00:00:00'); +unlock tables; # and this is how it usually goes: +set timestamp= unix_timestamp('2000-01-01 00:00:00'); create or replace table t1 (i int) with system versioning partition by system_time interval 1 day partitions 3; +lock tables t1 write; insert into t1 values (0); set timestamp= unix_timestamp('2000-01-01 00:00:01'); update t1 set i= i + 1; @@ -609,12 +674,26 @@ i row_end select *, row_end from t1 partition (p1); i row_end 1 2000-01-02 00:00:01.000000 +2 2000-01-03 00:00:01.000000 +3 2000-01-04 00:00:01.000000 +select *, row_end from t1 partition (p2); +i row_end +select *, row_end from t1 partition (p3); +i row_end +alter table t1 rebuild partition p0, p1, p2, p3; +select *, row_end from t1 partition (p0); +i row_end +0 2000-01-01 00:00:01.000000 +select *, row_end from t1 partition (p1); +i row_end +1 2000-01-02 00:00:01.000000 select *, row_end from t1 partition (p2); i row_end 2 2000-01-03 00:00:01.000000 select *, row_end from t1 partition (p3); i row_end 3 2000-01-04 00:00:01.000000 +unlock tables; drop tables t1, t2; ## Subpartitions create or replace table t1 (x int) @@ -632,9 +711,9 @@ select * from t1 partition (pnsp1); x 2 4 -### warn about full partition delete from t1 where x < 3; delete from t1; +### warn about full partition delete from t1; Warnings: Warning 4114 Versioned table `test`.`t1`: last HISTORY partition (`p1`) is out of LIMIT, need more HISTORY partitions @@ -845,7 +924,7 @@ ERROR HY000: Can not DROP SYSTEM VERSIONING for table `t` partitioned BY SYSTEM_ create or replace table t1 (i int) with system versioning; insert into t1 values (1), (2); update t1 set i= 3; -alter table t1 partition by system_time interval 1 month (partition p1 history, partition pn current); +alter table t1 partition by system_time interval 59 minute (partition p1 history, partition pn current); lock table t1 write; alter table t1 add partition (partition p2 history); insert into t1 values (4); diff --git a/mysql-test/suite/versioning/r/rpl.result b/mysql-test/suite/versioning/r/rpl.result index 627f3991499..a303e8f8e61 100644 --- a/mysql-test/suite/versioning/r/rpl.result +++ b/mysql-test/suite/versioning/r/rpl.result @@ -164,4 +164,23 @@ update t1 set i = 0; connection slave; connection master; drop table t1; +# +# MDEV-17554 Auto-create new partition for system versioned tables with history partitioned by INTERVAL/LIMIT +# +create or replace table t1 (x int) with system versioning +partition by system_time limit 1000 auto_increment; +lock tables t1 write; +insert t1 values (); +delete from t1; +unlock tables; +select count(*) - 1 as hist_partitions from information_schema.partitions where table_name = 't1' and table_schema = 'test'; +hist_partitions +2 +connection slave; +select count(*) - 1 as hist_partitions from information_schema.partitions where table_name = 't1' and table_schema = 'test'; +hist_partitions +2 +connection master; +drop database test; +create database test; include/rpl_end.inc diff --git a/mysql-test/suite/versioning/t/delete_history.test b/mysql-test/suite/versioning/t/delete_history.test index fb5c8520bcb..be9f6afaec8 100644 --- a/mysql-test/suite/versioning/t/delete_history.test +++ b/mysql-test/suite/versioning/t/delete_history.test @@ -169,4 +169,15 @@ insert into t1 values (1); select * from t1; drop table t1; +--echo # +--echo # MDEV-17554 Auto-create new partition for system versioned tables with history partitioned by INTERVAL/LIMIT +--echo # +--echo # Don't auto-create new partition on DELETE HISTORY: +create or replace table t (a int) +with system versioning +partition by system_time limit 1000; +delete history from t; +select count(*) - 1 as hist_partitions from information_schema.partitions where table_name = 't' and table_schema = 'test'; +drop table t; + --source suite/versioning/common_finish.inc diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index f55b43f56da..38755b3daec 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -3,6 +3,11 @@ -- source suite/versioning/engines.inc call mtr.add_suppression("need more HISTORY partitions"); +call mtr.add_suppression("Duplicate partition name"); +call mtr.add_suppression("Wrong partitioning type"); +call mtr.add_suppression("Table 'test.t1' doesn't exist"); +let $wait_no_alter= select count(*) = 0 from information_schema.processlist where info like 'ALTER TABLE %'; +let $wait_alter= select count(*) > 0 from information_schema.processlist where info like 'ALTER TABLE %'; set system_versioning_alter_history=keep; --echo # Check conventional partitioning on temporal tables @@ -213,7 +218,9 @@ prepare stmt from @str; execute stmt; drop prepare stmt; select @ts0 = @ts1; select @ts2 = @ts3; ---echo ## rotation by LIMIT +--echo # +--echo # Rotation by LIMIT +--echo # --error ER_PART_WRONG_VALUE create or replace table t1 (x int) with system versioning @@ -241,8 +248,37 @@ insert into t1 values (7), (8); delete from t1; select * from t1 partition (p1) order by x; +--echo # Auto-create history partitions +create or replace table t1 (x int) with system versioning +partition by system_time limit 1000 auto_increment; +lock tables t1 write; +insert into t1 values (1); +update t1 set x= x + 1; +--let $wait_condition= $wait_alter +--source include/wait_condition.inc +select state, info from information_schema.processlist where info like 'ALTER TABLE %'; +unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc +--replace_result $default_engine DEFAULT_ENGINE +show create table t1; ---echo ## rotation by INTERVAL +--echo ### Test ALTER failure +set @lock_wait_timeout0= @@global.lock_wait_timeout; +set global lock_wait_timeout= 1; +create or replace table t1 (x int) with system versioning partition by system_time limit 1000; +lock tables t1 write; +insert t1 values (1); +--real_sleep 1 +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc +insert t1 values (1); +unlock tables; +set global lock_wait_timeout= @lock_wait_timeout0; + +--echo # +--echo # Rotation by INTERVAL +--echo # --error ER_PART_WRONG_VALUE create or replace table t1 (x int) with system versioning @@ -258,7 +294,7 @@ partition by system_time interval 10 year partitions 3; --echo # INTERVAL and ALTER TABLE create or replace table t1 (i int) with system versioning -partition by system_time interval 1 hour; +partition by system_time interval 59 minute; set @ts=(select partition_description from information_schema.partitions where table_schema='test' and table_name='t1' and partition_name='p0'); @@ -281,12 +317,15 @@ create or replace table t1 (i int) with system versioning partition by system_time interval 1 day subpartition by key (i) subpartitions 2 (partition p1 history, partition pn current); +lock tables t1 write; # prevent partition auto-creation + set timestamp=unix_timestamp('2001-02-03 10:20:40'); insert t1 values (1); delete from t1; set timestamp=unix_timestamp('2001-02-04 10:20:50'); insert t1 values (2); delete from t1; -select subpartition_name,partition_description,table_rows from information_schema.partitions where table_schema='test' and table_name='t1'; +select subpartition_name, partition_description from information_schema.partitions where table_schema='test' and table_name='t1'; +select * from t1 partition (p1); set timestamp=unix_timestamp('2001-02-04 10:20:55'); alter table t1 add partition (partition p0 history, partition p2 history); @@ -295,7 +334,15 @@ insert t1 values (4),(5); set timestamp=unix_timestamp('2001-02-04 10:30:10'); update t1 set i=6 where i=5; -select subpartition_name,partition_description,table_rows from information_schema.partitions where table_schema='test' and table_name='t1'; +select subpartition_name, partition_description from information_schema.partitions where table_schema='test' and table_name='t1'; +select * from t1 partition (p1); +select * from t1 partition (p0); +select * from t1 partition (p2); + +alter table t1 rebuild partition p0, p1, p2; +select * from t1 partition (p1); +select * from t1 partition (p0); +select * from t1 partition (p2); --echo ## pruning check set @ts=(select partition_description from information_schema.partitions @@ -310,6 +357,9 @@ set @ts=(select row_end from t1 for system_time all where i=1); select * from t1 for system_time all where row_end = @ts; --replace_column 5 # 10 # 11 # explain partitions select * from t1 for system_time all where row_end = @ts; +unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc --echo ## INTERVAL ... STARTS --error ER_PART_WRONG_VALUE @@ -406,6 +456,7 @@ set timestamp= unix_timestamp('2001-01-01 00:00:00'); create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts '2000-01-01 00:00:00' partitions 3; +lock tables t1 write; # prevent partition auto-creation --echo # we are warned when we push to present: insert into t1 values (0); @@ -436,11 +487,16 @@ update t1 set i= i + 1; select *, row_end from t1 partition (p0); select *, row_end from t1 partition (p1); -set timestamp= unix_timestamp('2000-01-01 00:00:00'); +unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc + --echo # and this is how it usually goes: +set timestamp= unix_timestamp('2000-01-01 00:00:00'); create or replace table t1 (i int) with system versioning partition by system_time interval 1 day partitions 3; +lock tables t1 write; # prevent partition auto-creation insert into t1 values (0); set timestamp= unix_timestamp('2000-01-01 00:00:01'); @@ -453,12 +509,20 @@ set timestamp= unix_timestamp('2000-01-04 00:00:01'); update t1 set i= i + 1; alter table t1 add partition (partition p2 history, partition p3 history); - select *, row_end from t1 partition (p0); select *, row_end from t1 partition (p1); select *, row_end from t1 partition (p2); select *, row_end from t1 partition (p3); +alter table t1 rebuild partition p0, p1, p2, p3; +select *, row_end from t1 partition (p0); +select *, row_end from t1 partition (p1); +select *, row_end from t1 partition (p2); +select *, row_end from t1 partition (p3); + +unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc drop tables t1, t2; --echo ## Subpartitions @@ -472,9 +536,9 @@ insert into t1 (x) values (1), (2), (3), (4), (5); select * from t1 partition (pnsp0); select * from t1 partition (pnsp1); ---echo ### warn about full partition delete from t1 where x < 3; delete from t1; +--echo ### warn about full partition delete from t1; select * from t1 partition (p0sp0); select * from t1 partition (p0sp1); @@ -627,6 +691,8 @@ alter table t1 partition by system_time interval 1 week ( partition p1 history, partition pn current); unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc --echo # --echo # MDEV-14748 Assertion in ha_myisammrg::attach_children() @@ -663,6 +729,8 @@ lock table t1 write; alter table t1 add partition (partition p0 history); insert into t1 values (1); unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc --echo # --echo # MDEV-15103 Assertion in ha_partition::part_records() for updating VIEW @@ -687,11 +755,13 @@ alter table t drop system versioning; create or replace table t1 (i int) with system versioning; insert into t1 values (1), (2); update t1 set i= 3; -alter table t1 partition by system_time interval 1 month (partition p1 history, partition pn current); +alter table t1 partition by system_time interval 59 minute (partition p1 history, partition pn current); lock table t1 write; alter table t1 add partition (partition p2 history); insert into t1 values (4); unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc --echo # --echo # MDEV-15036 Assertion `!is_set() || (m_status == DA_OK_BULK && is_bulk_op())' in Diagnostics_area::set_ok_status or unexpected ER_RANGE_NOT_INCREASING_ERROR diff --git a/mysql-test/suite/versioning/t/rpl.test b/mysql-test/suite/versioning/t/rpl.test index b5be68feece..ec612551578 100644 --- a/mysql-test/suite/versioning/t/rpl.test +++ b/mysql-test/suite/versioning/t/rpl.test @@ -133,4 +133,28 @@ sync_slave_with_master; connection master; drop table t1; +--echo # +--echo # MDEV-17554 Auto-create new partition for system versioned tables with history partitioned by INTERVAL/LIMIT +--echo # +let $wait_no_alter= select count(*) = 0 from information_schema.processlist where info like 'ALTER TABLE %'; +let $wait_alter= select count(*) > 0 from information_schema.processlist where info like 'ALTER TABLE %'; + +create or replace table t1 (x int) with system versioning +partition by system_time limit 1000 auto_increment; +lock tables t1 write; +insert t1 values (); +delete from t1; +--let $wait_condition= $wait_alter +--source include/wait_condition.inc +unlock tables; +--let $wait_condition= $wait_no_alter +--source include/wait_condition.inc +select count(*) - 1 as hist_partitions from information_schema.partitions where table_name = 't1' and table_schema = 'test'; +--sync_slave_with_master +select count(*) - 1 as hist_partitions from information_schema.partitions where table_name = 't1' and table_schema = 'test'; +--connection master + +drop database test; +create database test; + --source include/rpl_end.inc diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b2f8afca7a6..7fdbb300298 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1090,7 +1090,7 @@ static PSI_cond_info all_server_conds[]= PSI_thread_key key_thread_delayed_insert, key_thread_handle_manager, key_thread_main, key_thread_one_connection, key_thread_signal_hand, - key_thread_slave_background, key_rpl_parallel_thread; + key_thread_slave_background, key_rpl_parallel_thread, key_thread_query; PSI_thread_key key_thread_ack_receiver; static PSI_thread_info all_server_threads[]= @@ -1102,7 +1102,8 @@ static PSI_thread_info all_server_threads[]= { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL}, { &key_thread_slave_background, "slave_background", PSI_FLAG_GLOBAL}, { &key_thread_ack_receiver, "Ack_receiver", PSI_FLAG_GLOBAL}, - { &key_rpl_parallel_thread, "rpl_parallel_thread", 0} + { &key_rpl_parallel_thread, "rpl_parallel_thread", 0}, + { &key_thread_query, "vers_add_hist_part_thread", 0} }; #ifdef HAVE_MMAP diff --git a/sql/mysqld.h b/sql/mysqld.h index 4f4e608e811..d1e13c2fc77 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -388,7 +388,7 @@ extern PSI_cond_key key_TABLE_SHARE_COND_rotation; extern PSI_thread_key key_thread_delayed_insert, key_thread_handle_manager, key_thread_kill_server, key_thread_main, key_thread_one_connection, key_thread_signal_hand, - key_thread_slave_background, key_rpl_parallel_thread; + key_thread_slave_background, key_rpl_parallel_thread, key_thread_query; extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest, key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file, diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 252744a3a73..e0ffaaecd36 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -322,13 +322,11 @@ bool partition_info::set_partition_bitmaps_from_table(TABLE_LIST *table_list) The external routine needing this code is check_partition_info */ -#define MAX_PART_NAME_SIZE 8 - char *partition_info::create_default_partition_names(THD *thd, uint part_no, uint num_parts_arg, uint start_no) { - char *ptr= (char*) thd->calloc(num_parts_arg * MAX_PART_NAME_SIZE); + char *ptr= (char*) thd->calloc(num_parts_arg * MAX_PART_NAME_SIZE + 1); char *move_ptr= ptr; uint i= 0; DBUG_ENTER("create_default_partition_names"); @@ -337,7 +335,8 @@ char *partition_info::create_default_partition_names(THD *thd, uint part_no, { do { - sprintf(move_ptr, "p%u", (start_no + i)); + if (make_partition_name(move_ptr, (start_no + i))) + DBUG_RETURN(NULL); move_ptr+= MAX_PART_NAME_SIZE; } while (++i < num_parts_arg); } @@ -808,6 +807,13 @@ bool partition_info::has_unique_name(partition_element *element) } +/* Auto-create history partition configuration */ +static const uint VERS_MIN_EMPTY= 1; +static const uint VERS_MIN_INTERVAL= 3600; // seconds +static const uint VERS_MIN_LIMIT= 1000; +static const uint VERS_ERROR_TIMEOUT= 300; // seconds + + /** @brief Switch history partition according limit or interval @@ -845,29 +851,265 @@ void partition_info::vers_set_hist_part(THD *thd) else vers_info->hist_part= next; } + if (vers_info->limit >= VERS_MIN_LIMIT) + goto add_hist_part; return; } if (vers_info->interval.is_set()) { - if (vers_info->hist_part->range_value > thd->query_start()) - return; - - partition_element *next= NULL; - List_iterator it(partitions); - while (next != vers_info->hist_part) - next= it++; - - while ((next= it++) != vers_info->now_part) + if (vers_info->hist_part->range_value <= thd->query_start()) { - vers_info->hist_part= next; - if (next->range_value > thd->query_start()) - return; + partition_element *next= NULL; + bool error= true; + List_iterator it(partitions); + while (next != vers_info->hist_part) + next= it++; + + while ((next= it++) != vers_info->now_part) + { + vers_info->hist_part= next; + if (next->range_value > thd->query_start()) + { + error= false; + break; + } + } + if (error) + my_error(WARN_VERS_PART_FULL, MYF(ME_WARNING|ME_ERROR_LOG), + table->s->db.str, table->s->table_name.str, + vers_info->hist_part->partition_name, "INTERVAL"); } - my_error(WARN_VERS_PART_FULL, MYF(ME_WARNING|ME_ERROR_LOG), - table->s->db.str, table->s->table_name.str, - vers_info->hist_part->partition_name, "INTERVAL"); + if (vers_info->interval.ge(VERS_MIN_INTERVAL)) + goto add_hist_part; } + + return; + +add_hist_part: + if (!vers_info->auto_inc) + return; + switch (thd->lex->sql_command) + { + case SQLCOM_DELETE: + if (thd->lex->last_table()->vers_conditions.type == SYSTEM_TIME_HISTORY) + break; + case SQLCOM_UPDATE: + case SQLCOM_INSERT: + case SQLCOM_INSERT_SELECT: + case SQLCOM_LOAD: + case SQLCOM_REPLACE: + case SQLCOM_REPLACE_SELECT: + case SQLCOM_DELETE_MULTI: + case SQLCOM_UPDATE_MULTI: + { + time_t &timeout= table->s->vers_hist_part_timeout; + if (!thd->slave_thread && + vers_info->hist_part->id + VERS_MIN_EMPTY == vers_info->now_part->id) + { + if (!timeout || timeout < thd->query_start()) + vers_add_hist_part(thd); + else if (table->s->vers_hist_part_error) + { + my_error(WARN_VERS_HIST_PART_ERROR, MYF(ME_WARNING), + table->s->db.str, table->s->table_name.str, + table->s->vers_hist_part_error); + } + } + } + default:; + } +} + + +struct vers_add_hist_part_data +{ + LEX_STRING query; + LEX_CSTRING db; + LEX_CSTRING table_name; + LEX_STRING part_name; + my_time_t start_time; + ulong start_time_sec_part; + + TABLE_SHARE *s; + + void assign(THD *thd, String &q, TABLE *table, LEX_CSTRING p) + { + start_time= thd->start_time; + start_time_sec_part= thd->start_time_sec_part; + s= table->s; + + memcpy(query.str, q.c_ptr_quick(), q.length()); + query.str[q.length()]= 0; + query.length= q.length(); + + db.length= table->s->db.length; + memcpy((char *)db.str, table->s->db.str, db.length); + ((char *)db.str)[db.length]= 0; + + table_name.length= table->s->table_name.length; + memcpy((char *)table_name.str, table->s->table_name.str, table_name.length); + ((char *)table_name.str)[table_name.length]= 0; + + memcpy(part_name.str, p.str, p.length + 1); + part_name.length= p.length; + } +}; + +pthread_handler_t vers_add_hist_part_thread(void *arg) +{ + Parser_state parser_state; + uint error; + DBUG_ASSERT(arg); + vers_add_hist_part_data &d= *(vers_add_hist_part_data *) arg; + sql_print_information("Adding history partition `%s` for table `%s`.`%s`", + d.part_name.str, + d.db.str, + d.table_name.str); + my_thread_init(); + /* Initialize THD */ + THD *thd= new THD(next_thread_id()); + if (unlikely(!thd)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(ME_ERROR_LOG)); + goto err1; + } + thd->thread_stack= (char*) &thd; + thd->store_globals(); + thd->set_command(COM_DAEMON); + thd->system_thread= SYSTEM_THREAD_GENERIC; + thd->security_ctx->host_or_ip= ""; + thd->security_ctx->master_access= ALTER_ACL; + thd->log_all_errors= true; + thd->start_time= d.start_time; + thd->start_time_sec_part= d.start_time_sec_part; + server_threads.insert(thd); + thd_proc_info(thd, "Add history partition"); + /* Initialize parser */ + lex_start(thd); + if (unlikely(parser_state.init(thd, d.query.str, d.query.length))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(ME_ERROR_LOG)); + lex_end(thd->lex); + goto err2; + } + if (unlikely(parse_sql(thd, &parser_state, NULL))) + { + lex_end(thd->lex); + goto err2; + } + thd->set_query_and_id(d.query.str, (uint32)d.query.length, thd->charset(), next_query_id()); + MYSQL_QUERY_EXEC_START(thd->query(), thd->thread_id, "", "", "", 0); + error= (uint) mysql_execute_command(thd); + MYSQL_QUERY_EXEC_DONE(error); + if (unlikely(error) && thd->is_error()) + { + error= thd->get_stmt_da()->get_sql_errno(); + thd->clear_error(); + TABLE_LIST table_list; + table_list.init_one_table(&d.db, &d.table_name, &d.table_name, TL_UNLOCK); + TABLE_SHARE *s= tdc_acquire_share(thd, &table_list, GTS_TABLE); + if (s == d.s) + { + /* Timeout new ALTER for 5 minutes in case of error */ + s->vers_hist_part_timeout= thd->query_start() + VERS_ERROR_TIMEOUT; + s->vers_hist_part_error= error; + s->vers_altering= false; + } + if (s) + tdc_release_share(s); + } + /* In case of success ALTER invalidates TABLE_SHARE */ + thd->end_statement(); + thd->cleanup_after_query(); +err2: + server_threads.erase(thd); + delete thd; +err1: + my_free(arg); + my_thread_end(); + return NULL; +} + + +void partition_info::vers_add_hist_part(THD *thd) +{ + pthread_t hThread; + int error; + char part_name[MAX_PART_NAME_SIZE + 1]; + + /* Prevent spawning multiple instances of same task */ + bool altering; + mysql_mutex_lock(&table->s->LOCK_share); + altering= table->s->vers_altering; + if (!altering) + table->s->vers_altering= true; + mysql_mutex_unlock(&table->s->LOCK_share); + if (altering) + return; + + /* Choose first non-occupied name suffix starting from id + 1 */ + uint32 suffix= vers_info->hist_part->id + 1; + if (make_partition_name(part_name, suffix)) + { + sql_print_warning("vers_add_hist_part name generation failed for suffix %d", + suffix); + my_error(WARN_VERS_HIST_PART_ERROR, MYF(ME_WARNING), + table->s->db.str, table->s->table_name.str, 0); + return; + } + List_iterator_fast it(partitions); + partition_element *el; + + while ((el= it++)) + { + if (0 == my_strcasecmp(&my_charset_latin1, el->partition_name, part_name)) + { + if (make_partition_name(part_name, ++suffix)) + { + sql_print_warning("vers_add_hist_part name generation failed for suffix %d", + suffix); + my_error(WARN_VERS_HIST_PART_ERROR, MYF(ME_WARNING), + table->s->db.str, table->s->table_name.str, 0); + return; + } + it.rewind(); + } + } + + String q(STRING_WITH_LEN("ALTER TABLE `"), &my_charset_latin1); + if (q.append(table->s->db) || + q.append(STRING_WITH_LEN("`.`")) || + q.append(table->s->table_name) || + q.append("` ADD PARTITION (PARTITION `") || + q.append(part_name) || + q.append("` HISTORY)")) + { + my_error(ER_OUT_OF_RESOURCES, MYF(ME_ERROR_LOG)); + return; + } + vers_add_hist_part_data *data; + vers_add_hist_part_data bufs; + LEX_CSTRING part_name2; + part_name2.str= part_name; + part_name2.length= strlen(part_name); + if (!my_multi_malloc(MYF(MY_WME|ME_ERROR_LOG), &data, sizeof(*data), + &bufs.query.str, q.length() + 1, + &bufs.db.str, table->s->db.length + 1, + &bufs.table_name.str, table->s->table_name.length + 1, + &bufs.part_name.str, part_name2.length + 1, + NULL)) + return; + bufs.assign(thd, q, table, part_name2); + *data= bufs; + + if ((error= mysql_thread_create(key_thread_query, &hThread, + &connection_attrib, vers_add_hist_part_thread, data))) + { + sql_print_warning("Can't create vers_add_hist_part thread (errno= %d)", + error); + } + return; } @@ -2646,13 +2888,14 @@ bool partition_info::vers_init_info(THD * thd) bool partition_info::vers_set_interval(THD* thd, Item* interval, interval_type int_type, Item* starts, - const char *table_name) + bool auto_inc, const char *table_name) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); MYSQL_TIME ltime; uint err; vers_info->interval.type= int_type; + vers_info->auto_inc= auto_inc; /* 1. assign INTERVAL to interval.step */ if (interval->fix_fields_if_needed_for_scalar(thd, &interval)) diff --git a/sql/partition_info.h b/sql/partition_info.h index eb8e53a381a..1534189af2a 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -38,6 +38,7 @@ struct Vers_part_info : public Sql_alloc { Vers_part_info() : limit(0), + auto_inc(false), now_part(NULL), hist_part(NULL) { @@ -46,6 +47,7 @@ struct Vers_part_info : public Sql_alloc Vers_part_info(Vers_part_info &src) : interval(src.interval), limit(src.limit), + auto_inc(src.auto_inc), now_part(NULL), hist_part(NULL) { @@ -69,9 +71,30 @@ struct Vers_part_info : public Sql_alloc my_time_t start; INTERVAL step; enum interval_type type; - bool is_set() { return type < INTERVAL_LAST; } + bool is_set() const { return type < INTERVAL_LAST; } + bool lt(size_t seconds) const + { + if (step.second) + return step.second < seconds; + if (step.minute) + return step.minute * 60 < seconds; + if (step.hour) + return step.hour * 3600 < seconds; + if (step.day) + return step.day * 3600 * 24 < seconds; + // comparison is used in rough estimates, it doesn't need to be calendar-correct + if (step.month) + return step.month * 3600 * 24 * 30 < seconds; + DBUG_ASSERT(step.year); + return step.year * 86400 * 30 * 365 < seconds; + } + bool ge(size_t seconds) const + { + return !(this->lt(seconds)); + } } interval; ulonglong limit; + bool auto_inc; partition_element *now_part; partition_element *hist_part; }; @@ -394,14 +417,16 @@ public: bool vers_init_info(THD *thd); bool vers_set_interval(THD *thd, Item *interval, interval_type int_type, Item *starts, - const char *table_name); - bool vers_set_limit(ulonglong limit) + bool auto_inc, const char *table_name); + bool vers_set_limit(ulonglong limit, bool auto_inc) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); vers_info->limit= limit; + vers_info->auto_inc= auto_inc; return !limit; } void vers_set_hist_part(THD *thd); + void vers_add_hist_part(THD *thd); bool vers_fix_field_list(THD *thd); void vers_update_el_ids(); partition_element *get_partition(uint part_id) @@ -500,4 +525,14 @@ void partition_info::vers_update_el_ids() } } + +#define MAX_PART_NAME_SIZE 8 + +inline +bool make_partition_name(char *move_ptr, uint i) +{ + int res= snprintf(move_ptr, MAX_PART_NAME_SIZE + 1, "p%u", i); + return res < 0 || res > MAX_PART_NAME_SIZE; +} + #endif /* PARTITION_INFO_INCLUDED */ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 78b6cfa63c8..01cf2745c41 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7944,4 +7944,8 @@ ER_WARN_HISTORY_ROW_START_TIME ER_PART_STARTS_BEYOND_INTERVAL eng "%`s: STARTS is later than query time, first history partition may exceed INTERVAL value" ER_GALERA_REPLICATION_NOT_SUPPORTED - eng "DDL-statement is forbidden as table storage engine does not support Galera replication" \ No newline at end of file + eng "DDL-statement is forbidden as table storage engine does not support Galera replication" +ER_VERS_MODIFY_HISTORY + eng "Modifying history is prohibited, set `secure_timestamp` to NO or SUPER" +WARN_VERS_HIST_PART_ERROR + eng "Versioned table %`s.%`s: adding HISTORY partition failed with error %u, see error log for details" diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 5885e3715bf..f8ae91114f0 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -2584,11 +2584,15 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info, err+= str.append(ctime, ctime_len); err+= str.append('\''); } + if (vers_info->auto_inc) + err+= str.append(STRING_WITH_LEN(" AUTO_INCREMENT")); } - if (vers_info->limit) + else if (vers_info->limit) { err+= str.append(STRING_WITH_LEN("LIMIT ")); err+= str.append_ulonglong(vers_info->limit); + if (vers_info->auto_inc) + err+= str.append(STRING_WITH_LEN(" AUTO_INCREMENT")); } } else if (part_info->part_expr) @@ -5313,12 +5317,6 @@ that are reorganised. now_part= el; } } - if (*fast_alter_table && tab_part_info->vers_info->interval.is_set()) - { - partition_element *hist_part= tab_part_info->vers_info->hist_part; - if (hist_part->range_value <= thd->query_start()) - hist_part->part_state= PART_CHANGED; - } } List_iterator alt_it(alt_part_info->partitions); uint part_count= 0; @@ -7218,7 +7216,8 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, } else if ((alter_info->partition_flags & ALTER_PARTITION_ADD) && (part_info->part_type == RANGE_PARTITION || - part_info->part_type == LIST_PARTITION)) + part_info->part_type == LIST_PARTITION || + part_info->part_type == VERSIONING_PARTITION)) { /* ADD RANGE/LIST PARTITIONS diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f24da3ed412..692ed48ab8c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1464,6 +1464,8 @@ End SQL_MODE_ORACLE_SPECIFIC */ condition_number opt_versioning_interval_start +%type opt_vers_auto_inc + %type param_marker %type @@ -5306,24 +5308,24 @@ opt_part_option: opt_versioning_rotation: /* empty */ {} - | INTERVAL_SYM expr interval opt_versioning_interval_start + | INTERVAL_SYM expr interval opt_versioning_interval_start opt_vers_auto_inc { partition_info *part_info= Lex->part_info; const char *table_name= Lex->create_last_non_select_table->table_name.str; - if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4, table_name))) + if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4, $5, table_name))) MYSQL_YYABORT; } - | LIMIT ulonglong_num - { - partition_info *part_info= Lex->part_info; - if (unlikely(part_info->vers_set_limit($2))) + | LIMIT ulonglong_num opt_vers_auto_inc { - my_error(ER_PART_WRONG_VALUE, MYF(0), - Lex->create_last_non_select_table->table_name.str, - "LIMIT"); - MYSQL_YYABORT; + partition_info *part_info= Lex->part_info; + if (unlikely(part_info->vers_set_limit($2, $3))) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), + Lex->create_last_non_select_table->table_name.str, + "LIMIT"); + MYSQL_YYABORT; + } } - } ; @@ -5338,6 +5340,20 @@ opt_versioning_interval_start: } ; +opt_vers_auto_inc: + /* empty */ + { + $$= 0; + } + | AUTO_INC + { + $$= 1; + } + | AUTO_SYM + { + $$= 1; + } + ; /* End of partition parser part */ diff --git a/sql/table.h b/sql/table.h index 6ce92ee048e..50ac46fbe39 100644 --- a/sql/table.h +++ b/sql/table.h @@ -800,6 +800,9 @@ struct TABLE_SHARE uint partition_info_str_len; uint partition_info_buffer_size; plugin_ref default_part_plugin; + bool vers_altering; + time_t vers_hist_part_timeout; + uint vers_hist_part_error; #endif /**