diff --git a/include/my_time.h b/include/my_time.h index 90a8885a293b3..c1c5d76ed8405 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -279,6 +279,41 @@ enum interval_type INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND, INTERVAL_LAST }; +typedef struct my_timespec +{ + my_time_t sec; + ulong usec; +} my_timespec_t; + +static inline +int cmp(my_timespec_t a, my_timespec_t b) +{ + return ((a.sec > b.sec || (a.sec == b.sec && a.usec > b.usec)) ? 1 : + ((a.sec < b.sec || (a.sec == b.sec && a.usec < b.usec)) ? -1 : 0)); +} + C_MODE_END +#ifdef __cplusplus +constexpr my_timespec_t MY_TIMESPEC_MIN= {MY_TIME_T_MIN, 0}; +constexpr my_timespec_t MY_TIMESPEC_MAX= {TIMESTAMP_MAX_VALUE, TIME_MAX_SECOND_PART}; + +inline +bool operator< (my_timespec_t a, my_timespec_t b) +{ + return cmp(a, b) < 0; +} + +inline +bool operator> (my_timespec_t a, my_timespec_t b) +{ + return cmp(a, b) > 0; +} + +inline +bool operator== (my_timespec_t a, my_timespec_t b) +{ + return (a.sec == b.sec) && (a.usec == b.usec); +} +#endif #endif /* _my_time_h_ */ diff --git a/mysql-test/main/partition.result b/mysql-test/main/partition.result index bfe5d07b1d99f..93ff867f39e63 100644 --- a/mysql-test/main/partition.result +++ b/mysql-test/main/partition.result @@ -2921,3 +2921,18 @@ DROP TABLE mdev20498; # # End of 10.6 tests # +# +# MDEV-25529 ALTER TABLE FORCE syntax improved +# +create table t1 (id int primary key); +alter table t1 force partition by hash(id) partitions 2; +alter table t1 force remove partitioning; +alter table t1 partition by hash(id) partitions 2 force; +alter table t1 remove partitioning force; +alter table t1 force partition by hash(id) partitions 2 force; +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 'force' at line 1 +alter table t1 force remove partitioning force; +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 'force' at line 1 +alter table t1 add column x int partition by hash(id) partitions 2; +alter table t1 remove partitioning add column y int; +drop table t1; diff --git a/mysql-test/main/partition.test b/mysql-test/main/partition.test index da0109913305a..0c0d2b30c3ad0 100644 --- a/mysql-test/main/partition.test +++ b/mysql-test/main/partition.test @@ -3144,3 +3144,20 @@ DROP TABLE mdev20498; --echo # --echo # End of 10.6 tests --echo # + +--echo # +--echo # MDEV-25529 ALTER TABLE FORCE syntax improved +--echo # +create table t1 (id int primary key); +alter table t1 force partition by hash(id) partitions 2; +alter table t1 force remove partitioning; +alter table t1 partition by hash(id) partitions 2 force; +alter table t1 remove partitioning force; +--error ER_PARSE_ERROR +alter table t1 force partition by hash(id) partitions 2 force; +--error ER_PARSE_ERROR +alter table t1 force remove partitioning force; +# Strange syntax, but it exists since 2005 (cd483c55) +alter table t1 add column x int partition by hash(id) partitions 2; +alter table t1 remove partitioning add column y int; +drop table t1; diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 2d408c170f4ce..67c53bca5bdd7 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -484,7 +484,7 @@ PARTITIONS 2 create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts '2000-01-01 00:00:01'; Warnings: -Warning 4164 `t1`: STARTS is later than query time, first history partition may exceed INTERVAL value +Warning 4164 `t1`: STARTS timestamp 2000-01-01 00:00:01 is later than query timestamp 2000-01-01 00:00:00, first history partition may exceed INTERVAL value # Test default STARTS rounding set timestamp= unix_timestamp('1999-12-15 13:33:33'); create or replace table t1 (i int) with system versioning @@ -556,7 +556,7 @@ set time_zone="+03: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'; Warnings: -Warning 4164 `t1`: STARTS is later than query time, first history partition may exceed INTERVAL value +Warning 4164 `t1`: STARTS timestamp 2000-01-01 00:00:00 is later than query timestamp 1999-12-15 16:33:33, first history partition may exceed INTERVAL value set timestamp= unix_timestamp('2000-01-01 00:00:00'); create or replace table t2 (i int) with system versioning partition by system_time interval 1 day; @@ -616,7 +616,7 @@ create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts '2000-01-03 00:00:00' partitions 3; Warnings: -Warning 4164 `t1`: STARTS is later than query time, first history partition may exceed INTERVAL value +Warning 4164 `t1`: STARTS timestamp 2000-01-03 00:00:00 is later than query timestamp 2000-01-01 00:00:00, first history partition may exceed INTERVAL value insert into t1 values (0); set timestamp= unix_timestamp('2000-01-01 00:00:01'); update t1 set i= i + 1; diff --git a/mysql-test/suite/versioning/r/partition2.result b/mysql-test/suite/versioning/r/partition2.result new file mode 100644 index 0000000000000..1b452a4c5c861 --- /dev/null +++ b/mysql-test/suite/versioning/r/partition2.result @@ -0,0 +1,533 @@ +set @@session.time_zone='+00:00'; +# +# MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER +# +set @old_dbug= @@debug_dbug; +set debug_dbug= '+d,test_mdev-25529'; +create or replace table t1 (x int) with system versioning +partition by system_time limit 1; +alter table t1 partition by system_time limit 1 auto; +create or replace table t1 (x int, +row_start timestamp(6) as row start invisible, +row_end timestamp(6) as row end invisible, +period for system_time (row_start, row_end), +index (row_end, row_start), +index (row_start), +index (row_end, x), +index (x, row_end), +index (row_end desc)) with system versioning +partition by system_time limit 1000 partitions 6; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME LIMIT 1000 +PARTITIONS 6 +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (0); +set timestamp= unix_timestamp('2000-01-01 00:10:00.22'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:00:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:30:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 02:00:00'); +update t1 set x= x + 1; +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +select *, row_start, row_end from t1 partition (p3); +x row_start row_end +select *, row_start, row_end from t1 partition (p4); +x row_start row_end +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 +set timestamp= default; +ALTER table t1 partition by system_time +interval 1 hour starts '2000-01-01 00:00:00'; +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p3); +x row_start row_end +select *, row_start, row_end from t1 partition (p4); +x row_start row_end +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 +alter TABLE t1 partition by system_time +interval 1 hour; +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p3); +x row_start row_end +select *, row_start, row_end from t1 partition (p4); +x row_start row_end +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 +set @@system_versioning_alter_history= keep; +alter table t1 remove partitioning; +select *, row_start, row_end from t1 for system_time all; +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 +alter table t1 PARTITION by system_time interval 1 hour auto; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 3 +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour starts '2000-01-01 00:00:00' auto; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 3 +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour auto; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 29 +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p27); +x row_start row_end +4 2000-01-01 02:00:00.000000 2000-01-02 03:01:23.456000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 partition by system_time INTERVAL 20 minute auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 20 MINUTE STARTS TIMESTAMP'2000-01-01 00:10:00' AUTO +PARTITIONS 82 +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +select *, row_start, row_end from t1 partition (p5); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +alter table t1 remove partitioning; +alter table t1 partition by system_time interval 1 hour +starts '2002-01-01 00:00:00' auto; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2002-01-01 00:00:00 is later than query timestamp 2000-01-02 03:01:23, first history partition may exceed INTERVAL value +Warning 4193 `t1`: STARTS timestamp 2002-01-01 00:00:00 is above earliest history date 2000-01-01 00:10:00 and was set to 2000-01-01 00:00:00 +# STARTS changed and WARN_VERS_WRONG_STARTS is here +alter table t1 force partition by system_time interval 1 hour +starts '2003-01-01 00:00:00' auto; +Warnings: +Warning 4193 `t1`: STARTS timestamp 2003-01-01 00:00:00 is above earliest history date 2000-01-01 00:10:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 29 +alter table t1 partition by system_time interval 1 hour +starts '2004-01-01 00:00:00' auto force; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2004-01-01 00:00:00 is later than query timestamp 2000-01-02 03:01:23, first history partition may exceed INTERVAL value +Warning 4193 `t1`: STARTS timestamp 2004-01-01 00:00:00 is above earliest history date 2000-01-01 00:10:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 29 +# STARTS doesn't change without FORCE +alter table t1 partition by system_time interval 1 hour +starts '2005-01-01 00:00:00' auto; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2005-01-01 00:00:00 is later than query timestamp 2000-01-02 03:01:23, first history partition may exceed INTERVAL value +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2005-01-01 00:00:00' AUTO +PARTITIONS 29 +# min_ts == max_ts case, partitions decrease +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +alter table t1 partition by system_time interval 7 hour auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 7 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 2 +# Explicit partition list ignores AUTO FORCE +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 8 hour auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 8 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 7 +alter table t1 partition by system_time interval 8 hour auto ( +partition p0 history, +partition p1 history, +partition pn current) force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 8 HOUR STARTS TIMESTAMP'2000-01-03 00:00:00' AUTO +(PARTITION `p0` HISTORY ENGINE = MyISAM, + PARTITION `p1` HISTORY ENGINE = MyISAM, + PARTITION `pn` CURRENT ENGINE = MyISAM) +# Switch system_time -> hash +alter table t1 partition by hash(x) partitions 3; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY HASH (`x`) +PARTITIONS 3 +# Switch hash -> system_time +alter table t1 partition by system_time interval 9 hour +starts '2001-01-01 00:00:00' auto force; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2001-01-01 00:00:00 is later than query timestamp 2000-01-03 00:00:00, first history partition may exceed INTERVAL value +Warning 4193 `t1`: STARTS timestamp 2001-01-01 00:00:00 is above earliest history date 2000-01-01 00:00:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 9 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 7 +# Year/month interval +delete history from t1; +alter table t1 remove partitioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +alter table t1 partition by system_time interval 1 month auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 MONTH STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 2 +set timestamp= unix_timestamp('2003-10-01 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 11 month auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 11 MONTH STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 6 +alter table t1 partition by system_time interval 1 year auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 YEAR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 5 +# No history +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 5 +insert t1 values (99); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end` DESC), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 5 +# Slow scan warning +create or replace table t1 (x int, +row_start timestamp(6) as row start invisible, +row_end timestamp(6) as row end invisible, +period for system_time (row_start, row_end), +index (x, row_end, row_start), +index (row_start, row_end), +index (x, row_end)) with system versioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 10 hour auto; +Warnings: +Warning 4194 `t1`: ROW END index not found, using slow scan +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `x` (`x`,`row_end`,`row_start`), + KEY `row_start` (`row_start`,`row_end`), + KEY `x_2` (`x`,`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 10 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 6 +# TRX_ID versioning +create or replace table t1 (x int, +row_start bigint(20) unsigned as row start invisible, +row_end bigint(20) unsigned as row end invisible, +period for system_time (row_start, row_end), +index (row_end)) with system versioning engine innodb; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +# Partitioning is not allowed for TRX_ID versioning +alter table t1 partition by system_time interval 11 hour auto; +ERROR HY000: `row_start` must be of type TIMESTAMP(6) for system-versioned table `t1` +drop table t1; +set debug_dbug= @old_dbug; diff --git a/mysql-test/suite/versioning/t/partition2.test b/mysql-test/suite/versioning/t/partition2.test new file mode 100644 index 0000000000000..cdfab219c1b22 --- /dev/null +++ b/mysql-test/suite/versioning/t/partition2.test @@ -0,0 +1,223 @@ +--source include/have_partition.inc +--source include/have_innodb.inc +--source include/maybe_debug.inc + +set @@session.time_zone='+00:00'; + +--echo # +--echo # MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER +--echo # + +# Test both index and scan, compare the results between them +if ($have_debug) +{ + set @old_dbug= @@debug_dbug; + set debug_dbug= '+d,test_mdev-25529'; +} + +create or replace table t1 (x int) with system versioning +partition by system_time limit 1; +alter table t1 partition by system_time limit 1 auto; + +create or replace table t1 (x int, + row_start timestamp(6) as row start invisible, + row_end timestamp(6) as row end invisible, + period for system_time (row_start, row_end), + index (row_end, row_start), + index (row_start), + index (row_end, x), + index (x, row_end), + index (row_end desc)) with system versioning +partition by system_time limit 1000 partitions 6; + +show create table t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (0); +set timestamp= unix_timestamp('2000-01-01 00:10:00.22'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:00:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:30:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 02:00:00'); +update t1 set x= x + 1; + +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p3); +select *, row_start, row_end from t1 partition (p4); +select *, row_start, row_end from t1 partition (pn); +set timestamp= default; + +ALTER table t1 partition by system_time +interval 1 hour starts '2000-01-01 00:00:00'; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p3); +select *, row_start, row_end from t1 partition (p4); +select *, row_start, row_end from t1 partition (pn); + +alter TABLE t1 partition by system_time +interval 1 hour; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p3); +select *, row_start, row_end from t1 partition (p4); +select *, row_start, row_end from t1 partition (pn); + +set @@system_versioning_alter_history= keep; +alter table t1 remove partitioning; +# First history: 2000-01-01 00:10:00.220000 +# Last history: 2000-01-01 02:00:00.00000021 +select *, row_start, row_end from t1 for system_time all; + +alter table t1 PARTITION by system_time interval 1 hour auto; + +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (pn); + +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour starts '2000-01-01 00:00:00' auto; + +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (pn); + +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour auto; + +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p27); +select *, row_start, row_end from t1 partition (pn); + +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 partition by system_time INTERVAL 20 minute auto force; +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p5); +select *, row_start, row_end from t1 partition (pn); + +alter table t1 remove partitioning; +alter table t1 partition by system_time interval 1 hour +starts '2002-01-01 00:00:00' auto; + +--echo # STARTS changed and WARN_VERS_WRONG_STARTS is here +alter table t1 force partition by system_time interval 1 hour +starts '2003-01-01 00:00:00' auto; +show create table t1; + +alter table t1 partition by system_time interval 1 hour +starts '2004-01-01 00:00:00' auto force; +show create table t1; + +--echo # STARTS doesn't change without FORCE +alter table t1 partition by system_time interval 1 hour +starts '2005-01-01 00:00:00' auto; +show create table t1; + +--echo # min_ts == max_ts case, partitions decrease +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +alter table t1 partition by system_time interval 7 hour auto force; +show create table t1; + +--echo # Explicit partition list ignores AUTO FORCE +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 8 hour auto force; +show create table t1; + +alter table t1 partition by system_time interval 8 hour auto ( + partition p0 history, + partition p1 history, + partition pn current) force; +show create table t1; + +--echo # Switch system_time -> hash +alter table t1 partition by hash(x) partitions 3; +show create table t1; +--echo # Switch hash -> system_time +alter table t1 partition by system_time interval 9 hour +starts '2001-01-01 00:00:00' auto force; +show create table t1; + +--echo # Year/month interval +delete history from t1; +alter table t1 remove partitioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +alter table t1 partition by system_time interval 1 month auto force; +show create table t1; +set timestamp= unix_timestamp('2003-10-01 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 11 month auto force; +show create table t1; +alter table t1 partition by system_time interval 1 year auto force; +show create table t1; + +--echo # No history +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; +insert t1 values (99); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; + +--echo # Slow scan warning +create or replace table t1 (x int, + row_start timestamp(6) as row start invisible, + row_end timestamp(6) as row end invisible, + period for system_time (row_start, row_end), + index (x, row_end, row_start), + index (row_start, row_end), + index (x, row_end)) with system versioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 10 hour auto; +show create table t1; + +--echo # TRX_ID versioning +create or replace table t1 (x int, + row_start bigint(20) unsigned as row start invisible, + row_end bigint(20) unsigned as row end invisible, + period for system_time (row_start, row_end), + index (row_end)) with system versioning engine innodb; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +--echo # Partitioning is not allowed for TRX_ID versioning +--error ER_VERS_FIELD_WRONG_TYPE +alter table t1 partition by system_time interval 11 hour auto; + +drop table t1; + +if ($have_debug) +{ + set debug_dbug= @old_dbug; +} diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 4fdfad925a760..ac456c77c110f 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -689,6 +689,10 @@ add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone, /* Computes the sum of a timestamp plus interval. + Computes the smallest next > time_now obtained by adding one or more + multiples of the given interval (i_value, i_type) to start, taking + into account time_zone conversions and DST ambiguities. + SYNOPSIS get_next_time() time_zone event time zone diff --git a/sql/field.cc b/sql/field.cc index 992429cb5b682..0a6eb44e0f5e7 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2372,6 +2372,32 @@ bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulong } +my_time_t Field_vers_trx_id::get_timestamp(const uchar *pos, ulong *sec_part) const +{ + ulonglong trx_id= uint8korr(pos); + THD *thd= get_thd(); + if (trx_id == ULONGLONG_MAX) + { + if (sec_part) + { + *sec_part= TIME_MAX_SECOND_PART; + } + return TIMESTAMP_MAX_VALUE; + } + TR_table trt(thd); + if (trt.query(trx_id)) + { + return trt[TR_table::FLD_COMMIT_TS]->get_timestamp(sec_part); + } + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID), + (longlong) trx_id); + + return -1; +} + + Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const LEX_CSTRING *field_name_arg, diff --git a/sql/field.h b/sql/field.h index c31b38d78e623..2526437201318 100644 --- a/sql/field.h +++ b/sql/field.h @@ -2884,6 +2884,7 @@ class Field_vers_trx_id :public Field_longlong { { return get_date(ltime, fuzzydate, (ulonglong) val_int()); } + my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const override; bool test_if_equality_guarantees_uniqueness(const Item *item) const override; Data_type_compatibility can_optimize_keypart_ref(const Item_bool_func *, const Item *) diff --git a/sql/partition_info.cc b/sql/partition_info.cc index fe2dd6f0895f0..f16e017a40b23 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -423,6 +423,7 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file, i= 0; do { + // FIXME (newbie): how is it freed? Explain in comment or remake via table->mem_root. partition_element *part_elem= new partition_element(); if (likely(part_elem != 0 && (!partitions.push_back(part_elem)))) @@ -442,7 +443,10 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file, } } else + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto end; + } } while (++i < num_parts); result= FALSE; end: @@ -2840,6 +2844,7 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, /* 2. assign STARTS to interval.start */ if (starts) { + vers_info->starts_clause= true; if (starts->fix_fields_if_needed_for_scalar(thd, &starts)) return true; switch (starts->result_type()) @@ -2868,30 +2873,21 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, } if (!table) { - if (thd->query_start() < vers_info->interval.start) { + if (thd->query_start() < vers_info->interval.start && + !(auto_hist && (thd->lex->alter_info.flags & ALTER_RECREATE))) + { + TimestampString str_interval(thd, vers_info->interval.start); + TimestampString str_query(thd, thd->query_start()); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_PART_STARTS_BEYOND_INTERVAL, - ER_THD(thd, ER_PART_STARTS_BEYOND_INTERVAL), - table_name); + WARN_VERS_STARTS_BEYOND_INTERVAL, + ER_THD(thd, WARN_VERS_STARTS_BEYOND_INTERVAL), + table_name, str_interval.cstr(), str_query.cstr()); } } } else // calculate default STARTS depending on INTERVAL { - thd->variables.time_zone->gmt_sec_to_TIME(<ime, thd->query_start()); - if (vers_info->interval.step.second) - goto interval_set_starts; - ltime.second= 0; - if (vers_info->interval.step.minute) - goto interval_set_starts; - ltime.minute= 0; - if (vers_info->interval.step.hour) - goto interval_set_starts; - ltime.hour= 0; - -interval_set_starts: - vers_info->interval.start= TIME_to_timestamp(thd, <ime, &err); - if (err) + if (vers_set_starts(thd, thd->query_start())) goto interval_starts_error; } @@ -2903,6 +2899,28 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, } +/* Round down STARTS to units based on INTERVAL value */ +bool partition_info::vers_set_starts(THD *thd, my_time_t ts) +{ + MYSQL_TIME ltime; + uint err; + thd->variables.time_zone->gmt_sec_to_TIME(<ime, ts); + if (vers_info->interval.step.second) + goto interval_set_starts; + ltime.second= 0; + if (vers_info->interval.step.minute) + goto interval_set_starts; + ltime.minute= 0; + if (vers_info->interval.step.hour) + goto interval_set_starts; + ltime.hour= 0; + +interval_set_starts: + vers_info->interval.start= TIME_to_timestamp(thd, <ime, &err); + return (bool) err; +} + + bool partition_info::vers_set_limit(ulonglong limit, bool auto_hist, const char *table_name) { diff --git a/sql/partition_info.h b/sql/partition_info.h index 3a8c3a3752435..7e32c8ecfabc3 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -41,6 +41,7 @@ struct Vers_part_info : public Sql_alloc Vers_part_info() : limit(0), auto_hist(false), + starts_clause(false), now_part(NULL), hist_part(NULL) { @@ -50,6 +51,7 @@ struct Vers_part_info : public Sql_alloc interval(src.interval), limit(src.limit), auto_hist(src.auto_hist), + starts_clause(src.starts_clause), now_part(NULL), hist_part(NULL) { @@ -59,6 +61,7 @@ struct Vers_part_info : public Sql_alloc interval= src.interval; limit= src.limit; auto_hist= src.auto_hist; + starts_clause= src.starts_clause; now_part= src.now_part; hist_part= src.hist_part; return *this; @@ -91,6 +94,7 @@ struct Vers_part_info : public Sql_alloc } interval; ulonglong limit; bool auto_hist; + bool starts_clause; partition_element *now_part; partition_element *hist_part; }; @@ -417,6 +421,7 @@ class partition_info : public DDL_LOG_STATE, public Sql_alloc bool vers_set_interval(THD *thd, Item *interval, interval_type int_type, Item *starts, bool auto_part, const char *table_name); + bool vers_set_starts(THD *thd, my_time_t ts); bool vers_set_limit(ulonglong limit, bool auto_part, const char *table_name); bool vers_set_hist_part(THD* thd, uint *create_count); bool vers_require_hist_part(THD *thd) const diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index ad124f180c833..d4468e0c83cc3 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -10676,9 +10676,9 @@ ER_UNKNOWN_OPERATOR spa "El operador no existe: '%-.128s'" ER_UNUSED_29 eng "You should never see it" -ER_PART_STARTS_BEYOND_INTERVAL - eng "%`s: STARTS is later than query time, first history partition may exceed INTERVAL value" - spa "%`s: STARTS es posterior al momento de consulta (query), la primera partición de historia puede exceder el valor INTERVAL" +WARN_VERS_STARTS_BEYOND_INTERVAL + eng "%`s: STARTS timestamp %s is later than query timestamp %s, first history partition may exceed INTERVAL value" + spa "%`s: STARTS %s es posterior al momento de consulta (query) %s, la primera partición de historia puede exceder el valor INTERVAL" ER_GALERA_REPLICATION_NOT_SUPPORTED eng "Galera replication not supported" spa "La replicación en Galera no está soportada" @@ -10758,3 +10758,11 @@ ER_CM_OPTION_MISSING_REQUIREMENT eng "CHANGE MASTER TO option '%s=%s' is missing requirement %s" ER_SLAVE_STATEMENT_TIMEOUT 70100 eng "Slave log event execution was interrupted (slave_max_statement_time exceeded)" +WARN_VERS_WRONG_STARTS + chi "%`s: STARTS 时间戳 %s 晚于最早历史日期 %s,已被设置为 %s" + eng "%`s: STARTS timestamp %s is above earliest history date %s and was set to %s" + spa "%`s: la marca de tiempo STARTS %s es posterior a la fecha histórica más temprana %s y se estableció en %s" +WARN_VERS_SLOW_ROW_END + chi "%`s: 未找到 ROW END 索引,正在使用慢速扫描" + eng "%`s: ROW END index not found, using slow scan" + spa "%`s: no se encontró el índice ROW END, usando escaneo lento" diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a5155b9955dc3..b241842e319ec 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -8675,6 +8675,13 @@ bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, } +bool THD::timestamp_to_string(String *str, uint dec, my_timespec_t ts) +{ + Temporal_hybrid th(this, ts); + return th.to_string(str, dec) == nullptr; +} + + void THD::my_ok_with_recreate_info(const Recreate_info &info, ulong warn_count) { diff --git a/sql/sql_class.h b/sql/sql_class.h index aa78312c3b6a5..3210c0321655c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4140,6 +4140,11 @@ class THD: public THD_count, /* this must be first */ const Type_handler *type_handler_for_datetime() const; bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, ulong sec_part, date_mode_t fuzzydate); + bool timestamp_to_string(String *str, uint dec, my_timespec_t ts); + bool timestamp_to_string(String *str, uint dec, my_time_t ts) + { + return timestamp_to_string(str, dec, {ts, 0}); + } inline my_time_t query_start() { return start_time; } inline ulong query_start_sec_part() { used|= QUERY_START_SEC_PART_USED; return start_time_sec_part; } @@ -8423,5 +8428,24 @@ class Write_log_with_flags } }; + +class TimestampString : public String +{ + THD *thd; + my_timespec_t ts; + +public: + TimestampString(THD *thd, my_timespec_t ts) : thd{thd}, ts{ts} {} + TimestampString(THD *thd, my_time_t sec) : thd{thd}, ts{sec, 0} {} + + const char * cstr(uint dec= 0) + { + if (thd->timestamp_to_string(this, dec, ts)) + return "ERROR"; + else + return c_ptr_safe(); + } +}; + #endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index bda2282efd465..18edec87f98c9 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -100,6 +100,9 @@ static int get_partition_id_linear_key_sub(partition_info *part_info, uint32 *pa static uint32 get_next_partition_via_walking(PARTITION_ITERATOR*); static void set_up_range_analysis_info(partition_info *part_info); static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR*); +static bool vers_calc_hist_parts(THD *thd, my_time_t end, + const Vers_part_info::interval_t &interval, + uint *hist_parts); #endif uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter); @@ -6141,6 +6144,70 @@ the generated partition syntax in a correct manner. } } + if (table->versioned() && + (alter_info->partition_flags & ALTER_PARTITION_INFO) && + part_info->part_type == VERSIONING_PARTITION && + part_info->vers_info->auto_hist && + alt_part_info->use_default_partitions && + (!*fast_alter_table || (alter_info->flags & ALTER_RECREATE))) + { + if (*fast_alter_table) + *fast_alter_table= false; + my_timespec_t min_ts, max_ts; + Vers_part_info *vers_info= part_info->vers_info; + auto &interval= vers_info->interval; + if (table->vers_get_history_range(thd, min_ts, max_ts)) + DBUG_RETURN(true); + if (max_ts.sec > MY_TIME_T_MIN) /* there is history in the table */ + { + DBUG_ASSERT(max_ts.sec < TIMESTAMP_MAX_VALUE); + DBUG_ASSERT(min_ts.sec <= max_ts.sec); + if (max_ts.usec) + { + max_ts.sec++; + max_ts.usec= 0; + } + if (vers_info->starts_clause) + { + if (interval.start > min_ts.sec) + { + TimestampString str_interval(thd, interval.start); + if (part_info->vers_set_starts(thd, min_ts.sec)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), + table->s->table_name.str, "STARTS"); + DBUG_RETURN(true); + } + TimestampString str_min_ts(thd, min_ts); + TimestampString str_interval2(thd, interval.start); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_WRONG_STARTS, ER_THD(thd, WARN_VERS_WRONG_STARTS), + table->s->table_name.str, + str_interval.cstr(), str_min_ts.cstr(), str_interval2.cstr()); + + } + } + else if (part_info->vers_set_starts(thd, min_ts.sec)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), + table->s->table_name.str, "STARTS"); + DBUG_RETURN(true); + } + + part_info->use_default_num_partitions= false; + part_info->use_default_partitions= true; + part_info->default_partitions_setup= false; + part_info->partitions.empty(); + + uint hist_parts= 0; + if (vers_calc_hist_parts(thd, max_ts.sec, interval, &hist_parts)) + DBUG_RETURN(true); + part_info->num_parts= hist_parts + 1; + } /* if (max_ts.sec > MY_TIME_T_MIN) */ + else + DBUG_ASSERT(max_ts == MY_TIMESPEC_MIN); /* no history in table */ + } /* if (need to get history range) */ + /* Set up partition default_engine_type either from the create_info or from the previus table @@ -6166,7 +6233,7 @@ the generated partition syntax in a correct manner. DBUG_ASSERT(create_info->db_type); create_info->db_type= partition_hton; } - } + } /* if (thd->work_part_info) */ } DBUG_RETURN(FALSE); err: @@ -6177,6 +6244,74 @@ the generated partition syntax in a correct manner. } +static bool vers_calc_hist_parts(THD *thd, my_time_t end, + const Vers_part_info::interval_t &interval, + uint *hist_parts) +{ + uint error= 0; + my_time_t start= interval.start; + DBUG_ASSERT(hist_parts != NULL); + *hist_parts= 0; + + if (end < start) + return false; + + if (!(interval.step.year || interval.step.month)) + { + my_time_t range= end - start; + if (!range) + range++; + + const longlong i_sec= interval2sec(&interval.step); + DBUG_ASSERT(i_sec > 0); + + *hist_parts= static_cast(range / i_sec); + if ((*hist_parts) * i_sec != range) + (*hist_parts)++; + if (*hist_parts >= MAX_PARTITIONS) + { + my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); + return true; + } + return false; + } + + MYSQL_TIME boundary; + thd->variables.time_zone->gmt_sec_to_TIME(&boundary, start); + + if (end == start) + { + *hist_parts= 1; + return false; + } + + while (start < end) + { + if (date_add_interval(thd, &boundary, interval.type, interval.step)) + { + my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "TIMESTAMP", "INTERVAL"); + return true; + } + + start= thd->variables.time_zone->TIME_to_gmt_sec(&boundary, &error); + if (error) + { + my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "TIMESTAMP", "INTERVAL"); + return true; + } + + (*hist_parts)++; + if (*hist_parts >= MAX_PARTITIONS) + { + my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); + return true; + } + } + + return false; +} + + /* Change partitions, used to implement ALTER TABLE ADD/REORGANIZE/COALESCE partitions. This method is used to implement both single-phase and multi- diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d73af1fedb401..7ff072a1a3fdd 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -62,6 +62,7 @@ #include "rpl_mi.h" #include "rpl_rli.h" #include "log.h" +#include "key.h" #ifdef WITH_WSREP #include "wsrep_mysqld.h" @@ -2394,7 +2395,7 @@ void promote_first_timestamp_column(List *column_definitions) } } -static bool key_cmp(const Key_part_spec &a, const Key_part_spec &b) +static bool key_eq(const Key_part_spec &a, const Key_part_spec &b) { return a.length == b.length && a.asc == b.asc && !lex_string_cmp(system_charset_info, &a.field_name, &b.field_name); @@ -2438,7 +2439,7 @@ static void check_duplicate_key(THD *thd, const Key *key, const KEY *key_info, } if (std::equal(key->columns.begin(), key->columns.end(), k.columns.begin(), - key_cmp)) + key_eq)) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX), key_info->name.str); @@ -13285,3 +13286,144 @@ bool HA_CREATE_INFO:: } return false; } + + +#ifdef WITH_PARTITION_STORAGE_ENGINE +bool TABLE::vers_get_history_range(THD *thd, my_timespec_t &min_ts, + my_timespec_t &max_ts) +{ + DBUG_ASSERT(versioned()); + // Find best key + KEY *key= key_info, *best_key= NULL; + uint best_idx; + Field *end_field= vers_end_field(); + const field_index_t end_field_idx= end_field->field_index; + for (uint idx= 0; idx < s->keys; idx++, key++) + { + DBUG_ASSERT(key->key_part->fieldnr > 0); + if (key->key_part->fieldnr - 1 == end_field_idx && + !(key->key_part->key_part_flag & HA_REVERSE_SORT) && + (!best_key || best_key->key_length > key->key_length)) + { + best_key= key; + best_idx= idx; + } + } + + int error; + my_timespec_t ts; + min_ts= MY_TIMESPEC_MAX; + max_ts= MY_TIMESPEC_MIN; +#ifndef DBUG_OFF + my_timespec_t min_ts2, max_ts2; +#endif /* DBUG_OFF */ + MY_BITMAP *save_read_set= read_set; + MY_BITMAP *save_write_set= write_set; + DBUG_ASSERT(save_read_set != &tmp_set); + bitmap_clear_all(&tmp_set); + column_bitmaps_set(&tmp_set, &tmp_set); + bitmap_set_bit(read_set, end_field->field_index); + file->column_bitmaps_signal(); + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, s->db.str, + s->table_name.str, MDL_SHARED_READ)); + if ((error= file->ha_external_lock(thd, F_RDLCK))) + goto end; + + if (best_key) + { + uchar search_key[MAX_KEY_LENGTH]; + KEY *best_key_info= key_info + best_idx; + end_field->set_max(); + KEY_PART_INFO *key_part= best_key_info->key_part; + const uint key_prefix_len= key_part[0].store_length; + key_copy(search_key, record[0], best_key_info, key_prefix_len); + + /* Get range from index */ + if ((error= file->ha_index_init(best_idx, true))) + goto end_unlock; + + if (!(error= file->ha_index_first(record[0]))) + { + min_ts.sec= end_field->get_timestamp(&min_ts.usec); + error= file->ha_index_read_map(record[0], (uchar*) search_key, + (key_part_map) 1, HA_READ_BEFORE_KEY); + if (!error) + { + max_ts.sec= end_field->get_timestamp(&max_ts.usec); + } + } + + file->ha_index_end(); + + if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND) + error= 0; + else if (error) + goto end_unlock; + +#ifndef DBUG_OFF + /* Test both index and scan, compare the results between them */ + min_ts2= min_ts; + max_ts2= max_ts; + if (DBUG_IF("test_mdev-25529")) + goto jump_scan; +#endif /* DBUG_OFF */ + } + else + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_SLOW_ROW_END, + ER_THD(thd, WARN_VERS_SLOW_ROW_END), + s->table_name.str); +#ifndef DBUG_OFF +jump_scan: +#endif /* DBUG_OFF */ + /* Get range by scan */ + if ((error= file->ha_rnd_init(1))) + goto end_unlock; + + /* can_continue_handler_scan() is only for heap (record changed protection) */ + while (!(error= file->can_continue_handler_scan()) && + !(error= file->ha_rnd_next(record[0]))) + { + ts.sec= end_field->get_timestamp(&ts.usec); + if (ts == MY_TIMESPEC_MAX) + continue; + if (ts < min_ts) + min_ts= ts; + if (ts > max_ts) + max_ts= ts; + } + + file->ha_rnd_end(); +#ifndef DBUG_OFF + if (best_key) + { + DBUG_ASSERT(DBUG_IF("test_mdev-25529")); + DBUG_ASSERT(min_ts == min_ts2); + DBUG_ASSERT(max_ts == max_ts2); + } +#endif /* DBUG_OFF */ + } + + if (error == HA_ERR_END_OF_FILE) + error= 0; + +end_unlock: + file->ha_external_unlock(thd); + +end: + if (error) + { + myf flags= 0; + + if (file->is_fatal_error(error, HA_CHECK_ALL)) + flags|= ME_FATAL; /* Other handler errors are fatal */ + + file->print_error(error, MYF(flags)); + } + + column_bitmaps_set(save_read_set, save_write_set); + file->column_bitmaps_signal(); + return error; +} +#endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/sql_time.cc b/sql/sql_time.cc index a729c01212aac..ae8ec8734b630 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -24,6 +24,7 @@ #include +/* Daynumber from year 0 to 9999-12-31 */ #define MAX_DAY_NUMBER 3652424L /* Some functions to calculate dates */ @@ -920,11 +921,6 @@ void make_truncated_value_warning(THD *thd, } -/* Daynumber from year 0 to 9999-12-31 */ -#define COMBINE(X) \ - (((((X)->day * 24LL + (X)->hour) * 60LL + \ - (X)->minute) * 60LL + (X)->second)*1000000LL + \ - (X)->second_part) #define GET_PART(X, N) X % N ## LL; X/= N ## LL bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, @@ -964,7 +960,7 @@ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, if (time_type != MYSQL_TIMESTAMP_TIME) ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1; - usec= COMBINE(ltime) + sign*COMBINE(&interval); + usec= interval2usec(ltime) + sign * interval2usec(&interval); if (usec < 0) { diff --git a/sql/sql_time.h b/sql/sql_time.h index c918eb6d807ba..7ed507c2f4759 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -187,4 +187,15 @@ longlong pack_time(const MYSQL_TIME *my_time); void unpack_time(longlong packed, MYSQL_TIME *my_time, enum_mysql_timestamp_type ts_type); +template +inline longlong interval2sec(T x) +{ + return ((x->day * 24LL + x->hour) * 60LL + x->minute) * 60LL + x->second; +} + +template +inline longlong interval2usec(T x) +{ + return interval2sec(x) * 1000000LL + x->second_part; +} #endif /* SQL_TIME_INCLUDED */ diff --git a/sql/sql_type.cc b/sql/sql_type.cc index cc793009b71f5..61846dacc6ea2 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -438,6 +438,14 @@ Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate) } +Temporal_hybrid::Temporal_hybrid(THD *thd, my_timespec_t time) +{ + thd->variables.time_zone->gmt_sec_to_TIME(this, time.sec); + DBUG_ASSERT(time.usec < 1000000); + second_part= time.usec; +} + + uint Timestamp::binary_length_to_precision(uint length) { switch (length) { diff --git a/sql/sql_type.h b/sql/sql_type.h index 53970f3d23a54..3b93fad7cfbd2 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -1319,6 +1319,7 @@ class Temporal_hybrid: public Temporal else make_from_decimal(thd, warn, nr, mode); } + Temporal_hybrid(THD *thd, my_timespec_t time); // End of constuctors bool copy_valid_value_to_mysql_time(MYSQL_TIME *ltime) const diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index eabd28ba35e63..c6a72e5e1d068 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7361,8 +7361,12 @@ alter_commands: } | alter_list opt_partitioning + | partitioning + alter_list | alter_list remove_partitioning + | remove_partitioning + alter_list | remove_partitioning | partitioning /* diff --git a/sql/table.h b/sql/table.h index e287ffb330abc..3c5f9f012dcf1 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1851,6 +1851,8 @@ struct TABLE #ifdef WITH_PARTITION_STORAGE_ENGINE bool vers_switch_partition(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx); + bool vers_get_history_range(THD *thd, my_timespec_t &min_ts, + my_timespec_t &max_ts); #endif int update_generated_fields();