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 {