From f4a097b796ab1a7ec1ff2a4c60cbf3b1868c0f23 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 11 Dec 2016 17:04:11 +0000 Subject: [PATCH] SQL: (0.5) Versioned partitions [closes #77] * one `AS OF NOW`, multiple `VERSIONING` partitions; * rotation of `VERSIONING` partitions by record count, time period; * rotation is multi-threaded; * conventional subpartitions as bottom level for versioned partitions; * `DEFAULT` keyword selects first `VERSIONING` partition; * ALTER TABLE ADD/DROP partition; * REBUILD PARTITION basic operation. --- mysql-test/r/parser.result | 4 +- mysql-test/r/partition.result | 2 +- mysql-test/r/partition_error.result | 14 +- .../parts/r/partition_syntax_innodb.result | 2 +- .../parts/r/partition_syntax_myisam.result | 2 +- mysql-test/suite/versioning/r/alter.result | 18 +- mysql-test/suite/versioning/r/create.result | 36 +-- .../suite/versioning/r/partition.result | 237 ++++++++++++++ mysql-test/suite/versioning/t/partition.test | 190 ++++++++++++ mysql-test/t/parser.test | 2 - sql/field.cc | 11 +- sql/field.h | 9 +- sql/ha_partition.cc | 7 +- sql/ha_partition.h | 34 +- sql/handler.cc | 13 +- sql/handler.h | 14 +- sql/item_timefunc.cc | 4 +- sql/lex.h | 4 +- sql/mysqld.cc | 7 +- sql/mysqld.h | 4 +- sql/partition_element.h | 55 +++- sql/partition_info.cc | 291 +++++++++++++++++- sql/partition_info.h | 109 +++++++ sql/share/errmsg-utf8.txt | 20 +- sql/sql_delete.cc | 3 +- sql/sql_partition.cc | 172 ++++++++++- sql/sql_show.cc | 5 + sql/sql_yacc.yy | 99 +++++- sql/table.cc | 26 +- sql/table.h | 45 ++- .../r/partition_syntax_tokudb.result | 2 +- 31 files changed, 1326 insertions(+), 115 deletions(-) create mode 100644 mysql-test/suite/versioning/r/partition.result create mode 100644 mysql-test/suite/versioning/t/partition.test diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result index 8ebb45b927b2b..21513ae2870c7 100644 --- a/mysql-test/r/parser.result +++ b/mysql-test/r/parser.result @@ -64,10 +64,8 @@ create table MIN(a int); ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'MIN(a int)' at line 1 create table MIN (a int); drop table MIN; -create table NOW(a int); -ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NOW(a int)' at line 1 create table NOW (a int); -drop table NOW; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NOW (a int)' at line 1 create table POSITION(a int); ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'POSITION(a int)' at line 1 create table POSITION (a int); diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index a45a389bab127..807213bd1dfa5 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1459,7 +1459,7 @@ ERROR 42000: Wrong number of subpartitions defined, mismatch with previous setti create table t1 (a int) partition by hash (a) (partition p0 (subpartition sp0)); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning create table t1 (a int) partition by range (a) (partition p0 values less than (1)); diff --git a/mysql-test/r/partition_error.result b/mysql-test/r/partition_error.result index f05b145053da7..0114838f4046d 100644 --- a/mysql-test/r/partition_error.result +++ b/mysql-test/r/partition_error.result @@ -1023,7 +1023,7 @@ c int not null, primary key (a,b)) partition by key (a) subpartition by key (b); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning select load_file('$MYSQLD_DATADIR/test/t1.par'); load_file('$MYSQLD_DATADIR/test/t1.par') NULL @@ -1034,7 +1034,7 @@ c int not null, primary key (a,b)) partition by key (a) subpartition by key (a, b); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning select load_file('$MYSQLD_DATADIR/test/t1.par'); load_file('$MYSQLD_DATADIR/test/t1.par') NULL @@ -1045,7 +1045,7 @@ c int not null, primary key (a,b)) partition by key (a) subpartition by hash (a+b); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning select load_file('$MYSQLD_DATADIR/test/t1.par'); load_file('$MYSQLD_DATADIR/test/t1.par') NULL @@ -1056,7 +1056,7 @@ c int not null, primary key (a,b)) partition by key (a) subpartition by key (b); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning select load_file('$MYSQLD_DATADIR/test/t1.par'); load_file('$MYSQLD_DATADIR/test/t1.par') NULL @@ -1067,7 +1067,7 @@ c int not null, primary key (a,b)) partition by key (a) subpartition by key (a, b); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning select load_file('$MYSQLD_DATADIR/test/t1.par'); load_file('$MYSQLD_DATADIR/test/t1.par') NULL @@ -1078,7 +1078,7 @@ c int not null, primary key (a,b)) partition by key (a) subpartition by hash (a+b); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning select load_file('$MYSQLD_DATADIR/test/t1.par'); load_file('$MYSQLD_DATADIR/test/t1.par') NULL @@ -1135,7 +1135,7 @@ c int not null, primary key (a,b)) partition by key (a) subpartition by hash (3+4); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning CREATE TABLE t1 ( a int not null, b int not null, diff --git a/mysql-test/suite/parts/r/partition_syntax_innodb.result b/mysql-test/suite/parts/r/partition_syntax_innodb.result index 767f023d04e66..119f775c9eb64 100644 --- a/mysql-test/suite/parts/r/partition_syntax_innodb.result +++ b/mysql-test/suite/parts/r/partition_syntax_innodb.result @@ -510,7 +510,7 @@ f_charbig VARCHAR(1000) ) PARTITION BY RANGE(f_int1) ( PARTITION part1 VALUES LESS THAN (1000) (SUBPARTITION subpart11)); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning #------------------------------------------------------------------------ # 2.2 Every partition must have the same number of subpartitions. # This is a limitation of MySQL 5.1, which could be removed in diff --git a/mysql-test/suite/parts/r/partition_syntax_myisam.result b/mysql-test/suite/parts/r/partition_syntax_myisam.result index 97eabe7d2ceff..2202fa0789267 100644 --- a/mysql-test/suite/parts/r/partition_syntax_myisam.result +++ b/mysql-test/suite/parts/r/partition_syntax_myisam.result @@ -510,7 +510,7 @@ f_charbig VARCHAR(1000) ) PARTITION BY RANGE(f_int1) ( PARTITION part1 VALUES LESS THAN (1000) (SUBPARTITION subpart11)); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning #------------------------------------------------------------------------ # 2.2 Every partition must have the same number of subpartitions. # This is a limitation of MySQL 5.1, which could be removed in diff --git a/mysql-test/suite/versioning/r/alter.result b/mysql-test/suite/versioning/r/alter.result index 5011302f1518b..ca2a7e682ca97 100644 --- a/mysql-test/suite/versioning/r/alter.result +++ b/mysql-test/suite/versioning/r/alter.result @@ -7,9 +7,9 @@ t CREATE TABLE `t` ( `a` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 alter table t without system versioning; -ERROR HY000: Wrong parameters for versioned table `t`: table is not versioned +ERROR HY000: Wrong parameters for `t`: table is not versioned alter table t with system versioning without system versioning; -ERROR HY000: Wrong parameters for versioned table `t`: Versioning specified more than once for the same table +ERROR HY000: Wrong parameters for `t`: Versioning specified more than once for the same table alter table t with system versioning; show create table t; Table Create Table @@ -119,9 +119,9 @@ t CREATE TABLE `t` ( PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING alter table t add column f int after sys_trx_start; -ERROR HY000: Wrong parameters for versioned table `t`: Can not put new field after system versioning field +ERROR HY000: Wrong parameters for `t`: Can not put new field after system versioning field alter table t add column f int after sys_trx_end; -ERROR HY000: Wrong parameters for versioned table `t`: Can not put new field after system versioning field +ERROR HY000: Wrong parameters for `t`: Can not put new field after system versioning field alter table t drop column a; show create table t; Table Create Table @@ -135,9 +135,9 @@ t CREATE TABLE `t` ( PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING alter table t drop column sys_trx_start; -ERROR HY000: Wrong parameters for versioned table `t`: Can not drop system versioning field +ERROR HY000: Wrong parameters for `t`: Can not drop system versioning field alter table t drop column sys_trx_end; -ERROR HY000: Wrong parameters for versioned table `t`: Can not drop system versioning field +ERROR HY000: Wrong parameters for `t`: Can not drop system versioning field create or replace table t( a int ); @@ -205,11 +205,11 @@ t CREATE TABLE `t` ( `b` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 alter table t modify a int with system versioning; -ERROR HY000: Wrong parameters for versioned table `t`: Can not change fields versioning mode in a non-versioned table +ERROR HY000: Wrong parameters for `t`: Can not change fields versioning mode in a non-versioned table alter table t modify a int with system versioning with system versioning; -ERROR HY000: Wrong parameters for versioned table `t`: Versioning specified more than once for the same field +ERROR HY000: Wrong parameters for `t`: Versioning specified more than once for the same field alter table t modify a int with system versioning without system versioning; -ERROR HY000: Wrong parameters for versioned table `t`: Versioning specified more than once for the same field +ERROR HY000: Wrong parameters for `t`: Versioning specified more than once for the same field alter table t with system versioning; alter table t modify a int without system versioning; show create table t; diff --git a/mysql-test/suite/versioning/r/create.result b/mysql-test/suite/versioning/r/create.result index be9f9ae7ea906..1fcf0c1e6a86e 100644 --- a/mysql-test/suite/versioning/r/create.result +++ b/mysql-test/suite/versioning/r/create.result @@ -32,14 +32,14 @@ Sys_start2 timestamp(6) generated always as row start, Sys_end timestamp(6) generated always as row end, period for system_time (Sys_start, Sys_end) ) with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: multiple 'GENERATED ALWAYS AS ROW START' +ERROR HY000: Wrong parameters for `t1`: multiple 'GENERATED ALWAYS AS ROW START' create or replace table t1 ( XNo int unsigned, Sys_start timestamp(6) generated always as row start, Sys_end2 timestamp(6) generated always as row end, period for system_time (Sys_start, Sys_end) ) with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: 'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW END' mismatch +ERROR HY000: Wrong parameters for `t1`: 'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW END' mismatch create or replace table t1 ( XNo int unsigned, Sys_start timestamp(6) generated always as row start, @@ -47,12 +47,12 @@ Sys_end timestamp(6) generated always as row end, Sys_end2 timestamp(6) generated always as row end, period for system_time (Sys_start, Sys_end) ) with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: multiple 'GENERATED ALWAYS AS ROW END' +ERROR HY000: Wrong parameters for `t1`: multiple 'GENERATED ALWAYS AS ROW END' create or replace table t1 ( XNo int unsigned, period for system_time (Sys_start, Sys_end) ) with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: 'GENERATED AS ROW START' column missing +ERROR HY000: Wrong parameters for `t1`: 'GENERATED AS ROW START' column missing create or replace table t1 ( XNo int unsigned, Sys_start timestamp(6) generated always as row start, @@ -60,28 +60,28 @@ Sys_end timestamp(6) generated always as row end, Sys_end2 timestamp(6) generated always as row end, period for system_time (Sys_start, Sys_end) ); -ERROR HY000: Wrong parameters for versioned table `t1`: multiple 'GENERATED ALWAYS AS ROW END' +ERROR HY000: Wrong parameters for `t1`: multiple 'GENERATED ALWAYS AS ROW END' create or replace table t1 ( XNo int unsigned, Sys_start timestamp(6) generated always as row start, Sys_end timestamp(6) generated always as row end, period for system_time (sys_insert, sys_remove) ) with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: 'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW START' mismatch +ERROR HY000: Wrong parameters for `t1`: 'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW START' mismatch create or replace table t1 ( XNo int unsigned, Sys_start timestamp(6) generated always as row start, Sys_end timestamp(6) generated always as row end, period for system_time (Sys_start, Sys_end) ); -ERROR HY000: Wrong parameters for versioned table `t1`: 'WITH SYSTEM VERSIONING' missing +ERROR HY000: Wrong parameters for `t1`: 'WITH SYSTEM VERSIONING' missing create or replace table t1 ( XNo int unsigned, Sys_start timestamp(6) generated always as row start, Sys_end timestamp(6) generated always as row end, period for system_time (Sys_start, Sys_start) ); -ERROR HY000: Wrong parameters for versioned table `t1`: 'PERIOD FOR SYSTEM_TIME' columns must be different +ERROR HY000: Wrong parameters for `t1`: 'PERIOD FOR SYSTEM_TIME' columns must be different create or replace table t1 ( XNo int unsigned, Sys_start int generated always as row start, @@ -147,7 +147,7 @@ create or replace table t1 ( A int, B int without system versioning ); -ERROR HY000: Wrong parameters for versioned table `t1`: 'WITH SYSTEM VERSIONING' missing +ERROR HY000: Wrong parameters for `t1`: 'WITH SYSTEM VERSIONING' missing create or replace table t1 ( A int, B int without system versioning @@ -190,37 +190,37 @@ t1 CREATE TABLE `t1` ( create or replace table t1 ( A int without system versioning ); -ERROR HY000: Wrong parameters for versioned table `t1`: 'WITH SYSTEM VERSIONING' missing +ERROR HY000: Wrong parameters for `t1`: 'WITH SYSTEM VERSIONING' missing create or replace table t1 ( A int without system versioning ) with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: versioned fields missing +ERROR HY000: Wrong parameters for `t1`: versioned fields missing create or replace table t1 ( A int without system versioning with system versioning ); -ERROR HY000: Wrong parameters for versioned table `t1`: Versioning specified more than once for the same field +ERROR HY000: Wrong parameters for `t1`: Versioning specified more than once for the same field create or replace table t1 ( A int with system versioning without system versioning ); -ERROR HY000: Wrong parameters for versioned table `t1`: Versioning specified more than once for the same field +ERROR HY000: Wrong parameters for `t1`: Versioning specified more than once for the same field create table t( a int ) without system versioning; -ERROR HY000: Wrong parameters for versioned table `t`: 'WITHOUT SYSTEM VERSIONING' is not allowed +ERROR HY000: Wrong parameters for `t`: 'WITHOUT SYSTEM VERSIONING' is not allowed create or replace table t1 ( A int ) without system versioning with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: Versioning specified more than once for the same table +ERROR HY000: Wrong parameters for `t1`: Versioning specified more than once for the same table create or replace table t1 ( A int ) with system versioning without system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: Versioning specified more than once for the same table +ERROR HY000: Wrong parameters for `t1`: Versioning specified more than once for the same table create or replace table t1 ( A int ) with system versioning with system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: Versioning specified more than once for the same table +ERROR HY000: Wrong parameters for `t1`: Versioning specified more than once for the same table create or replace table t1 ( A int ) without system versioning without system versioning; -ERROR HY000: Wrong parameters for versioned table `t1`: Versioning specified more than once for the same table +ERROR HY000: Wrong parameters for `t1`: Versioning specified more than once for the same table drop table t1; diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result new file mode 100644 index 0000000000000..b2f31837c4bbb --- /dev/null +++ b/mysql-test/suite/versioning/r/partition.result @@ -0,0 +1,237 @@ +create table t1 (x int) +with system versioning +engine innodb +partition by range columns (x) ( +partition p0 values less than (100), +partition p1 values less than (1000)); +insert into t1 values (3), (300); +select * from t1; +x +3 +300 +select * from t1 partition (p0); +x +3 +select * from t1 partition (p1); +x +300 +delete from t1; +select * from t1; +x +select * from t1 for system_time all; +x +3 +300 +select * from t1 partition (p0) for system_time all; +x +3 +select * from t1 partition (p1) for system_time all; +x +300 +create or replace table t1 (x int) +partition by system_time ( +partition p0 as of now, +partition p1 versioning); +ERROR HY000: System Versioning required: `BY SYSTEM_TIME` partitioning +create or replace table t1 (x int); +alter table t1 +partition by system_time ( +partition p0 as of now, +partition p1 versioning); +ERROR HY000: System Versioning required: `BY SYSTEM_TIME` partitioning +create or replace table t1 (x int) +with system versioning +partition by system_time ( +partition p0 as of now); +ERROR HY000: Wrong parameters for `BY SYSTEM_TIME`: unexpected number of partitions (expected > 1) +create or replace table t1 (x int) +with system versioning +partition by system_time ( +partition p0 as of now, +partition p1 as of now); +ERROR HY000: Wrong parameters for `BY SYSTEM_TIME`: multiple `AS OF NOW` partitions +create or replace table t1 (x int) +with system versioning +partition by system_time ( +partition p0 versioning, +partition p1 versioning); +ERROR HY000: Wrong parameters for `BY SYSTEM_TIME`: no `AS OF NOW` partition defined +create or replace table t1 (x int) +with system versioning +partition by system_time ( +partition p0 as of now, +partition p1 versioning); +alter table t1 add partition ( +partition p2 as of now); +ERROR HY000: Wrong parameters for `BY SYSTEM_TIME`: multiple `AS OF NOW` partitions +alter table t1 add partition ( +partition p2 versioning default); +Warnings: +Warning 4041 Maybe missing parameters: no rotation condition for multiple `VERSIONING` partitions. +alter table t1 drop partition p0; +ERROR HY000: Wrong parameters for `BY SYSTEM_TIME`: `AS OF NOW` partition can not be dropped +alter table t1 drop partition p2; +alter table t1 drop partition p1; +ERROR HY000: Wrong parameters for `BY SYSTEM_TIME`: one `AS OF NOW` and at least one `VERSIONING` partition required +insert into t1 values (1); +select * from t1; +x +1 +select * from t1 partition (p0); +x +1 +select * from t1 partition (p1); +x +delete from t1; +select * from t1 partition (p0) for system_time all; +x +select * from t1 partition (p1) for system_time all; +x +1 +create or replace table t1 (x int) +with system versioning +partition by system_time ( +partition p0 versioning, +partition p1 as of now); +insert into t1 values (1); +select * from t1; +x +1 +select * from t1 partition (p0); +x +select * from t1 partition (p1); +x +1 +delete from t1; +select * from t1 partition (p0) for system_time all; +x +1 +select * from t1 partition (p1) for system_time all; +x +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 ( +partition p0 as of now, +partition p1 versioning, +partition p2 versioning); +Warnings: +Warning 4041 No `DEFAULT` for `VERSIONING` partitions. Setting `p1` as default. +insert into t1 values (1), (2); +select * from t1 partition (p0); +x +1 +2 +delete from t1; +Warnings: +Note 4042 Switching from partition `p1` to `p2` +select * from t1 partition (p1) for system_time all; +x +1 +select * from t1 partition (p2) for system_time all; +x +2 +insert into t1 values (3); +delete from t1; +Warnings: +Warning 4040 Using full partition `p2`, need more VERSIONING partitions! +select * from t1 partition (p2) for system_time all; +x +2 +3 +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time interval 1 second ( +partition p0 as of now, +partition p1 versioning default, +partition p2 versioning); +insert into t1 values (1), (2), (3); +select * from t1 partition (p0); +x +1 +2 +3 +delete from t1; +select * from t1 partition (p1) for system_time all; +x +1 +2 +3 +insert into t1 values (4); +delete from t1; +Warnings: +Note 4042 Switching from partition `p1` to `p2` +select * from t1 partition (p2) for system_time all; +x +4 +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 ( +partition p0 as of now, +partition p1 versioning default, +partition p2 versioning default); +ERROR HY000: Wrong parameters for `BY SYSTEM_TIME`: multiple `DEFAULT` partitions +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 ( +partition p0 as of now, +partition p1 versioning, +partition p2 versioning default); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `sys_trx_start` timestamp(6) GENERATED ALWAYS AS ROW START, + `sys_trx_end` timestamp(6) GENERATED ALWAYS AS ROW END, + PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +/*!50500 PARTITION BY SYSTEM_TIME LIMIT 1 +(PARTITION p0 AS OF NOW ENGINE = MyISAM, + PARTITION p1 VERSIONING ENGINE = MyISAM, + PARTITION p2 VERSIONING DEFAULT ENGINE = MyISAM) */ +insert into t1 values (4), (5); +delete from t1; +Warnings: +Note 4042 Switching from partition `p2` to `p1` +select * from t1 partition (p2) for system_time all; +x +4 +select * from t1 partition (p1) for system_time all; +x +5 +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 +subpartition by key (x) +subpartitions 2 ( +partition p0 as of now, +partition p1 versioning default, +partition p2 versioning); +insert into t1 (x) values (1), (2), (3); +select * from t1 partition (p0sp0); +x +1 +3 +select * from t1 partition (p0sp1); +x +2 +delete from t1; +Warnings: +Note 4042 Switching from partition `p1` to `p2` +Warning 4040 Using full partition `p2`, need more VERSIONING partitions! +select * from t1 partition (p1sp0) for system_time all; +x +1 +select * from t1 partition (p1sp1) for system_time all; +x +select * from t1 partition (p2sp0) for system_time all; +x +3 +select * from t1 partition (p2sp1) for system_time all; +x +2 +drop table t1; diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test new file mode 100644 index 0000000000000..c2b7c097a5f03 --- /dev/null +++ b/mysql-test/suite/versioning/t/partition.test @@ -0,0 +1,190 @@ +--source include/have_innodb.inc +--source include/have_partition.inc + +### check InnoDB versioning and conventional partitioning + +create table t1 (x int) +with system versioning +engine innodb +partition by range columns (x) ( + partition p0 values less than (100), + partition p1 values less than (1000)); + +insert into t1 values (3), (300); +select * from t1; +select * from t1 partition (p0); +select * from t1 partition (p1); + +delete from t1; +select * from t1; +select * from t1 for system_time all; +select * from t1 partition (p0) for system_time all; +select * from t1 partition (p1) for system_time all; + +### check server-level partitioning + +# create errors +--error ER_VERSIONING_REQUIRED +create or replace table t1 (x int) +partition by system_time ( + partition p0 as of now, + partition p1 versioning); + +create or replace table t1 (x int); +--error ER_VERSIONING_REQUIRED +alter table t1 +partition by system_time ( + partition p0 as of now, + partition p1 versioning); + +--error ER_VERS_WRONG_PARAMS +create or replace table t1 (x int) +with system versioning +partition by system_time ( + partition p0 as of now); + +--error ER_VERS_WRONG_PARAMS +create or replace table t1 (x int) +with system versioning +partition by system_time ( + partition p0 as of now, + partition p1 as of now); + +--error ER_VERS_WRONG_PARAMS +create or replace table t1 (x int) +with system versioning +partition by system_time ( + partition p0 versioning, + partition p1 versioning); + +create or replace table t1 (x int) +with system versioning +partition by system_time ( + partition p0 as of now, + partition p1 versioning); + +# alter table +--error ER_VERS_WRONG_PARAMS +alter table t1 add partition ( + partition p2 as of now); + +alter table t1 add partition ( + partition p2 versioning default); + +--error ER_VERS_WRONG_PARAMS +alter table t1 drop partition p0; +alter table t1 drop partition p2; +--error ER_VERS_WRONG_PARAMS +alter table t1 drop partition p1; + +# insertion, deletion +insert into t1 values (1); +select * from t1; +select * from t1 partition (p0); +select * from t1 partition (p1); + +delete from t1; +select * from t1 partition (p0) for system_time all; +select * from t1 partition (p1) for system_time all; + +create or replace table t1 (x int) +with system versioning +partition by system_time ( + partition p0 versioning, + partition p1 as of now); + +insert into t1 values (1); +select * from t1; +select * from t1 partition (p0); +select * from t1 partition (p1); + +delete from t1; +select * from t1 partition (p0) for system_time all; +select * from t1 partition (p1) for system_time all; + +# rotation by LIMIT +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 ( + partition p0 as of now, + partition p1 versioning, + partition p2 versioning); + +insert into t1 values (1), (2); +select * from t1 partition (p0); +delete from t1; +select * from t1 partition (p1) for system_time all; +select * from t1 partition (p2) for system_time all; + +insert into t1 values (3); +delete from t1; +select * from t1 partition (p2) for system_time all; + +# rotation by INTERVAL +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time interval 1 second ( + partition p0 as of now, + partition p1 versioning default, + partition p2 versioning); + +insert into t1 values (1), (2), (3); +select * from t1 partition (p0); +delete from t1; +select * from t1 partition (p1) for system_time all; + +--sleep 2 +insert into t1 values (4); +delete from t1; +select * from t1 partition (p2) for system_time all; + +# DEFAULT partition +--error ER_VERS_WRONG_PARAMS +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 ( + partition p0 as of now, + partition p1 versioning default, + partition p2 versioning default); + +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 ( + partition p0 as of now, + partition p1 versioning, + partition p2 versioning default); + +show create table t1; + +insert into t1 values (4), (5); +delete from t1; +select * from t1 partition (p2) for system_time all; +select * from t1 partition (p1) for system_time all; + +# Subpartitions +create or replace table t1 (x int) +with system versioning +engine myisam +partition by system_time limit 1 +subpartition by key (x) +subpartitions 2 ( + partition p0 as of now, + partition p1 versioning default, + partition p2 versioning); + +insert into t1 (x) values (1), (2), (3); +select * from t1 partition (p0sp0); +select * from t1 partition (p0sp1); + +delete from t1; +select * from t1 partition (p1sp0) for system_time all; +select * from t1 partition (p1sp1) for system_time all; +select * from t1 partition (p2sp0) for system_time all; +select * from t1 partition (p2sp1) for system_time all; + +drop table t1; + diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test index 5970b564c8546..787dee2846ba1 100644 --- a/mysql-test/t/parser.test +++ b/mysql-test/t/parser.test @@ -104,9 +104,7 @@ create table MIN (a int); drop table MIN; --error ER_PARSE_ERROR -create table NOW(a int); create table NOW (a int); -drop table NOW; --error ER_PARSE_ERROR create table POSITION(a int); diff --git a/sql/field.cc b/sql/field.cc index bd916b0d5b7b8..4a97424702089 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5519,8 +5519,15 @@ my_time_t Field_timestampf::get_timestamp(const uchar *pos, ulong *sec_part) const { struct timeval tm; - my_timestamp_from_binary(&tm, pos, dec); - *sec_part= tm.tv_usec; + if (sec_part) + { + my_timestamp_from_binary(&tm, pos ? pos : ptr, dec); + *sec_part= tm.tv_usec; + } + else + { + my_timestamp_from_binary(&tm, pos ? pos : ptr, 0); + } return tm.tv_sec; } diff --git a/sql/field.h b/sql/field.h index 84b85dfec5725..210a22cf936b3 100644 --- a/sql/field.h +++ b/sql/field.h @@ -679,10 +679,6 @@ class Field: public Value_source */ virtual bool set_max() { DBUG_ASSERT(0); return false; } - - /** - Used by System Versioning. - */ virtual bool is_max() { DBUG_ASSERT(0); return false; } @@ -950,6 +946,9 @@ class Field: public Value_source } virtual void set_explicit_default(Item *value); + virtual my_time_t get_timestamp(const uchar *pos= NULL, ulong *sec_part= NULL) const + { DBUG_ASSERT(0); return 0; } + /** Evaluates the @c INSERT default function and stores the result in the field. If no such function exists for the column, or the function is not @@ -2517,7 +2516,7 @@ class Field_timestampf :public Field_timestamp_with_dec { bool set_max(); bool is_max(); void store_TIME(my_time_t timestamp, ulong sec_part); - my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; + my_time_t get_timestamp(const uchar *pos= NULL, ulong *sec_part= NULL) const; uint size_of() const { return sizeof(*this); } }; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 400ca6129d6e2..20c18ab9f2840 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2023,12 +2023,15 @@ int ha_partition::copy_partitions(ulonglong * const copied, else { THD *thd= ha_thd(); + handler *new_file= m_new_file[new_part]; /* Copy record to new handler */ (*copied)++; + if (new_file->ha_external_lock(thd, F_UNLCK) || new_file->ha_external_lock(thd, F_WRLCK)) + goto error; tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ - result= m_new_file[new_part]->ha_write_row(m_rec0); + result= new_file->ha_write_row(m_rec0); reenable_binlog(thd); - if (result) + if (new_file->ha_external_lock(thd, F_UNLCK) || new_file->ha_external_lock(thd, F_RDLCK) || result) goto error; } } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 3ea8d4a855d8e..440b0cf427ee6 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -23,7 +23,7 @@ enum partition_keywords { - PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR, + PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_SYSTEM_TIME, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR, PKW_COLUMNS, PKW_ALGORITHM }; @@ -1282,6 +1282,38 @@ class ha_partition :public handler return h; } + virtual bool versioned() const + { + return m_innodb; + } + + virtual ha_rows part_recs_slow(void *_part_elem) + { + partition_element *part_elem= reinterpret_cast(_part_elem); + DBUG_ASSERT(m_part_info); + uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; + uint32 part_id= part_elem->id * sub_factor; + uint32 part_id_end= part_id + sub_factor; + DBUG_ASSERT(part_id_end <= m_tot_parts); + ha_rows part_recs= 0; + for (; part_id < part_id_end; ++part_id) + { + handler *file= m_file[part_id]; + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); + file->info(HA_STATUS_TIME | HA_STATUS_VARIABLE | + HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK); + + part_recs+= file->stats.records; + } + return part_recs; + } + + virtual handler* part_handler(uint32 part_id) + { + DBUG_ASSERT(part_id < m_tot_parts); + return m_file[part_id]; + } + friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); }; diff --git a/sql/handler.cc b/sql/handler.cc index 497ff096f8e0b..6c0ecb372265c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6491,7 +6491,7 @@ static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) return *s == NULL; } -static bool create_sys_trx_field(THD *thd, const char *field_name, +static bool vers_create_sys_field(THD *thd, const char *field_name, Alter_info *alter_info, String **s, int flags, bool integer_fields) @@ -6536,16 +6536,19 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; - return create_sys_trx_field(thd, "sys_trx_start", alter_info, + static const char * sys_trx_start= "sys_trx_start"; + static const char * sys_trx_end= "sys_trx_end"; + + return vers_create_sys_field(thd, sys_trx_start, alter_info, &generated_as_row.start, VERS_SYS_START_FLAG, integer_fields) || - create_sys_trx_field(thd, "sys_trx_end", alter_info, + vers_create_sys_field(thd, sys_trx_end, alter_info, &generated_as_row.end, VERS_SYS_END_FLAG, integer_fields) || create_string(thd->mem_root, &period_for_system_time.start, - "sys_trx_start") || + sys_trx_start) || create_string(thd->mem_root, &period_for_system_time.end, - "sys_trx_end"); + sys_trx_end); } bool Vers_parse_info::check_and_fix_implicit( diff --git a/sql/handler.h b/sql/handler.h index a9bb31daf4cda..2c4635694ed6c 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1371,7 +1371,6 @@ struct handlerton /* System Versioning */ - bool versioned() const; bool (*vers_query_trx_id)(THD* thd, void *out, ulonglong trx_id, vtq_field_t field); bool (*vers_query_commit_ts)(THD* thd, void *out, const MYSQL_TIME &commit_ts, vtq_field_t field, bool backwards); bool (*vers_trx_sees)(THD *thd, bool &result, ulonglong trx_id1, ulonglong trx_id0, ulonglong commit_id1, uchar iso_level1, ulonglong commit_id0); @@ -4223,6 +4222,13 @@ class handler :public Sql_alloc virtual void set_lock_type(enum thr_lock_type lock); friend enum icp_result handler_index_cond_check(void* h_arg); + + virtual bool versioned() const + { DBUG_ASSERT(ht); return partition_ht()->flags & HTON_SUPPORTS_SYS_VERSIONING; } + virtual ha_rows part_recs_slow(void *_part_elem) + { DBUG_ASSERT(0); return false; } + virtual handler* part_handler(uint32 part_id) + { DBUG_ASSERT(0); return NULL; } protected: Handler_share *get_ha_share_ptr(); void set_ha_share_ptr(Handler_share *arg_ha_share); @@ -4408,10 +4414,4 @@ void print_keydup_error(TABLE *table, KEY *key, myf errflag); int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info); int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table); - -inline -bool handlerton::versioned() const -{ - return flags & HTON_SUPPORTS_SYS_VERSIONING; -} #endif /* HANDLER_INCLUDED */ diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 95c26699703f8..bd8aefdcc1530 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3290,7 +3290,7 @@ VTQ_common::init_hton() f->field->table && f->field->table->s && f->field->table->s->db_plugin); - hton= plugin_hton(f->field->table->s->db_plugin); + hton= f->field->table->file->partition_ht(); DBUG_ASSERT(hton); } else if (innodb_plugin) @@ -3298,7 +3298,7 @@ VTQ_common::init_hton() hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); DBUG_ASSERT(hton); } - if (hton && !hton->versioned()) + if (hton && !(hton->flags & HTON_SUPPORTS_SYS_VERSIONING)) { my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); hton= NULL; diff --git a/sql/lex.h b/sql/lex.h index 409e8c7853238..1b69045d33bf5 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -406,6 +406,7 @@ static SYMBOL symbols[] = { { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, { "NOT", SYM(NOT_SYM)}, + { "NOW", SYM(NOW_SYM)}, { "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)}, { "NULL", SYM(NULL_SYM)}, { "NUMBER", SYM(NUMBER_SYM)}, @@ -662,7 +663,7 @@ static SYMBOL symbols[] = { { "VIA", SYM(VIA_SYM)}, { "VIEW", SYM(VIEW_SYM)}, { "VIRTUAL", SYM(VIRTUAL_SYM)}, - { "VERSIONING", SYM(VERSIONING)}, + { "VERSIONING", SYM(VERSIONING_SYM)}, { "WAIT", SYM(WAIT_SYM)}, { "WARNINGS", SYM(WARNINGS)}, { "WEEK", SYM(WEEK_SYM)}, @@ -708,7 +709,6 @@ static SYMBOL sql_functions[] = { { "MAX", SYM(MAX_SYM)}, { "MID", SYM(SUBSTRING)}, /* unireg function */ { "MIN", SYM(MIN_SYM)}, - { "NOW", SYM(NOW_SYM)}, { "NTH_VALUE", SYM(NTH_VALUE_SYM)}, { "NTILE", SYM(NTILE_SYM)}, { "POSITION", SYM(POSITION_SYM)}, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a5cfd0527e850..df0a7167c581b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -940,6 +940,9 @@ PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered, key_LOCK_slave_background; PSI_mutex_key key_TABLE_SHARE_LOCK_share; +PSI_mutex_key key_TABLE_SHARE_LOCK_rotation; +PSI_cond_key key_TABLE_SHARE_COND_rotation; + static PSI_mutex_info all_server_mutexes[]= { #ifdef HAVE_MMAP @@ -999,6 +1002,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_structure_guard_mutex, "Query_cache::structure_guard_mutex", 0}, { &key_TABLE_SHARE_LOCK_ha_data, "TABLE_SHARE::LOCK_ha_data", 0}, { &key_TABLE_SHARE_LOCK_share, "TABLE_SHARE::LOCK_share", 0}, + { &key_TABLE_SHARE_LOCK_rotation, "TABLE_SHARE::LOCK_rotation", 0}, { &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL}, { &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL}, { &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL}, @@ -1113,7 +1117,8 @@ static PSI_cond_info all_server_conds[]= { &key_COND_slave_background, "COND_slave_background", 0}, { &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL}, { &key_COND_wait_gtid, "COND_wait_gtid", 0}, - { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0} + { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}, + { &key_TABLE_SHARE_COND_rotation, "TABLE_SHARE::COND_rotation", 0} }; PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, diff --git a/sql/mysqld.h b/sql/mysqld.h index 83d723c6ab7f6..c729319c65782 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -305,7 +305,8 @@ extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, extern PSI_mutex_key key_TABLE_SHARE_LOCK_share, key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, - key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit; + key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit, + key_TABLE_SHARE_LOCK_rotation; extern PSI_mutex_key key_LOCK_gtid_waiting; extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, @@ -339,6 +340,7 @@ extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue, key_COND_rpl_thread_stop, key_COND_rpl_thread_pool, key_COND_parallel_entry, key_COND_group_commit_orderer; extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates; +extern PSI_cond_key key_TABLE_SHARE_COND_rotation; extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, key_thread_handle_manager, key_thread_kill_server, key_thread_main, diff --git a/sql/partition_element.h b/sql/partition_element.h index b979b7a58e626..e76558bf7ae08 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -26,7 +26,8 @@ enum partition_type { NOT_A_PARTITION= 0, RANGE_PARTITION, HASH_PARTITION, - LIST_PARTITION + LIST_PARTITION, + VERSIONING_PARTITION }; enum partition_state { @@ -89,6 +90,37 @@ typedef struct p_elem_val struct st_ddl_log_memory_entry; +class Stat_timestampf : public Sql_alloc +{ + static const uint buf_size= 4 + (TIME_SECOND_PART_DIGITS + 1) / 2; + uchar min_buf[buf_size]; + uchar max_buf[buf_size]; + Field_timestampf min_value; + Field_timestampf max_value; + +public: + Stat_timestampf(const char *field_name, TABLE_SHARE *share) : + min_value(min_buf, NULL, 0, Field::NONE, field_name, share, 6), + max_value(max_buf, NULL, 0, Field::NONE, field_name, share, 6) + { + min_value.set_max(); + memset(max_buf, 0, buf_size); + } + void update(Field *from) + { + from->update_min(&min_value, false); + from->update_max(&max_value, false); + } + my_time_t min_time() + { + return min_value.get_timestamp(); + } + my_time_t max_time() + { + return max_value.get_timestamp(); + } +}; + class partition_element :public Sql_alloc { public: List subpartitions; @@ -109,6 +141,17 @@ class partition_element :public Sql_alloc { bool has_null_value; bool signed_flag; // Range value signed bool max_value; // MAXVALUE range + uint32 id; + + enum elem_type + { + CONVENTIONAL= 0, + AS_OF_NOW, + VERSIONING + }; + + elem_type type; + Stat_timestampf *stat_trx_end; partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), @@ -117,7 +160,10 @@ class partition_element :public Sql_alloc { data_file_name(NULL), index_file_name(NULL), engine_type(NULL), connect_string(null_lex_str), part_state(PART_NORMAL), nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), - signed_flag(FALSE), max_value(FALSE) + signed_flag(FALSE), max_value(FALSE), + id(UINT32_MAX), + type(CONVENTIONAL), + stat_trx_end(NULL) { } partition_element(partition_element *part_elem) @@ -132,7 +178,10 @@ class partition_element :public Sql_alloc { connect_string(null_lex_str), part_state(part_elem->part_state), nodegroup_id(part_elem->nodegroup_id), - has_null_value(FALSE) + has_null_value(FALSE), + id(part_elem->id), + type(part_elem->type), + stat_trx_end(NULL) { } ~partition_element() {} diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 1d4123cb3b218..f40a3d6710c05 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -30,6 +30,7 @@ #include "sql_parse.h" // test_if_data_home_dir #include "sql_acl.h" // *_ACL #include "sql_base.h" // fill_record +#include "sql_statistics.h" // vers_stat_end #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -114,6 +115,19 @@ partition_info *partition_info::get_clone(THD *thd) part_clone->list_val_list.push_back(new_val, mem_root); } } + if (part_type == VERSIONING_PARTITION && vers_info) + { + // clone Vers_part_info; set now_part, hist_part + clone->vers_info= new (mem_root) Vers_part_info(*vers_info); + List_iterator it(clone->partitions); + while ((part= it++)) + { + if (vers_info->now_part && part->id == vers_info->now_part->id) + clone->vers_info->now_part= part; + else if (vers_info->hist_part && part->id == vers_info->hist_part->id) + clone->vers_info->hist_part= part; + } // while ((part= it++)) + } // if (part_type == VERSIONING_PARTITION ... DBUG_RETURN(clone); } @@ -1058,6 +1072,216 @@ bool partition_info::has_unique_name(partition_element *element) DBUG_RETURN(TRUE); } +bool partition_info::vers_init_info(THD * thd) +{ + part_type= VERSIONING_PARTITION; + list_of_part_fields= TRUE; + column_list= TRUE; + vers_info= new (thd->mem_root) Vers_part_info; + if (!vers_info) + { + mem_alloc_error(sizeof(Vers_part_info)); + return true; + } + return false; +} + +bool partition_info::vers_set_interval(const INTERVAL & i) +{ + if (i.neg) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "negative INTERVAL"); + return true; + } + if (i.second_part) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "second fractions in INTERVAL"); + return true; + } + + DBUG_ASSERT(vers_info); + + // TODO: INTERVAL conversion to seconds leads to mismatch with calendar intervals (MONTH and YEAR) + vers_info->interval= + i.second + + i.minute * 60 + + i.hour * 60 * 60 + + i.day * 24 * 60 * 60 + + i.month * 30 * 24 * 60 * 60 + + i.year * 365 * 30 * 24 * 60 * 60; + + if (vers_info->interval == 0) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "zero INTERVAL"); + return true; + } + return false; +} + +bool partition_info::vers_set_limit(ulonglong limit) +{ + if (limit < 1) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "non-positive LIMIT"); + return true; + } + DBUG_ASSERT(vers_info); + + vers_info->limit= limit; + return false; +} + +partition_element* +partition_info::vers_part_rotate(THD * thd) +{ + DBUG_ASSERT(table && table->s); + if (table->s->free_parts.is_empty()) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PART_FULL, + ER_THD(thd, WARN_VERS_PART_FULL), + vers_info->hist_part->partition_name); + return vers_info->hist_part; + } + + table->s->vers_part_rotate(); + DBUG_ASSERT(table->s->hist_part_id < num_parts); + const char* old_part_name= vers_info->hist_part->partition_name; + vers_hist_part(); + + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_NOTE, + WARN_VERS_PART_ROTATION, + ER_THD(thd, WARN_VERS_PART_ROTATION), + old_part_name, + vers_info->hist_part->partition_name); + + return vers_info->hist_part; +} + +bool partition_info::vers_setup_1(THD * thd) +{ + DBUG_ASSERT(part_type == VERSIONING_PARTITION); + if (!table->versioned()) + { + my_error(ER_VERSIONING_REQUIRED, MYF(0), "`BY SYSTEM_TIME` partitioning"); + return true; + } + Field *sys_trx_end= table->vers_end_field(); + part_field_list.empty(); + part_field_list.push_back(const_cast(sys_trx_end->field_name), thd->mem_root); + sys_trx_end->flags|= GET_FIXED_FIELDS_FLAG; // needed in handle_list_of_fields() + return false; +} + + +// scan table for min/max sys_trx_end +inline +bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) +{ + uint32 sub_factor= num_subparts ? num_subparts : 1; + uint32 part_id= part->id * sub_factor; + uint32 part_id_end= part_id + sub_factor; + for (; part_id < part_id_end; ++part_id) + { + handler *file= table->file->part_handler(part_id); + int rc= file->ha_external_lock(thd, F_RDLCK); + if (rc) + goto error; + rc= file->ha_rnd_init(true); + if (!rc) + { + while ((rc= file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) + { + if (thd->killed) + { + file->ha_rnd_end(); + return true; + } + if (rc) + { + if (rc == HA_ERR_RECORD_DELETED) + continue; + break; + } + part->stat_trx_end->update(table->vers_end_field()); + } + file->ha_rnd_end(); + } + file->ha_external_lock(thd, F_UNLCK); + if (rc != HA_ERR_END_OF_FILE) + { + error: + my_error(ER_INTERNAL_ERROR, MYF(0), "partition/subpartition scan failed in versioned partitions setup"); + return true; + } + } + return false; +} + + +// setup at open stage (TABLE_SHARE is initialized) +bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) +{ + DBUG_ASSERT(part_type == VERSIONING_PARTITION); + DBUG_ASSERT(vers_info && vers_info->initialized(is_create_table_ind) && vers_info->hist_default != UINT32_MAX); + DBUG_ASSERT(table && table->s); + if (!table->versioned_by_sql()) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table->s->table_name.str, "selected engine is not supported in `BY SYSTEM_TIME` partitioning"); + return true; + } + mysql_mutex_lock(&table->s->LOCK_rotation); + if (table->s->busy_rotation) + { + table->s->vers_wait_rotation(); + vers_hist_part(); + } + else + { + table->s->busy_rotation= true; + mysql_mutex_unlock(&table->s->LOCK_rotation); + // build freelist, scan min/max, assign hist_part + List_iterator it(partitions); + partition_element *el; + while ((el= it++)) + { + DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); + if (el->type == partition_element::VERSIONING) + { + DBUG_ASSERT(!el->stat_trx_end); + el->stat_trx_end= new (&table->mem_root) + Stat_timestampf(table->s->vers_end_field()->field_name, table->s); + if (!is_create_table_ind && vers_scan_min_max(thd, el)) + return true; + } + if (el == vers_info->now_part || el == vers_info->hist_part) + continue; + if (!vers_info->hist_part && el->id == vers_info->hist_default) + { + vers_info->hist_part= el; + } + if (is_create_table_ind || ( + table->s->free_parts_init && + !vers_limit_exceed(el) && + !vers_interval_exceed(el))) + { + table->s->free_parts.push_back((void *) el->id, &table->s->mem_root); + } + } + table->s->hist_part_id= vers_info->hist_part->id; + if (!is_create_table_ind && (vers_limit_exceed() || vers_interval_exceed())) + vers_part_rotate(thd); + table->s->free_parts_init= false; + mysql_mutex_lock(&table->s->LOCK_rotation); + mysql_cond_broadcast(&table->s->COND_rotation); + table->s->busy_rotation= false; + } + mysql_mutex_unlock(&table->s->LOCK_rotation); + return false; +} + /* Check that the partition/subpartition is setup to use the correct @@ -1664,6 +1888,9 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, uint i, tot_partitions; bool result= TRUE, table_engine_set; char *same_name; + uint32 hist_parts= 0; + uint32 now_parts= 0; + const char* hist_default= NULL; DBUG_ENTER("partition_info::check_partition_info"); DBUG_ASSERT(default_engine_type != partition_hton); @@ -1705,7 +1932,8 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } if (unlikely(is_sub_partitioned() && (!(part_type == RANGE_PARTITION || - part_type == LIST_PARTITION)))) + part_type == LIST_PARTITION || + part_type == VERSIONING_PARTITION)))) { /* Only RANGE and LIST partitioning can be subpartitioned */ my_error(ER_SUBPARTITION_ERROR, MYF(0)); @@ -1767,6 +1995,23 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name); goto end; } + + if (part_type == VERSIONING_PARTITION) + { + if (num_parts < 2) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "unexpected number of partitions (expected > 1)"); + goto end; + } + DBUG_ASSERT(vers_info); + if (!vers_info->now_part) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "no `AS OF NOW` partition defined"); + goto end; + } + DBUG_ASSERT(vers_info->initialized(false)); + DBUG_ASSERT(num_parts == partitions.elements); + } i= 0; { List_iterator part_it(partitions); @@ -1847,6 +2092,25 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } } } + if (part_type == VERSIONING_PARTITION) + { + if (part_elem->type == partition_element::VERSIONING) + { + hist_parts++; + if (vers_info->hist_default == UINT32_MAX) + { + vers_info->hist_default= part_elem->id; + hist_default= part_elem->partition_name; + } + if (vers_info->hist_default == part_elem->id) + vers_info->hist_part= part_elem; + } + else + { + DBUG_ASSERT(part_elem->type == partition_element::AS_OF_NOW); + now_parts++; + } + } } while (++i < num_parts); if (!table_engine_set && num_parts_not_set != 0 && @@ -1884,6 +2148,31 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, check_list_constants(thd)))) goto end; } + + if (hist_parts > 1) + { + if (vers_info->limit == 0 && vers_info->interval == 0) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PARAMETERS, + ER_THD(thd, WARN_VERS_PARAMETERS), + "no rotation condition for multiple `VERSIONING` partitions."); + } + if (hist_default) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PARAMETERS, + "No `DEFAULT` for `VERSIONING` partitions. Setting `%s` as default.", + hist_default); + } + } + if (now_parts > 1) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "multiple `AS OF NOW` partitions"); + goto end; + } result= FALSE; end: DBUG_RETURN(result); diff --git a/sql/partition_info.h b/sql/partition_info.h index 66579be638410..61cbd5c14fe73 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -34,6 +34,46 @@ typedef int (*get_subpart_id_func)(partition_info *part_info, struct st_ddl_log_memory_entry; +struct Vers_part_info : public Sql_alloc +{ + Vers_part_info() : + interval(0), + limit(0), + now_part(NULL), + hist_part(NULL), + hist_default(UINT32_MAX) + { + } + Vers_part_info(Vers_part_info &src) : + interval(src.interval), + limit(src.limit), + now_part(NULL), + hist_part(NULL), + hist_default(src.hist_default) + { + } + bool initialized(bool fully= true) + { + if (now_part) + { + DBUG_ASSERT( + now_part->id != UINT32_MAX && + now_part->type == partition_element::AS_OF_NOW && + (fully ? (bool) hist_part : true) && + (!hist_part || ( + hist_part->id != UINT32_MAX && + hist_part->type == partition_element::VERSIONING))); + return true; + } + return false; + } + my_time_t interval; + ulonglong limit; + partition_element *now_part; + partition_element *hist_part; + uint32 hist_default; +}; + class partition_info : public Sql_alloc { public: @@ -142,6 +182,7 @@ class partition_info : public Sql_alloc LIST_PART_ENTRY *list_array; part_column_list_val *range_col_array; part_column_list_val *list_col_array; + Vers_part_info *vers_info; }; /******************************************** @@ -390,6 +431,74 @@ class partition_info : public Sql_alloc bool is_full_part_expr_in_fields(List &fields); public: bool has_unique_name(partition_element *element); + + bool vers_init_info(THD *thd); + bool vers_set_interval(const INTERVAL &i); + bool vers_set_limit(ulonglong limit); + partition_element* vers_part_rotate(THD *thd); + bool vers_setup_1(THD *thd); + bool vers_setup_2(THD *thd, bool is_create_table_ind); + bool vers_scan_min_max(THD *thd, partition_element *part); + + partition_element *vers_hist_part() + { + DBUG_ASSERT(table && table->s); + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(table->s->hist_part_id != UINT32_MAX); + if (table->s->hist_part_id == vers_info->hist_part->id) + return vers_info->hist_part; + + List_iterator it(partitions); + partition_element *el; + while ((el= it++)) + { + DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); + if (el->type == partition_element::VERSIONING && + el->id == table->s->hist_part_id) + { + vers_info->hist_part= el; + return vers_info->hist_part; + } + } + DBUG_ASSERT(0); + return NULL; + } + bool vers_limit_exceed(partition_element *part= NULL) + { + DBUG_ASSERT(vers_info); + if (!vers_info->limit) + return false; + if (!part) + { + DBUG_ASSERT(vers_info->initialized()); + part= vers_hist_part(); + } + // TODO: cache thread-shared part_recs and increment on INSERT + return table->file->part_recs_slow(part) >= vers_info->limit; + } + bool vers_interval_exceed(my_time_t max_time, partition_element *part= NULL) + { + DBUG_ASSERT(vers_info); + if (!vers_info->interval) + return false; + if (!part) + { + DBUG_ASSERT(vers_info->initialized()); + part= vers_hist_part(); + } + DBUG_ASSERT(part->stat_trx_end); + max_time-= part->stat_trx_end->min_time(); + return max_time > vers_info->interval; + } + bool vers_interval_exceed(partition_element *part) + { + DBUG_ASSERT(part->stat_trx_end); + return vers_interval_exceed(part->stat_trx_end->max_time(), part); + } + bool vers_interval_exceed() + { + return vers_interval_exceed(vers_hist_part()); + } }; uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index f8792939c7c46..03086f32700e7 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -5716,9 +5716,9 @@ ER_TOO_MANY_PARTITIONS_ERROR ger "Es wurden zu vielen Partitionen (einschließlich Unterpartitionen) definiert" swe "För många partitioner (inkluderande subpartitioner) definierades" ER_SUBPARTITION_ERROR - eng "It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning" - ger "RANGE/LIST-Partitionierung kann bei Unterpartitionen nur zusammen mit HASH/KEY-Partitionierung verwendet werden" - swe "Det är endast möjligt att blanda RANGE/LIST partitionering med HASH/KEY partitionering för subpartitionering" + eng "It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning" + ger "RANGE/LIST-Partitionierung kann bei Unterpartitionen nur zusammen mit HASH/KEY/SYSTEM_TIME-Partitionierung verwendet werden" + swe "Det är endast möjligt att blanda RANGE/LIST/SYSTEM_TIME partitionering med HASH/KEY partitionering för subpartitionering" ER_CANT_CREATE_HANDLER_FILE eng "Failed to create specific handler file" ger "Erzeugen einer spezifischen Handler-Datei fehlgeschlagen" @@ -7247,7 +7247,7 @@ ER_VERS_FIELD_WRONG_TYPE eng "%`s must be of type %`s for versioned table %`s" ER_VERS_WRONG_PARAMS - eng "Wrong parameters for versioned table %`s: %s" + eng "Wrong parameters for %`s: %s" ER_VERS_ENGINE_UNSUPPORTED eng "Engine does not support System Versioning for %`s" @@ -7257,3 +7257,15 @@ ER_VERS_RANGE_UNITS_MISMATCH ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY eng "Attempt to read unversioned field %`s in historical query" + +ER_PARTITION_WRONG_TYPE + eng "Wrong partition type, expected type: %`s" + +WARN_VERS_PART_FULL + eng "Using full partition %`s, need more VERSIONING partitions!" + +WARN_VERS_PARAMETERS + eng "Maybe missing parameters: %s" + +WARN_VERS_PART_ROTATION + eng "Switching from partition %`s to %`s" diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index e1516f251f4c1..43c273ecfd7f9 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -221,7 +221,8 @@ int TABLE::delete_row() else { store_record(this, record[1]); - vers_end_field()->set_time(); + Field *sys_trx_end= vers_end_field(); + sys_trx_end->set_time(); error= file->ha_update_row(record[1], record[0]); } return error; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 324eef09854df..fcc930becd3ff 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -87,7 +87,8 @@ const LEX_STRING partition_keywords[]= { { C_STRING_WITH_LEN("HASH") }, { C_STRING_WITH_LEN("RANGE") }, - { C_STRING_WITH_LEN("LIST") }, + { C_STRING_WITH_LEN("LIST") }, + { C_STRING_WITH_LEN("SYSTEM_TIME") }, { C_STRING_WITH_LEN("KEY") }, { C_STRING_WITH_LEN("MAXVALUE") }, { C_STRING_WITH_LEN("LINEAR ") }, @@ -108,6 +109,7 @@ static int get_partition_id_list_col(partition_info *, uint32 *, longlong *); static int get_partition_id_list(partition_info *, uint32 *, longlong *); static int get_partition_id_range_col(partition_info *, uint32 *, longlong *); static int get_partition_id_range(partition_info *, uint32 *, longlong *); +static int vers_get_partition_id(partition_info *, uint32 *, longlong *); static int get_part_id_charset_func_part(partition_info *, uint32 *, longlong *); static int get_part_id_charset_func_subpart(partition_info *, uint32 *); static int get_partition_id_hash_nosub(partition_info *, uint32 *, longlong *); @@ -1317,6 +1319,24 @@ static void set_up_partition_func_pointers(partition_info *part_info) part_info->get_subpartition_id= get_partition_id_hash_sub; } } + else if (part_info->part_type == VERSIONING_PARTITION) + { + part_info->get_part_partition_id= vers_get_partition_id; + if (part_info->list_of_subpart_fields) + { + if (part_info->linear_hash_ind) + part_info->get_subpartition_id= get_partition_id_linear_key_sub; + else + part_info->get_subpartition_id= get_partition_id_key_sub; + } + else + { + if (part_info->linear_hash_ind) + part_info->get_subpartition_id= get_partition_id_linear_hash_sub; + else + part_info->get_subpartition_id= get_partition_id_hash_sub; + } + } else /* LIST Partitioning */ { if (part_info->column_list) @@ -1357,6 +1377,10 @@ static void set_up_partition_func_pointers(partition_info *part_info) else part_info->get_partition_id= get_partition_id_list; } + else if (part_info->part_type == VERSIONING_PARTITION) + { + part_info->get_partition_id= vers_get_partition_id; + } else /* HASH partitioning */ { if (part_info->list_of_part_fields) @@ -1629,6 +1653,7 @@ bool fix_partition_func(THD *thd, TABLE *table, } } DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION); + DBUG_ASSERT(part_info->part_type != VERSIONING_PARTITION || part_info->column_list); /* Partition is defined. We need to verify that partitioning function is correct. @@ -1661,6 +1686,9 @@ bool fix_partition_func(THD *thd, TABLE *table, const char *error_str; if (part_info->column_list) { + if (part_info->part_type == VERSIONING_PARTITION && + part_info->vers_setup_1(thd)) + goto end; List_iterator it(part_info->part_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE))) goto end; @@ -1684,6 +1712,10 @@ bool fix_partition_func(THD *thd, TABLE *table, if (unlikely(part_info->check_list_constants(thd))) goto end; } + else if (part_info->part_type == VERSIONING_PARTITION) + { + error_str= partition_keywords[PKW_SYSTEM_TIME].str; + } else { DBUG_ASSERT(0); @@ -2354,6 +2386,23 @@ static int add_partition_values(File fptr, partition_info *part_info, } while (++i < num_items); err+= add_end_parenthesis(fptr); } + else if (part_info->part_type == VERSIONING_PARTITION) + { + switch (p_elem->type) + { + case partition_element::AS_OF_NOW: + err+= add_string(fptr, " AS OF NOW"); + break; + case partition_element::VERSIONING: + err+= add_string(fptr, " VERSIONING"); + DBUG_ASSERT(part_info->vers_info); + if (part_info->vers_info->hist_default == p_elem->id) + err+= add_string(fptr, " DEFAULT"); + break; + default: + DBUG_ASSERT(0 && "wrong p_elem->type"); + } + } end: return err; } @@ -2496,13 +2545,32 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info, else err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str); break; + case VERSIONING_PARTITION: + err+= add_part_key_word(fptr, partition_keywords[PKW_SYSTEM_TIME].str); + break; default: DBUG_ASSERT(0); /* We really shouldn't get here, no use in continuing from here */ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); DBUG_RETURN(NULL); } - if (part_info->part_expr) + if (part_info->part_type == VERSIONING_PARTITION) + { + Vers_part_info *vers_info= part_info->vers_info; + DBUG_ASSERT(vers_info); + if (vers_info->interval) + { + err+= add_string(fptr, "INTERVAL "); + err+= add_int(fptr, vers_info->interval); + err+= add_string(fptr, " SECOND "); + } + if (vers_info->limit) + { + err+= add_string(fptr, "LIMIT "); + err+= add_int(fptr, vers_info->limit); + } + } + else if (part_info->part_expr) { err+= add_begin_parenthesis(fptr); err+= add_string_len(fptr, part_info->part_func_string, @@ -3349,6 +3417,73 @@ int get_partition_id_range_col(partition_info *part_info, } +int vers_get_partition_id(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + DBUG_ENTER("vers_get_partition_id"); + Field *sys_trx_end= part_info->part_field_array[0]; + DBUG_ASSERT(sys_trx_end); + DBUG_ASSERT(part_info->table); + Vers_part_info *vers_info= part_info->vers_info; + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(sys_trx_end->table == part_info->table && part_info->table->versioned()); + DBUG_ASSERT(part_info->table->vers_end_field() == sys_trx_end); + + // new rows have NULL in sys_trx_end + if (sys_trx_end->is_max() || sys_trx_end->is_null()) + { + *part_id= vers_info->now_part->id; + } + else // row is historical + { + partition_element *part= vers_info->hist_part; + THD *thd= current_thd; + TABLE *table= part_info->table; + + switch (thd->lex->sql_command) + { + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_ALTER_TABLE: + mysql_mutex_lock(&table->s->LOCK_rotation); + if (table->s->busy_rotation) + { + table->s->vers_wait_rotation(); + part_info->vers_hist_part(); + } + else + { + table->s->busy_rotation= true; + mysql_mutex_unlock(&table->s->LOCK_rotation); + if (part_info->vers_limit_exceed() || part_info->vers_interval_exceed(sys_trx_end->get_timestamp())) + { + part= part_info->vers_part_rotate(thd); + } + mysql_mutex_lock(&table->s->LOCK_rotation); + mysql_cond_broadcast(&table->s->COND_rotation); + table->s->busy_rotation= false; + } + mysql_mutex_unlock(&table->s->LOCK_rotation); + if (vers_info->interval) + { + DBUG_ASSERT(part->stat_trx_end); + part->stat_trx_end->update(sys_trx_end); + } + break; + default: + ; + } + *part_id= vers_info->hist_part->id; + } + + DBUG_PRINT("exit",("partition: %d", *part_id)); + DBUG_RETURN(0); +} + + int get_partition_id_range(partition_info *part_info, uint32 *part_id, longlong *func_value) @@ -4953,7 +5088,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, must know the number of new partitions in this case. */ if (thd->lex->no_write_to_binlog && - tab_part_info->part_type != HASH_PARTITION) + tab_part_info->part_type != HASH_PARTITION && + tab_part_info->part_type != VERSIONING_PARTITION) { my_error(ER_NO_BINLOG_ERROR, MYF(0)); goto err; @@ -5205,16 +5341,27 @@ that are reorganised. List_iterator part_it(tab_part_info->partitions); tab_part_info->is_auto_partitioned= FALSE; - if (!(tab_part_info->part_type == RANGE_PARTITION || - tab_part_info->part_type == LIST_PARTITION)) + if (tab_part_info->part_type == VERSIONING_PARTITION) { - my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); - goto err; + if (num_parts_dropped >= tab_part_info->num_parts - 1) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "one `AS OF NOW` and at least one `VERSIONING` partition required"); + goto err; + } } - if (num_parts_dropped >= tab_part_info->num_parts) + else { - my_error(ER_DROP_LAST_PARTITION, MYF(0)); - goto err; + if (!(tab_part_info->part_type == RANGE_PARTITION || + tab_part_info->part_type == LIST_PARTITION)) + { + my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); + goto err; + } + if (num_parts_dropped >= tab_part_info->num_parts) + { + my_error(ER_DROP_LAST_PARTITION, MYF(0)); + goto err; + } } do { @@ -5222,6 +5369,11 @@ that are reorganised. if (is_name_in_list(part_elem->partition_name, alter_info->partition_names)) { + if (part_elem->type == partition_element::AS_OF_NOW) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "`AS OF NOW` partition can not be dropped"); + goto err; + } /* Set state to indicate that the partition is to be dropped. */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 5ca353d3b9854..27ee384a4a8c8 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6893,6 +6893,11 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, partition_keywords[PKW_HASH].length); table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs); break; + case VERSIONING_PARTITION: + tmp_res.length(0); + tmp_res.append(partition_keywords[PKW_SYSTEM_TIME].str, + partition_keywords[PKW_SYSTEM_TIME].length); + break; default: DBUG_ASSERT(0); my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 64d0b6c9868b6..5d0c7dc74e402 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1705,7 +1705,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ %token VAR_SAMP_SYM -%token VERSIONING /* 32N2439 */ +%token VERSIONING_SYM /* 32N2439 */ %token VIA_SYM %token VIEW_SYM /* SQL-2003-N */ %token VIRTUAL_SYM @@ -4974,6 +4974,10 @@ part_type_def: { Lex->part_info->part_type= LIST_PARTITION; } | LIST_SYM part_column_list { Lex->part_info->part_type= LIST_PARTITION; } + | SYSTEM_TIME_SYM + { if (Lex->part_info->vers_init_info(thd)) MYSQL_YYABORT; } + opt_versioning_interval + opt_versioning_limit ; opt_linear: @@ -5181,6 +5185,7 @@ part_definition: MYSQL_YYABORT; } p_elem->part_state= PART_NORMAL; + p_elem->id= part_info->partitions.elements - 1; part_info->curr_part_elem= p_elem; part_info->current_partition= p_elem; part_info->use_default_partitions= FALSE; @@ -5247,6 +5252,42 @@ opt_part_values: part_info->part_type= LIST_PARTITION; } part_values_in {} + | AS OF_SYM NOW_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != VERSIONING_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_TYPE, MYF(0), + "BY SYSTEM_TIME")); + } + else + { + part_info->vers_init_info(thd); + } + partition_element *elem= part_info->curr_part_elem; + elem->type= partition_element::AS_OF_NOW; + DBUG_ASSERT(part_info->vers_info); + part_info->vers_info->now_part= elem; + } + | VERSIONING_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != VERSIONING_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_TYPE, MYF(0), + "BY SYSTEM_TIME")); + } + else + { + part_info->vers_init_info(thd); + } + part_info->curr_part_elem->type= partition_element::VERSIONING; + } + opt_default_hist_part | DEFAULT { LEX *lex= Lex; @@ -5492,6 +5533,7 @@ sub_part_definition: mem_alloc_error(sizeof(partition_element)); MYSQL_YYABORT; } + sub_p_elem->id= curr_part->subpartitions.elements - 1; part_info->curr_part_elem= sub_p_elem; part_info->use_default_subpartitions= FALSE; part_info->use_default_num_subpartitions= FALSE; @@ -5544,6 +5586,45 @@ opt_part_option: { Lex->part_info->curr_part_elem->part_comment= $3.str; } ; +opt_versioning_interval: + /* empty */ {} + | INTERVAL_SYM expr interval + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); + INTERVAL interval; + if (get_interval_value($2, $3, &interval)) + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "wrong INTERVAL value")); + if (part_info->vers_set_interval(interval)) + MYSQL_YYABORT; + } + ; + +opt_versioning_limit: + /* empty */ {} + | LIMIT ulonglong_num + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); + if (part_info->vers_set_limit($2)) + MYSQL_YYABORT; + } + ; + +opt_default_hist_part: + /* empty */ {} + | DEFAULT + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info && part_info->vers_info && part_info->curr_part_elem); + if (part_info->vers_info->hist_part) + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), + "BY SYSTEM_TIME", "multiple `DEFAULT` partitions")); + part_info->vers_info->hist_part= part_info->curr_part_elem; + part_info->vers_info->hist_default= part_info->curr_part_elem->id; + } + ; + /* End of partition parser part */ @@ -5906,7 +5987,7 @@ create_table_option: engine_option_value($1, &Lex->create_info.option_list, &Lex->option_list_last); } - | WITH_SYSTEM_SYM VERSIONING + | WITH_SYSTEM_SYM VERSIONING_SYM { const char *table_name= Lex->create_last_non_select_table->table_name; @@ -5920,7 +6001,7 @@ create_table_option: info.declared_with_system_versioning= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } - | WITHOUT SYSTEM VERSIONING + | WITHOUT SYSTEM VERSIONING_SYM { const char *table_name= Lex->create_last_non_select_table->table_name; @@ -6139,10 +6220,9 @@ period_for_system_time: Vers_parse_info &info= Lex->vers_get_info(); if (!my_strcasecmp(system_charset_info, $4->c_ptr(), $6->c_ptr())) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), Lex->create_last_non_select_table->table_name, - "'PERIOD FOR SYSTEM_TIME' columns must be different"); - MYSQL_YYABORT; + "'PERIOD FOR SYSTEM_TIME' columns must be different")); } info.set_period_for_system_time($4, $6); } @@ -6247,8 +6327,7 @@ field_def: } if (*p) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, err); - MYSQL_YYABORT; + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), table_name, err)); } *p= field_name; } @@ -6726,7 +6805,7 @@ attribute: new (thd->mem_root) engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); } - | WITH_SYSTEM_SYM VERSIONING + | WITH_SYSTEM_SYM VERSIONING_SYM { if (Lex->last_field->versioning != Column_definition::VERSIONING_NOT_SET) @@ -6739,7 +6818,7 @@ attribute: Lex->create_info.vers_info.has_versioned_fields= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } - | WITHOUT SYSTEM VERSIONING + | WITHOUT SYSTEM VERSIONING_SYM { if (Lex->last_field->versioning != Column_definition::VERSIONING_NOT_SET) diff --git a/sql/table.cc b/sql/table.cc index ebe8b25cdda15..9e3b57183b210 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -422,6 +422,9 @@ void TABLE_SHARE::destroy() DBUG_ENTER("TABLE_SHARE::destroy"); DBUG_PRINT("info", ("db: %s table: %s", db.str, table_name.str)); + if (versioned) + vers_destroy(); + if (ha_share) { delete ha_share; @@ -2369,7 +2372,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, /* Set system versioning information. */ if (system_period == NULL) { - share->disable_system_versioning(); + versioned= false; + row_start_field = 0; + row_end_field = 0; } else { @@ -2378,9 +2383,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint16 row_end= uint2korr(system_period + sizeof(uint16)); if (row_start >= share->fields || row_end >= share->fields) goto err; - DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, - row_end)); - share->enable_system_versioning(row_start, row_end); + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + versioned= true; + vers_init(); + row_start_field = row_start; + row_end_field = row_end; vers_start_field()->flags|= VERS_SYS_START_FLAG; vers_end_field()->flags|= VERS_SYS_END_FLAG; } // if (system_period == NULL) @@ -3403,6 +3410,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str)) outparam->s->cached_row_logging_check= 0; // No row based replication +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (outparam->part_info && + outparam->part_info->part_type == VERSIONING_PARTITION && + outparam->part_info->vers_setup_2(thd, is_create_table)) + { + error= OPEN_FRM_OPEN_ERROR; + error_reported= true; + goto err; + } +#endif + /* Increment the opened_tables counter, only when open flags set. */ if (db_stat) thd->status_var.opened_tables++; diff --git a/sql/table.h b/sql/table.h index f2f0b0d87729c..ad521598c4382 100644 --- a/sql/table.h +++ b/sql/table.h @@ -546,6 +546,9 @@ struct TABLE_STATISTICS_CB bool histograms_are_read; }; +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif /** This structure is shared between different table objects. There is one @@ -727,19 +730,27 @@ struct TABLE_SHARE bool versioned; uint16 row_start_field; uint16 row_end_field; - - void enable_system_versioning(uint16 row_start, uint row_end) + uint32 hist_part_id; + List free_parts; + bool free_parts_init; + bool busy_rotation; + mysql_mutex_t LOCK_rotation; + mysql_cond_t COND_rotation; + + void vers_init() { - versioned = true; - row_start_field = row_start; - row_end_field = row_end; + hist_part_id= UINT32_MAX; + busy_rotation= false; + free_parts.empty(); + free_parts_init= true; + mysql_mutex_init(key_TABLE_SHARE_LOCK_rotation, &LOCK_rotation, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_TABLE_SHARE_COND_rotation, &COND_rotation, NULL); } - void disable_system_versioning() + void vers_destroy() { - versioned = false; - row_start_field = 0; - row_end_field = 0; + mysql_mutex_destroy(&LOCK_rotation); + mysql_cond_destroy(&COND_rotation); } Field *vers_start_field() @@ -752,6 +763,18 @@ struct TABLE_SHARE return field[row_end_field]; } + void vers_part_rotate() + { + DBUG_ASSERT(!free_parts.is_empty()); + hist_part_id= (ulong)(void *)(free_parts.pop()); + } + + void vers_wait_rotation() + { + while (busy_rotation) + mysql_cond_wait(&COND_rotation, &LOCK_rotation); + } + /** Cache the checked structure of this table. @@ -1480,8 +1503,8 @@ struct TABLE /* Versioned by SQL layer */ bool versioned_by_sql() const { - DBUG_ASSERT(s->db_type()); - return s->versioned && !s->db_type()->versioned(); + DBUG_ASSERT(file); + return s->versioned && !file->versioned(); } Field *vers_start_field() const diff --git a/storage/tokudb/mysql-test/tokudb_parts/r/partition_syntax_tokudb.result b/storage/tokudb/mysql-test/tokudb_parts/r/partition_syntax_tokudb.result index 13b8a13362831..bdd7a1d78e963 100644 --- a/storage/tokudb/mysql-test/tokudb_parts/r/partition_syntax_tokudb.result +++ b/storage/tokudb/mysql-test/tokudb_parts/r/partition_syntax_tokudb.result @@ -510,7 +510,7 @@ f_charbig VARCHAR(1000) ) PARTITION BY RANGE(f_int1) ( PARTITION part1 VALUES LESS THAN (1000) (SUBPARTITION subpart11)); -ERROR HY000: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning +ERROR HY000: It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning #------------------------------------------------------------------------ # 2.2 Every partition must have the same number of subpartitions. # This is a limitation of MySQL 5.1, which could be removed in