diff --git a/mysql-test/main/parser.result b/mysql-test/main/parser.result index 5cdb6b29fd9..ec6c3f5d895 100644 --- a/mysql-test/main/parser.result +++ b/mysql-test/main/parser.result @@ -1388,6 +1388,7 @@ CALL p1(name, 'SELECT name TRANSACTION FROM t1'); CALL p1(name, 'SELECT name VALUE FROM t1'); CALL p1(name, 'SELECT name VERSIONING FROM t1'); CALL p1(name, 'SELECT name WITHOUT FROM t1'); +CALL p1(name, 'SELECT name OVERLAPS FROM t1'); DROP TABLE t1; END; $$ @@ -1414,6 +1415,7 @@ SELECT date TRANSACTION FROM t1 SELECT date VALUE FROM t1 SELECT date VERSIONING FROM t1 SELECT date WITHOUT FROM t1 +SELECT date OVERLAPS FROM t1 CALL p2('history'); BEGIN NOT ATOMIC DECLARE history INT; SET history=10; SELECT history; END 10 @@ -1436,6 +1438,7 @@ SELECT history TRANSACTION FROM t1 SELECT history VALUE FROM t1 SELECT history VERSIONING FROM t1 SELECT history WITHOUT FROM t1 +SELECT history OVERLAPS FROM t1 CALL p2('next'); BEGIN NOT ATOMIC DECLARE next INT; SET next=10; SELECT next; END 10 @@ -1459,6 +1462,7 @@ SELECT next VALUE FROM t1 Error 1064 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 'FROM t1' at line 1 SELECT next VERSIONING FROM t1 SELECT next WITHOUT FROM t1 +SELECT next OVERLAPS FROM t1 CALL p2('period'); BEGIN NOT ATOMIC DECLARE period INT; SET period=10; SELECT period; END 10 @@ -1481,6 +1485,7 @@ SELECT period TRANSACTION FROM t1 SELECT period VALUE FROM t1 SELECT period VERSIONING FROM t1 SELECT period WITHOUT FROM t1 +SELECT period OVERLAPS FROM t1 CALL p2('previous'); BEGIN NOT ATOMIC DECLARE previous INT; SET previous=10; SELECT previous; END 10 @@ -1504,6 +1509,7 @@ SELECT previous VALUE FROM t1 Error 1064 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 'FROM t1' at line 1 SELECT previous VERSIONING FROM t1 SELECT previous WITHOUT FROM t1 +SELECT previous OVERLAPS FROM t1 CALL p2('system'); BEGIN NOT ATOMIC DECLARE system INT; SET system=10; SELECT system; END 10 @@ -1526,6 +1532,7 @@ SELECT system TRANSACTION FROM t1 SELECT system VALUE FROM t1 SELECT system VERSIONING FROM t1 SELECT system WITHOUT FROM t1 +SELECT system OVERLAPS FROM t1 CALL p2('system_time'); BEGIN NOT ATOMIC DECLARE system_time INT; SET system_time=10; SELECT system_time; END 10 @@ -1548,6 +1555,7 @@ SELECT system_time TRANSACTION FROM t1 SELECT system_time VALUE FROM t1 SELECT system_time VERSIONING FROM t1 SELECT system_time WITHOUT FROM t1 +SELECT system_time OVERLAPS FROM t1 CALL p2('time'); BEGIN NOT ATOMIC DECLARE time INT; SET time=10; SELECT time; END 10 @@ -1571,6 +1579,7 @@ SELECT time TRANSACTION FROM t1 SELECT time VALUE FROM t1 SELECT time VERSIONING FROM t1 SELECT time WITHOUT FROM t1 +SELECT time OVERLAPS FROM t1 CALL p2('timestamp'); BEGIN NOT ATOMIC DECLARE timestamp INT; SET timestamp=10; SELECT timestamp; END 10 @@ -1594,6 +1603,7 @@ SELECT timestamp TRANSACTION FROM t1 SELECT timestamp VALUE FROM t1 SELECT timestamp VERSIONING FROM t1 SELECT timestamp WITHOUT FROM t1 +SELECT timestamp OVERLAPS FROM t1 CALL p2('transaction'); BEGIN NOT ATOMIC DECLARE transaction INT; SET transaction=10; SELECT transaction; END 10 @@ -1616,6 +1626,7 @@ SELECT transaction TRANSACTION FROM t1 SELECT transaction VALUE FROM t1 SELECT transaction VERSIONING FROM t1 SELECT transaction WITHOUT FROM t1 +SELECT transaction OVERLAPS FROM t1 CALL p2('value'); BEGIN NOT ATOMIC DECLARE value INT; SET value=10; SELECT value; END 10 @@ -1638,6 +1649,7 @@ SELECT value TRANSACTION FROM t1 SELECT value VALUE FROM t1 SELECT value VERSIONING FROM t1 SELECT value WITHOUT FROM t1 +SELECT value OVERLAPS FROM t1 CALL p2('versioning'); BEGIN NOT ATOMIC DECLARE versioning INT; SET versioning=10; SELECT versioning; END 10 @@ -1660,6 +1672,7 @@ SELECT versioning TRANSACTION FROM t1 SELECT versioning VALUE FROM t1 SELECT versioning VERSIONING FROM t1 SELECT versioning WITHOUT FROM t1 +SELECT versioning OVERLAPS FROM t1 CALL p2('without'); BEGIN NOT ATOMIC DECLARE without INT; SET without=10; SELECT without; END 10 @@ -1682,6 +1695,30 @@ SELECT without TRANSACTION FROM t1 SELECT without VALUE FROM t1 SELECT without VERSIONING FROM t1 SELECT without WITHOUT FROM t1 +SELECT without OVERLAPS FROM t1 +CALL p2('overlaps'); +BEGIN NOT ATOMIC DECLARE overlaps INT; SET overlaps=10; SELECT overlaps; END +10 +SELECT overlaps FROM t1 +SELECT overlaps 'alias' FROM t1 +SELECT overlaps() +Error 1582 Incorrect parameter count in the call to native function 'overlaps()' +SELECT overlaps.overlaps() +Error 1630 FUNCTION overlaps.overlaps does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual +SELECT overlaps DATE FROM t1 +SELECT overlaps HISTORY FROM t1 +SELECT overlaps NEXT FROM t1 +SELECT overlaps PERIOD FROM t1 +SELECT overlaps PREVIOUS FROM t1 +SELECT overlaps SYSTEM FROM t1 +SELECT overlaps SYSTEM_TIME FROM t1 +SELECT overlaps TIME FROM t1 +SELECT overlaps TIMESTAMP FROM t1 +SELECT overlaps TRANSACTION FROM t1 +SELECT overlaps VALUE FROM t1 +SELECT overlaps VERSIONING FROM t1 +SELECT overlaps WITHOUT FROM t1 +SELECT overlaps OVERLAPS FROM t1 DROP PROCEDURE p2; DROP PROCEDURE p1; # diff --git a/mysql-test/main/parser.test b/mysql-test/main/parser.test index aba83b146a0..c3b4baaf95a 100644 --- a/mysql-test/main/parser.test +++ b/mysql-test/main/parser.test @@ -1423,6 +1423,7 @@ BEGIN CALL p1(name, 'SELECT name VALUE FROM t1'); CALL p1(name, 'SELECT name VERSIONING FROM t1'); CALL p1(name, 'SELECT name WITHOUT FROM t1'); + CALL p1(name, 'SELECT name OVERLAPS FROM t1'); DROP TABLE t1; END; $$ @@ -1442,6 +1443,7 @@ CALL p2('transaction'); CALL p2('value'); CALL p2('versioning'); CALL p2('without'); +CALL p2('overlaps'); --enable_column_names DROP PROCEDURE p2; diff --git a/mysql-test/suite/perfschema/r/start_server_low_digest_sql_length.result b/mysql-test/suite/perfschema/r/start_server_low_digest_sql_length.result index 8ff2bdebd3c..9e9cb98073b 100644 --- a/mysql-test/suite/perfschema/r/start_server_low_digest_sql_length.result +++ b/mysql-test/suite/perfschema/r/start_server_low_digest_sql_length.result @@ -8,5 +8,5 @@ SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 #################################### SELECT event_name, digest, digest_text, sql_text FROM events_statements_history_long; event_name digest digest_text sql_text -statement/sql/select 8ad134e475b278738ee855a05d6a77cf SELECT ? + ? + SELECT ... -statement/sql/truncate 2b32156b59f41d61d9070458bce5932e TRUNCATE TABLE truncat... +statement/sql/select 230fd11f009a87fecbb87c9fc7361475 SELECT ? + ? + SELECT ... +statement/sql/truncate faf6cefb662b443f05e97b5c5ab14a59 TRUNCATE TABLE truncat... diff --git a/mysql-test/suite/period/r/create.result b/mysql-test/suite/period/r/create.result index 8cedb23465d..b454661afc9 100644 --- a/mysql-test/suite/period/r/create.result +++ b/mysql-test/suite/period/r/create.result @@ -7,8 +7,8 @@ t CREATE TABLE `t` ( `id` int(11) NOT NULL, `s` date NOT NULL, `e` date NOT NULL, - PRIMARY KEY (`id`), - PERIOD FOR `mytime` (`s`, `e`) + PERIOD FOR `mytime` (`s`, `e`), + PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 create or replace table t (id int primary key, s timestamp(6), e timestamp(6), period for mytime(s,e)); @@ -18,8 +18,8 @@ t CREATE TABLE `t` ( `id` int(11) NOT NULL, `s` timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000', `e` timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000', - PRIMARY KEY (`id`), - PERIOD FOR `mytime` (`s`, `e`) + PERIOD FOR `mytime` (`s`, `e`), + PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 # SQL16, Part 2, 11.3 , Syntax Rules, 2)a) # 2) If a
TPD is specified, then: diff --git a/mysql-test/suite/period/r/overlaps.result b/mysql-test/suite/period/r/overlaps.result new file mode 100644 index 00000000000..042c68d4f54 --- /dev/null +++ b/mysql-test/suite/period/r/overlaps.result @@ -0,0 +1,207 @@ +create or replace table t(id int, s date, e date, +period for p(s,e), +primary key(id, p without overlaps) +) partition by key (id); +show create table t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) NOT NULL, + `s` date NOT NULL, + `e` date NOT NULL, + PERIOD FOR `p` (`s`, `e`), + PRIMARY KEY (`id`,`p` WITHOUT OVERLAPS) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 + PARTITION BY KEY (`id`) +insert into t values (1, '2003-01-01', '2003-03-01'), +(1, '2003-05-01', '2003-07-01'); +insert into t values (1, '2003-02-01', '2003-04-01'); +ERROR 23000: Duplicate entry '1-2003-04-01-2003-02-01' for key 'PRIMARY' +insert into t values (1, '2003-04-01', '2003-06-01'); +ERROR 23000: Duplicate entry '1-2003-06-01-2003-04-01' for key 'PRIMARY' +insert into t values (1, '2003-05-15', '2003-06-15'); +ERROR 23000: Duplicate entry '1-2003-06-15-2003-05-15' for key 'PRIMARY' +insert into t values (1, '2003-04-01', '2003-08-01'); +ERROR 23000: Duplicate entry '1-2003-08-01-2003-04-01' for key 'PRIMARY' +insert into t values (1, '2003-03-01', '2003-05-01'); +# expand/shrink period +update t set s= '2002-12-01' where s = '2003-01-01'; +update t set s= '2003-01-01' where s = '2002-12-01'; +update t set e= '2003-08-01' where s = '2003-05-01'; +update t set e= '2003-07-01' where s = '2003-05-01'; +# move left/right +update t set s= '2002-12-15', e= '2003-01-15' where s = '2003-01-01'; +update t set s= '2003-01-01', e= '2003-02-01' where s = '2002-12-15'; +# diminish/enlarge +update t set s= '2003-01-10', e= '2003-01-20' where s = '2003-01-01'; +update t set s= '2003-01-01', e= '2003-02-01' where s = '2003-01-10'; +select * from t; +id s e +1 2003-01-01 2003-02-01 +1 2003-03-01 2003-05-01 +1 2003-05-01 2003-07-01 +# intersect left/right, strict inclusion/containment +update t set e= '2003-04-01' where s = '2003-01-01'; +ERROR 23000: Duplicate entry '1-2003-04-01-2003-01-01' for key 'PRIMARY' +update t set s= '2003-04-01' where s = '2003-05-01'; +ERROR 23000: Duplicate entry '1-2003-07-01-2003-04-01' for key 'PRIMARY' +update t set s= '2003-03-10', e= '2003-03-20' where s = '2003-01-01'; +ERROR 23000: Duplicate entry '1-2003-03-20-2003-03-10' for key 'PRIMARY' +update t set s= '2003-04-01', e= '2003-08-01' where s = '2003-03-01'; +ERROR 23000: Duplicate entry '1-2003-08-01-2003-04-01' for key 'PRIMARY' +# inclusion/containment with partial match +update t set s= '2003-03-01', e= '2003-04-01' where s = '2003-01-01'; +ERROR 23000: Duplicate entry '1-2003-04-01-2003-03-01' for key 'PRIMARY' +update t set s= '2003-04-01', e= '2003-05-01' where s = '2003-01-01'; +ERROR 23000: Duplicate entry '1-2003-05-01-2003-04-01' for key 'PRIMARY' +update t set s= '2003-03-01' where s = '2003-05-01'; +ERROR 23000: Duplicate entry '1-2003-07-01-2003-03-01' for key 'PRIMARY' +update t set e= '2003-05-01' where s = '2003-01-01'; +ERROR 23000: Duplicate entry '1-2003-05-01-2003-01-01' for key 'PRIMARY' +select * from t where year(s) = 2003; +id s e +1 2003-01-01 2003-02-01 +1 2003-03-01 2003-05-01 +1 2003-05-01 2003-07-01 +create or replace table t(id int, s date, e date, +period for p(s,e), +primary key(id, q without overlaps)); +ERROR HY000: Period `q` is not found in table +create or replace table t(id int, s date, e date, +primary key(id, p without overlaps)); +ERROR HY000: Period `p` is not found in table +create or replace table t(id int, s date, e date, +period for p(s,e), +primary key(id, s, p without overlaps)); +ERROR HY000: Key `(null)` cannot explicitly include column `s` +create or replace table t(id int, s date, e date, +period for p(s,e), +primary key(id)); +insert into t values (1, '2003-03-01', '2003-05-01'); +insert into t values (1, '2003-04-01', '2003-05-01'); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +create or replace table t(id int, u int, s date, e date, +period for p(s,e), +primary key(id, p without overlaps), +unique(u)); +show create table t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) NOT NULL, + `u` int(11) DEFAULT NULL, + `s` date NOT NULL, + `e` date NOT NULL, + PERIOD FOR `p` (`s`, `e`), + PRIMARY KEY (`id`,`p` WITHOUT OVERLAPS), + UNIQUE KEY `u` (`u`) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 +insert into t values (1, 1, '2003-03-01', '2003-05-01'); +insert into t values (1, 2, '2003-05-01', '2003-07-01'); +insert into t values (1, 3, '2003-04-01', '2003-05-01'); +ERROR 23000: Duplicate entry '1-2003-05-01-2003-04-01' for key 'PRIMARY' +create or replace table t(id int, u int, s date, e date, +period for p(s,e), +primary key(id, p without overlaps), +unique(u, p without overlaps)); +show create table t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) NOT NULL, + `u` int(11) DEFAULT NULL, + `s` date NOT NULL, + `e` date NOT NULL, + PERIOD FOR `p` (`s`, `e`), + PRIMARY KEY (`id`,`p` WITHOUT OVERLAPS), + UNIQUE KEY `u` (`u`,`p` WITHOUT OVERLAPS) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 +insert into t values (2, NULL, '2003-03-01', '2003-05-01'); +insert into t values (2, NULL, '2003-03-01', '2003-05-01'); +ERROR 23000: Duplicate entry '2-2003-05-01-2003-03-01' for key 'PRIMARY' +insert into t values (3, NULL, '2003-03-01', '2003-05-01'); +insert into t values (1, 1, '2003-03-01', '2003-05-01'); +insert into t values (1, 2, '2003-05-01', '2003-07-01'); +insert into t values (4, NULL, '2003-03-01', '2003-05-01'); +create sequence seq start=5; +update t set id= nextval(seq), u= nextval(seq), s='2003-05-01', e='2003-07-01' + where u is NULL; +select * from t; +id u s e +1 1 2003-03-01 2003-05-01 +1 2 2003-05-01 2003-07-01 +5 6 2003-05-01 2003-07-01 +7 8 2003-05-01 2003-07-01 +9 10 2003-05-01 2003-07-01 +create or replace table t(id int, s date, e date, +period for p(s,e)); +insert into t values (1, '2003-01-01', '2003-03-01'), +(1, '2003-05-01', '2003-07-01'), +(1, '2003-02-01', '2003-04-01'); +alter table t add primary key(id, p without overlaps); +ERROR 23000: Duplicate entry '1-2003-04-01-2003-02-01' for key 'PRIMARY' +# Historical rows are not checked against constraints +set @@system_versioning_alter_history= keep; +alter table t add system versioning; +delete from t; +alter table t add primary key(id, p without overlaps); +insert into t values (1, '2003-01-01', '2003-03-01'), +(1, '2003-03-01', '2003-05-01'); +# `without overlaps` is not lost on alter table +alter table t add y int; +show create table t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) NOT NULL, + `s` date NOT NULL, + `e` date NOT NULL, + `y` int(11) DEFAULT NULL, + PERIOD FOR `p` (`s`, `e`), + PRIMARY KEY (`id`,`p` WITHOUT OVERLAPS) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +alter table t drop y; +create or replace table t1 like t; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL, + `s` date NOT NULL, + `e` date NOT NULL, + PERIOD FOR `p` (`s`, `e`), + PRIMARY KEY (`id`,`p` WITHOUT OVERLAPS) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +create or replace table t1 (x int, s date, e date, +period for p(s,e), +primary key(x, p without overlaps)); +alter table t1 partition by key (x); +create or replace table t1 (x int, s date, e date, period for p (s, e)) +partition by hash (x); +alter table t1 add primary key (x, p without overlaps); +create or replace table t2 (x int, s date, e date, +period for p (s, e), +key(x, p without overlaps)); +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 'without overlaps))' at line 3 +create or replace table t2 (x int, s date, e date, +period for p (s, e), +unique(x, p without overlaps, x, p without overlaps)); +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 ' x, p without overlaps))' at line 3 +create or replace table t1 (x varchar(100), s date, e date, +period for p(s,e), +primary key(x, p without overlaps)); +create or replace table t1 (x varchar(100) compressed, s date, e date, +period for p(s,e), +primary key(x, p without overlaps)); +ERROR HY000: Compressed column 'x' can't be used in key specification +create or replace table t (x int, s date, e date, period for apptime(s,e), +unique(x, apptime without overlaps) using hash); +ERROR HY000: Key `x` cannot have WITHOUT OVERLAPS +create or replace table t (x int, s date, e date, period for apptime(s,e), +b blob, unique(x, b, apptime without overlaps)); +ERROR HY000: Key `x` cannot have WITHOUT OVERLAPS +create or replace table t (x int, s date, e date, b blob unique, +period for apptime(s,e), +unique(x, apptime without overlaps)); +insert into t values (1, '2020-03-01', '2020-03-05', 'test'); +insert into t values (1, '2020-03-05', '2020-03-10', 'test'); +ERROR 23000: Duplicate entry 'test' for key 'b' +insert into t values (1, '2020-03-05', '2020-03-10', 'test2'); +insert into t values (1, '2020-03-03', '2020-03-10', 'test3'); +ERROR 23000: Duplicate entry '1-2020-03-10-2020-03-03' for key 'x' +create or replace database test; diff --git a/mysql-test/suite/period/t/overlaps.test b/mysql-test/suite/period/t/overlaps.test new file mode 100644 index 00000000000..fdd277350f2 --- /dev/null +++ b/mysql-test/suite/period/t/overlaps.test @@ -0,0 +1,204 @@ +--source include/have_partition.inc + +# Test both myisam and innodb +--source suite/period/engines.inc + +let $default_engine= `select @@default_storage_engine`; + +create or replace table t(id int, s date, e date, + period for p(s,e), + primary key(id, p without overlaps) +) partition by key (id); + +--replace_result $default_engine DEFAULT_ENGINE +show create table t; + + +insert into t values (1, '2003-01-01', '2003-03-01'), + (1, '2003-05-01', '2003-07-01'); + +--error ER_DUP_ENTRY +insert into t values (1, '2003-02-01', '2003-04-01'); +--error ER_DUP_ENTRY +insert into t values (1, '2003-04-01', '2003-06-01'); +--error ER_DUP_ENTRY +insert into t values (1, '2003-05-15', '2003-06-15'); +--error ER_DUP_ENTRY +insert into t values (1, '2003-04-01', '2003-08-01'); + +insert into t values (1, '2003-03-01', '2003-05-01'); + +--echo # expand/shrink period +update t set s= '2002-12-01' where s = '2003-01-01'; +update t set s= '2003-01-01' where s = '2002-12-01'; + +update t set e= '2003-08-01' where s = '2003-05-01'; +update t set e= '2003-07-01' where s = '2003-05-01'; + +--echo # move left/right +update t set s= '2002-12-15', e= '2003-01-15' where s = '2003-01-01'; +update t set s= '2003-01-01', e= '2003-02-01' where s = '2002-12-15'; + +--echo # diminish/enlarge +update t set s= '2003-01-10', e= '2003-01-20' where s = '2003-01-01'; +update t set s= '2003-01-01', e= '2003-02-01' where s = '2003-01-10'; + +select * from t; + +--echo # intersect left/right, strict inclusion/containment +--error ER_DUP_ENTRY +update t set e= '2003-04-01' where s = '2003-01-01'; +--error ER_DUP_ENTRY +update t set s= '2003-04-01' where s = '2003-05-01'; +--error ER_DUP_ENTRY +update t set s= '2003-03-10', e= '2003-03-20' where s = '2003-01-01'; +--error ER_DUP_ENTRY +update t set s= '2003-04-01', e= '2003-08-01' where s = '2003-03-01'; + +--echo # inclusion/containment with partial match +--error ER_DUP_ENTRY +update t set s= '2003-03-01', e= '2003-04-01' where s = '2003-01-01'; +--error ER_DUP_ENTRY +update t set s= '2003-04-01', e= '2003-05-01' where s = '2003-01-01'; +--error ER_DUP_ENTRY +update t set s= '2003-03-01' where s = '2003-05-01'; +--error ER_DUP_ENTRY +update t set e= '2003-05-01' where s = '2003-01-01'; + +select * from t where year(s) = 2003; + +--error ER_PERIOD_NOT_FOUND +create or replace table t(id int, s date, e date, + period for p(s,e), + primary key(id, q without overlaps)); + +--error ER_PERIOD_NOT_FOUND +create or replace table t(id int, s date, e date, + primary key(id, p without overlaps)); + +--error ER_KEY_CONTAINS_PERIOD_FIELDS +create or replace table t(id int, s date, e date, + period for p(s,e), + primary key(id, s, p without overlaps)); + +create or replace table t(id int, s date, e date, + period for p(s,e), + primary key(id)); +insert into t values (1, '2003-03-01', '2003-05-01'); +--error ER_DUP_ENTRY +insert into t values (1, '2003-04-01', '2003-05-01'); + +create or replace table t(id int, u int, s date, e date, + period for p(s,e), + primary key(id, p without overlaps), + unique(u)); +--replace_result $default_engine DEFAULT_ENGINE +show create table t; +insert into t values (1, 1, '2003-03-01', '2003-05-01'); +insert into t values (1, 2, '2003-05-01', '2003-07-01'); +--error ER_DUP_ENTRY +insert into t values (1, 3, '2003-04-01', '2003-05-01'); + + + +create or replace table t(id int, u int, s date, e date, + period for p(s,e), + primary key(id, p without overlaps), + unique(u, p without overlaps)); +--replace_result $default_engine DEFAULT_ENGINE +show create table t; + +insert into t values (2, NULL, '2003-03-01', '2003-05-01'); +--error ER_DUP_ENTRY +insert into t values (2, NULL, '2003-03-01', '2003-05-01'); +insert into t values (3, NULL, '2003-03-01', '2003-05-01'); +insert into t values (1, 1, '2003-03-01', '2003-05-01'); +insert into t values (1, 2, '2003-05-01', '2003-07-01'); +insert into t values (4, NULL, '2003-03-01', '2003-05-01'); + +create sequence seq start=5; +update t set id= nextval(seq), u= nextval(seq), s='2003-05-01', e='2003-07-01' + where u is NULL; + +--sorted_result +select * from t; + +create or replace table t(id int, s date, e date, + period for p(s,e)); + +insert into t values (1, '2003-01-01', '2003-03-01'), + (1, '2003-05-01', '2003-07-01'), + (1, '2003-02-01', '2003-04-01'); + +--replace_regex /#sql-\w+/#sql-temp/ +--error ER_DUP_ENTRY +alter table t add primary key(id, p without overlaps); + +--echo # Historical rows are not checked against constraints +set @@system_versioning_alter_history= keep; +alter table t add system versioning; +delete from t; +alter table t add primary key(id, p without overlaps); + +insert into t values (1, '2003-01-01', '2003-03-01'), + (1, '2003-03-01', '2003-05-01'); + + +--echo # `without overlaps` is not lost on alter table +alter table t add y int; +--replace_result $default_engine DEFAULT_ENGINE +show create table t; +alter table t drop y; + +create or replace table t1 like t; +--replace_result $default_engine DEFAULT_ENGINE +show create table t1; + +create or replace table t1 (x int, s date, e date, + period for p(s,e), + primary key(x, p without overlaps)); +alter table t1 partition by key (x); + +create or replace table t1 (x int, s date, e date, period for p (s, e)) + partition by hash (x); +alter table t1 add primary key (x, p without overlaps); + +--error ER_PARSE_ERROR +create or replace table t2 (x int, s date, e date, + period for p (s, e), + key(x, p without overlaps)); + +--error ER_PARSE_ERROR +create or replace table t2 (x int, s date, e date, + period for p (s, e), + unique(x, p without overlaps, x, p without overlaps)); + +create or replace table t1 (x varchar(100), s date, e date, + period for p(s,e), + primary key(x, p without overlaps)); + +--error ER_COMPRESSED_COLUMN_USED_AS_KEY +create or replace table t1 (x varchar(100) compressed, s date, e date, + period for p(s,e), + primary key(x, p without overlaps)); + +--error ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS +create or replace table t (x int, s date, e date, period for apptime(s,e), + unique(x, apptime without overlaps) using hash); + +--error ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS +create or replace table t (x int, s date, e date, period for apptime(s,e), + b blob, unique(x, b, apptime without overlaps)); + +create or replace table t (x int, s date, e date, b blob unique, + period for apptime(s,e), + unique(x, apptime without overlaps)); + +insert into t values (1, '2020-03-01', '2020-03-05', 'test'); +--error ER_DUP_ENTRY +insert into t values (1, '2020-03-05', '2020-03-10', 'test'); +insert into t values (1, '2020-03-05', '2020-03-10', 'test2'); +--error ER_DUP_ENTRY +insert into t values (1, '2020-03-03', '2020-03-10', 'test3'); + +create or replace database test; diff --git a/sql/handler.cc b/sql/handler.cc index c76234f52a1..cc229cea0b1 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6734,6 +6734,116 @@ int handler::check_duplicate_long_entries_update(const uchar *new_rec) } +int handler::ha_check_overlaps(const uchar *old_data, const uchar* new_data) +{ + DBUG_ASSERT(new_data); + if (!table_share->period.unique_keys) + return 0; + if (table->versioned() && !table->vers_end_field()->is_max()) + return 0; + + bool is_update= old_data != NULL; + alloc_lookup_buffer(); + auto *record_buffer= lookup_buffer + table_share->max_unique_length + + table_share->null_fields; + auto *handler= this; + // handler->inited can be NONE on INSERT + if (handler->inited != NONE) + { + create_lookup_handler(); + handler= lookup_handler; + + // Needs to compare record refs later is old_row_found() + if (is_update) + position(old_data); + } + + DBUG_ASSERT(!keyread_enabled()); + + int error= 0; + lookup_errkey= (uint)-1; + + for (uint key_nr= 0; key_nr < table_share->keys && !error; key_nr++) + { + const KEY &key_info= table->key_info[key_nr]; + const uint key_parts= key_info.user_defined_key_parts; + if (!key_info.without_overlaps) + continue; + + if (is_update) + { + bool key_used= false; + for (uint k= 0; k < key_parts && !key_used; k++) + key_used= bitmap_is_set(table->write_set, + key_info.key_part[k].fieldnr - 1); + if (!key_used) + continue; + } + + error= handler->ha_index_init(key_nr, 0); + if (error) + return error; + + error= handler->ha_start_keyread(key_nr); + DBUG_ASSERT(!error); + + const uint period_field_length= key_info.key_part[key_parts - 1].length; + const uint key_base_length= key_info.key_length - 2 * period_field_length; + + key_copy(lookup_buffer, new_data, &key_info, 0); + + /* Copy period_start to period_end. + the value in period_start field is not significant, but anyway let's leave + it defined to avoid uninitialized memory access + */ + memcpy(lookup_buffer + key_base_length, + lookup_buffer + key_base_length + period_field_length, + period_field_length); + + /* Find row with period_end > (period_start of new_data) */ + error = handler->ha_index_read_map(record_buffer, lookup_buffer, + key_part_map((1 << (key_parts - 1)) - 1), + HA_READ_AFTER_KEY); + + if (!error && is_update) + { + /* In case of update it could happen that the nearest neighbour is + a record we are updating. It means, that there are no overlaps + from this side. + + An assumption is made that during update we always have the last + fetched row in old_data. Therefore, comparing ref's is enough + */ + DBUG_ASSERT(handler != this); + DBUG_ASSERT(inited != NONE); + DBUG_ASSERT(ref_length == handler->ref_length); + + handler->position(record_buffer); + if (memcmp(ref, handler->ref, ref_length) == 0) + error= handler->ha_index_next(record_buffer); + } + + if (!error && table->check_period_overlaps(key_info, new_data, record_buffer)) + error= HA_ERR_FOUND_DUPP_KEY; + + if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) + error= 0; + + if (error == HA_ERR_FOUND_DUPP_KEY) + lookup_errkey= key_nr; + + int end_error= handler->ha_end_keyread(); + DBUG_ASSERT(!end_error); + + end_error= handler->ha_index_end(); + if (!error && end_error) + error= end_error; + } + + return error; +} + + /** Check if galera disables binary logging for this table @@ -6831,6 +6941,10 @@ int handler::ha_write_row(const uchar *buf) DBUG_ENTER("handler::ha_write_row"); DEBUG_SYNC_C("ha_write_row_start"); + error= ha_check_overlaps(NULL, buf); + if (unlikely(error)) + goto end; + MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_write_count); @@ -6862,6 +6976,7 @@ int handler::ha_write_row(const uchar *buf) #endif /* WITH_WSREP */ } +end: DEBUG_SYNC_C("ha_write_row_end"); DBUG_RETURN(error); } @@ -6879,6 +6994,9 @@ int handler::ha_update_row(const uchar *old_data, const uchar *new_data) DBUG_ASSERT(new_data == table->record[0]); DBUG_ASSERT(old_data == table->record[1]); + if ((error= ha_check_overlaps(old_data, new_data))) + return error; + MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_update_count); diff --git a/sql/handler.h b/sql/handler.h index ed2f68d8ea6..8c45c64bec8 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1998,11 +1998,13 @@ struct Table_period_info: Sql_alloc { Table_period_info() : create_if_not_exists(false), - constr(NULL) {} + constr(NULL), + unique_keys(0) {} Table_period_info(const char *name_arg, size_t size) : name(name_arg, size), create_if_not_exists(false), - constr(NULL) {} + constr(NULL), + unique_keys(0){} Lex_ident name; @@ -2018,6 +2020,7 @@ struct Table_period_info: Sql_alloc start_end_t period; bool create_if_not_exists; Virtual_column_info *constr; + uint unique_keys; bool is_set() const { @@ -4677,6 +4680,8 @@ private: int check_duplicate_long_entries(const uchar *new_rec); int check_duplicate_long_entries_update(const uchar *new_rec); int check_duplicate_long_entry_key(const uchar *new_rec, uint key_no); + /** PRIMARY KEY/UNIQUE WITHOUT OVERLAPS check */ + int ha_check_overlaps(const uchar *old_data, const uchar* new_data); protected: /* diff --git a/sql/lex.h b/sql/lex.h index a4442ee8529..30f330cedce 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -455,6 +455,7 @@ static SYMBOL symbols[] = { { "OUTER", SYM(OUTER)}, { "OUTFILE", SYM(OUTFILE)}, { "OVER", SYM(OVER_SYM)}, + { "OVERLAPS", SYM(OVERLAPS_SYM)}, { "OWNER", SYM(OWNER_SYM)}, { "PACKAGE", SYM(PACKAGE_MARIADB_SYM)}, { "PACK_KEYS", SYM(PACK_KEYS_SYM)}, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 64683023d9d..2c419bc1902 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7957,3 +7957,7 @@ ER_SLAVE_IGNORED_SHARED_TABLE swe "Slav SQL tråden ignorerade '%s' pga tabellen är delad" ER_NO_AUTOINCREMENT_WITH_UNIQUE eng "AUTO_INCREMENT column %`s cannot be used in the UNIQUE index %`s" +ER_KEY_CONTAINS_PERIOD_FIELDS + eng "Key %`s cannot explicitly include column %`s" +ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS + eng "Key %`s cannot have WITHOUT OVERLAPS" diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d323f7e0004..03383a744fc 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -175,7 +175,8 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root) columns(rhs.columns, mem_root), name(rhs.name), option_list(rhs.option_list), - generated(rhs.generated), invisible(false) + generated(rhs.generated), invisible(false), + without_overlaps(rhs.without_overlaps), period(rhs.period) { list_copy_and_replace_each_value(columns, mem_root); } diff --git a/sql/sql_class.h b/sql/sql_class.h index af56eecfe60..9c3774c4a91 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -382,13 +382,15 @@ public: engine_option_value *option_list; bool generated; bool invisible; + bool without_overlaps; + Lex_ident period; Key(enum Keytype type_par, const LEX_CSTRING *name_arg, ha_key_alg algorithm_arg, bool generated_arg, DDL_options_st ddl_options) :DDL_options(ddl_options), type(type_par), key_create_info(default_key_create_info), name(*name_arg), option_list(NULL), generated(generated_arg), - invisible(false) + invisible(false), without_overlaps(false) { key_create_info.algorithm= algorithm_arg; } @@ -399,7 +401,7 @@ public: :DDL_options(ddl_options), type(type_par), key_create_info(*key_info_arg), columns(*cols), name(*name_arg), option_list(create_opt), generated(generated_arg), - invisible(false) + invisible(false), without_overlaps(false) {} Key(const Key &rhs, MEM_ROOT *mem_root); virtual ~Key() {} diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 78a54f6e5e3..755ae547db3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2253,6 +2253,14 @@ int show_create_table_ex(THD *thd, TABLE_LIST *table_list, } + if (period.name) + { + append_period(thd, packet, + period.start_field(share)->field_name, + period.end_field(share)->field_name, + period.name, true); + } + key_info= table->s->key_info; primary_key= share->primary_key; @@ -2287,7 +2295,11 @@ int show_create_table_ex(THD *thd, TABLE_LIST *table_list, packet->append(STRING_WITH_LEN(" (")); - for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) + uint key_parts= key_info->user_defined_key_parts; + if (key_info->without_overlaps) + key_parts-= 2; + + for (uint j=0 ; j < key_parts ; j++,key_part++) { Field *field= key_part->field; if (field->invisible > INVISIBLE_USER) @@ -2307,6 +2319,14 @@ int show_create_table_ex(THD *thd, TABLE_LIST *table_list, key_part->field->charset()->mbmaxlen); } } + + if (key_info->without_overlaps) + { + packet->append(','); + append_identifier(thd, packet, &share->period.name); + packet->append(STRING_WITH_LEN(" WITHOUT OVERLAPS")); + } + packet->append(')'); store_key_options(thd, packet, table, &table->key_info[i]); if (key_info->parser) @@ -2340,15 +2360,6 @@ int show_create_table_ex(THD *thd, TABLE_LIST *table_list, } } - if (period.name) - { - append_period(thd, packet, - period.start_field(share)->field_name, - period.end_field(share)->field_name, - period.name, true); - } - - /* Get possible foreign key definitions stored in InnoDB and append them to the CREATE TABLE statement diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4750a302017..639890df9f2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4280,6 +4280,18 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, // Check if a duplicate index is defined. check_duplicate_key(thd, key, key_info, &alter_info->key_list); + + key_info->without_overlaps= key->without_overlaps; + if (key_info->without_overlaps) + { + if (key_info->algorithm == HA_KEY_ALG_LONG_HASH) + { + my_error(ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS, MYF(0), key_info->name.str); + DBUG_RETURN(true); + } + create_info->period_info.unique_keys++; + } + key_info++; } @@ -4582,42 +4594,66 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) } -static bool vers_prepare_keys(THD *thd, HA_CREATE_INFO *create_info, - Alter_info *alter_info, KEY **key_info, uint key_count) +static bool append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info, + Alter_info *alter_info, KEY **key_info, + uint key_count) { - DBUG_ASSERT(create_info->versioned()); - - const char *row_start_field= create_info->vers_info.as_row.start; - DBUG_ASSERT(row_start_field); - const char *row_end_field= create_info->vers_info.as_row.end; - DBUG_ASSERT(row_end_field); + const Lex_ident &row_start_field= create_info->vers_info.as_row.start; + const Lex_ident &row_end_field= create_info->vers_info.as_row.end; + DBUG_ASSERT(!create_info->versioned() || (row_start_field && row_end_field)); List_iterator key_it(alter_info->key_list); Key *key= NULL; + + if (create_info->versioned()) + { + while ((key=key_it++)) + { + if (key->type != Key::PRIMARY && key->type != Key::UNIQUE) + continue; + + Key_part_spec *key_part=NULL; + List_iterator part_it(key->columns); + while ((key_part=part_it++)) + { + if (row_start_field.streq(key_part->field_name) || + row_end_field.streq(key_part->field_name)) + break; + } + if (!key_part) + key->columns.push_back(new Key_part_spec(&row_end_field, 0)); + } + key_it.rewind(); + } + while ((key=key_it++)) { - if (key->type != Key::PRIMARY && key->type != Key::UNIQUE) - continue; - - Key_part_spec *key_part= NULL; - List_iterator part_it(key->columns); - while ((key_part=part_it++)) + if (key->without_overlaps) { - if (!my_strcasecmp(system_charset_info, - row_start_field, - key_part->field_name.str) || + DBUG_ASSERT(key->type == Key::PRIMARY || key->type == Key::UNIQUE); + if (!create_info->period_info.is_set() + || !key->period.streq(create_info->period_info.name)) + { + my_error(ER_PERIOD_NOT_FOUND, MYF(0), key->period.str); + return true; + } - !my_strcasecmp(system_charset_info, - row_end_field, - key_part->field_name.str)) - break; + const auto &period_start= create_info->period_info.period.start; + const auto &period_end= create_info->period_info.period.end; + List_iterator part_it(key->columns); + while (Key_part_spec *key_part= part_it++) + { + if (period_start.streq(key_part->field_name) + || period_end.streq(key_part->field_name)) + { + my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key->name.str, + key_part->field_name); + return true; + } + } + key->columns.push_back(new Key_part_spec(&period_end, 0)); + key->columns.push_back(new Key_part_spec(&period_start, 0)); } - if (key_part) - continue; // Key already contains Sys_start or Sys_end - - Key_part_spec *key_part_sys_end_col= - new (thd->mem_root) Key_part_spec(&create_info->vers_info.as_row.end, 0); - key->columns.push_back(key_part_sys_end_col); } return false; @@ -4858,12 +4894,9 @@ handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, } #endif - if (create_info->versioned()) - { - if(vers_prepare_keys(thd, create_info, alter_info, key_info, - *key_count)) - goto err; - } + if (append_system_key_parts(thd, create_info, alter_info, key_info, + *key_count)) + goto err; if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, file, key_info, key_count, create_table_mode)) @@ -8554,8 +8587,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, const char *dropped_key_part= NULL; KEY_PART_INFO *key_part= key_info->key_part; key_parts.empty(); + uint key_parts_nr= key_info->user_defined_key_parts; + if (key_info->without_overlaps) + key_parts_nr-= 2; + bool delete_index_stat= FALSE; - for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) + for (uint j=0 ; j < key_parts_nr ; j++,key_part++) { Field *kfield= key_part->field; if (!kfield) @@ -8694,6 +8731,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key= new Key(key_type, &tmp_name, &key_create_info, MY_TEST(key_info->flags & HA_GENERATED_KEY), &key_parts, key_info->option_list, DDL_options()); + key->without_overlaps= key_info->without_overlaps; + key->period= table->s->period.name; new_key_list.push_back(key, thd->mem_root); } if (long_hash_key) @@ -10309,6 +10348,14 @@ do_continue:; table->file->check_if_supported_inplace_alter(&altered_table, &ha_alter_info); + Key *k; + for (List_iterator it(alter_info->key_list); + (k= it++) && inplace_supported != HA_ALTER_INPLACE_NOT_SUPPORTED;) + { + if(k->without_overlaps) + inplace_supported= HA_ALTER_INPLACE_NOT_SUPPORTED; + } + if (alter_info->supports_algorithm(thd, inplace_supported, &ha_alter_info) || alter_info->supports_lock(thd, inplace_supported, &ha_alter_info)) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7cc1faea79b..c2b843f051b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -966,6 +966,7 @@ End SQL_MODE_ORACLE_SPECIFIC */ %token OPEN_SYM /* SQL-2003-R */ %token OPTIONS_SYM %token OPTION /* SQL-2003-N */ +%token OVERLAPS_SYM %token OWNER_SYM %token PACK_KEYS_SYM %token PAGE_SYM @@ -1501,7 +1502,7 @@ End SQL_MODE_ORACLE_SPECIFIC */ option_type opt_var_type opt_var_ident_type %type - opt_unique constraint_key_type fulltext spatial + constraint_key_type fulltext spatial %type btree_or_rtree opt_key_algorithm_clause opt_USING_key_algorithm @@ -2363,7 +2364,26 @@ create: create_table_set_open_action_and_adjust_tables(lex); Lex->pop_select(); //main select } - | create_or_replace opt_unique INDEX_SYM opt_if_not_exists + | create_or_replace INDEX_SYM opt_if_not_exists + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + ident + opt_key_algorithm_clause + ON table_ident + { + if (Lex->add_create_index_prepare($8)) + MYSQL_YYABORT; + if (Lex->add_create_index(Key::MULTIPLE, &$5, $6, $1 | $3)) + MYSQL_YYABORT; + } + '(' key_list ')' opt_lock_wait_timeout normal_key_options + opt_index_lock_algorithm + { + Lex->pop_select(); //main select + } + | create_or_replace UNIQUE_SYM INDEX_SYM opt_if_not_exists { if (Lex->main_select_push()) MYSQL_YYABORT; @@ -2374,10 +2394,11 @@ create: { if (Lex->add_create_index_prepare($9)) MYSQL_YYABORT; - if (Lex->add_create_index($2, &$6, $7, $1 | $4)) + if (Lex->add_create_index(Key::UNIQUE, &$6, $7, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' opt_lock_wait_timeout normal_key_options + '(' key_list opt_without_overlaps ')' + opt_lock_wait_timeout normal_key_options opt_index_lock_algorithm { Lex->pop_select(); //main select @@ -5847,7 +5868,7 @@ key_def: if (unlikely(Lex->add_key($2, $4.str ? &$4 : &$1, $5, $3))) MYSQL_YYABORT; } - '(' key_list ')' normal_key_options { } + '(' key_list opt_without_overlaps ')' normal_key_options { } | opt_constraint constraint_key_type opt_if_not_exists ident TYPE_SYM btree_or_rtree { @@ -5855,7 +5876,7 @@ key_def: if (unlikely(Lex->add_key($2, $4.str ? &$4 : &$1, $6, $3))) MYSQL_YYABORT; } - '(' key_list ')' normal_key_options { } + '(' key_list opt_without_overlaps ')' normal_key_options { } | opt_constraint FOREIGN KEY_SYM opt_if_not_exists opt_ident { if (unlikely(Lex->check_add_key($4)) || @@ -6903,11 +6924,6 @@ keys_or_index: | INDEXES {} ; -opt_unique: - /* empty */ { $$= Key::MULTIPLE; } - | UNIQUE_SYM { $$= Key::UNIQUE; } - ; - fulltext: FULLTEXT_SYM { $$= Key::FULLTEXT;} ; @@ -7048,6 +7064,15 @@ key_list: } ; +opt_without_overlaps: + /* nothing */ {} + | ',' ident WITHOUT OVERLAPS_SYM + { + Lex->last_key->without_overlaps= true; + Lex->last_key->period= $2; + } + ; + key_part: ident { @@ -10591,6 +10616,12 @@ function_call_generic: $1, $3))) MYSQL_YYABORT; } + | OVERLAPS_SYM '(' opt_expr_list ')' + { + if (!($$= Lex->make_item_func_call_native_or_parse_error(thd, + $1, $3))) + MYSQL_YYABORT; + } | WITHIN '(' opt_expr_list ')' { if (!($$= Lex->make_item_func_call_native_or_parse_error(thd, @@ -15647,6 +15678,7 @@ keyword_sp_var_and_label: | ONE_SYM | ONLINE_SYM | ONLY_SYM + | OVERLAPS_SYM | PACKAGE_MARIADB_SYM | PACK_KEYS_SYM | PAGE_SYM diff --git a/sql/structs.h b/sql/structs.h index f6d63051dfe..0993c453e16 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -164,6 +164,7 @@ typedef struct st_key { double actual_rec_per_key(uint i); + bool without_overlaps; } KEY; diff --git a/sql/table.cc b/sql/table.cc index a31db77cfff..99ecd8450bb 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -59,6 +59,7 @@ struct extra2_fields LEX_CUSTRING system_period; LEX_CUSTRING application_period; LEX_CUSTRING field_data_type_info; + LEX_CUSTRING without_overlaps; void reset() { bzero((void*)this, sizeof(*this)); } }; @@ -1562,6 +1563,9 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields) case EXTRA2_APPLICATION_TIME_PERIOD: fail= read_extra2_section_once(extra2, length, &fields->application_period); break; + case EXTRA2_PERIOD_WITHOUT_OVERLAPS: + fail= read_extra2_section_once(extra2, length, &fields->without_overlaps); + break; case EXTRA2_FIELD_DATA_TYPE_INFO: fail= read_extra2_section_once(extra2, length, &fields->field_data_type_info); break; @@ -2248,9 +2252,32 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (init_period_from_extra2(&period, pos, end)) goto err; + if (extra2_str_size(period.name.length) + + extra2_str_size(period.constr_name.length) + + 2 * frm_fieldno_size + != extra2.application_period.length) + goto err; status_var_increment(thd->status_var.feature_application_time_periods); } + if (extra2.without_overlaps.str) + { + if (extra2.application_period.str == NULL) + goto err; + const uchar *key_pos= extra2.without_overlaps.str; + period.unique_keys= read_frm_keyno(key_pos); + for (uint k= 0; k < period.unique_keys; k++) + { + key_pos+= frm_keyno_size; + uint key_nr= read_frm_keyno(key_pos); + key_info[key_nr].without_overlaps= true; + } + + if ((period.unique_keys + 1) * frm_keyno_size + != extra2.without_overlaps.length) + goto err; + } + if (extra2.field_data_type_info.length && field_data_type_info_array.parse(old_root, share->fields, extra2.field_data_type_info)) @@ -8593,6 +8620,38 @@ void TABLE::evaluate_update_default_function() DBUG_VOID_RETURN; } +/** + Compare two records by a specific key (that has WITHOUT OVERLAPS clause) + + @return true, key values are equal and periods overlap + false, either key values differ or periods don't overlap + */ +bool TABLE::check_period_overlaps(const KEY &key, + const uchar *lhs, const uchar *rhs) +{ + DBUG_ASSERT(key.without_overlaps); + uint base_part_nr= key.user_defined_key_parts - 2; + for (uint part_nr= 0; part_nr < base_part_nr; part_nr++) + { + Field *f= key.key_part[part_nr].field; + if (key.key_part[part_nr].null_bit) + if (f->is_null_in_record(lhs) || f->is_null_in_record(rhs)) + return false; + if (f->cmp(f->ptr_in_record(lhs), f->ptr_in_record(rhs)) != 0) + return false; + } + + uint period_start= key.user_defined_key_parts - 1; + uint period_end= key.user_defined_key_parts - 2; + const Field *fs= key.key_part[period_start].field; + const Field *fe= key.key_part[period_end].field; + + if (fs->cmp(fe->ptr_in_record(lhs), fs->ptr_in_record(rhs)) <= 0) + return false; + if (fs->cmp(fs->ptr_in_record(lhs), fe->ptr_in_record(rhs)) >= 0) + return false; + return true; +} void TABLE::vers_update_fields() { diff --git a/sql/table.h b/sql/table.h index 2ccc59f9282..310ffb8c704 100644 --- a/sql/table.h +++ b/sql/table.h @@ -803,7 +803,7 @@ struct TABLE_SHARE #endif /** - System versioning support. + System versioning and application-time periods support. */ struct period_info_t { @@ -811,6 +811,7 @@ struct TABLE_SHARE uint16 end_fieldno; Lex_ident name; Lex_ident constr_name; + uint unique_keys; Field *start_field(TABLE_SHARE *s) const { return s->field[start_fieldno]; @@ -1632,7 +1633,7 @@ public: int insert_portion_of_time(THD *thd, const vers_select_conds_t &period_conds, ha_rows *rows_inserted); bool vers_check_update(List &items); - + static bool check_period_overlaps(const KEY &key, const uchar *lhs, const uchar *rhs); int delete_row(); void vers_update_fields(); void vers_update_end(); @@ -1770,10 +1771,18 @@ class IS_table_read_plan; /** number of bytes used by field positional indexes in frm */ constexpr uint frm_fieldno_size= 2; +/** number of bytes used by key position number in frm */ +constexpr uint frm_keyno_size= 2; static inline uint16 read_frm_fieldno(const uchar *data) { return uint2korr(data); } static inline void store_frm_fieldno(uchar *data, uint16 fieldno) { int2store(data, fieldno); } +static inline uint16 read_frm_keyno(const uchar *data) +{ return uint2korr(data); } +static inline void store_frm_keyno(uchar *data, uint16 fieldno) +{ int2store(data, fieldno); } +static inline size_t extra2_str_size(size_t len) +{ return (len > 255 ? 3 : 1) + len; } class select_unit; class TMP_TABLE_PARAM; diff --git a/sql/unireg.cc b/sql/unireg.cc index 2aa46131efb..79403ff339d 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -147,12 +147,6 @@ bool has_extra2_field_flags(List &create_fields) return false; } -static size_t extra2_str_size(size_t len) -{ - return (len > 255 ? 3 : 1) + len; -} - - static uint gis_field_options_image(uchar *buff, List &create_fields) { @@ -258,6 +252,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table, + extra2_str_size(create_info->period_info.constr->name.length) + 2 * frm_fieldno_size : 0; + size_t without_overlaps_len= frm_keyno_size * (create_info->period_info.unique_keys + 1); uint e_unique_hash_extra_parts= 0; uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE]; const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0); @@ -390,7 +385,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table, if (create_info->period_info.name) { - extra2_size+= 1 + extra2_str_size(period_info_len); + extra2_size+= 2 + extra2_str_size(period_info_len) + + extra2_str_size(without_overlaps_len); } bool has_extra2_field_flags_= has_extra2_field_flags(create_fields); @@ -485,6 +481,19 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table, store_frm_fieldno(pos, get_fieldno_by_name(create_info, create_fields, create_info->period_info.period.end)); pos+= frm_fieldno_size; + + *pos++= EXTRA2_PERIOD_WITHOUT_OVERLAPS; + pos= extra2_write_len(pos, without_overlaps_len); + store_frm_keyno(pos, create_info->period_info.unique_keys); + pos+= frm_keyno_size; + for (uint key= 0; key < keys; key++) + { + if (key_info[key].without_overlaps) + { + store_frm_keyno(pos, key); + pos+= frm_keyno_size; + } + } } if (create_info->versioned()) diff --git a/sql/unireg.h b/sql/unireg.h index 419fbc4bd80..873d6f681fc 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -177,7 +177,8 @@ enum extra2_frm_value_type { EXTRA2_ENGINE_TABLEOPTS=128, EXTRA2_FIELD_FLAGS=129, - EXTRA2_FIELD_DATA_TYPE_INFO=130 + EXTRA2_FIELD_DATA_TYPE_INFO=130, + EXTRA2_PERIOD_WITHOUT_OVERLAPS=131, }; enum extra2_field_flags {