summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Malyavin <nikitamalyavin@gmail.com>2019-11-26 19:22:04 +1000
committerNikita Malyavin <nikitamalyavin@gmail.com>2020-01-21 20:35:05 +1000
commitb423e60f743bc45f74219895fe5e05398a0d6d96 (patch)
tree5986ae6309ade6887c9a6b8718fc064dc1ac8b2a
parent628a48b5679a7772e7cfc6cfce4179f89e4272bb (diff)
downloadmariadb-git-b423e60f743bc45f74219895fe5e05398a0d6d96.tar.gz
MDEV-16978 Application-time periods: WITHOUT OVERLAPS
* The overlaps check is implemented on a handler level per row command. It creates a separate cursor (actually, another handler instance) and caches it inside the original handler, when ha_update_row or ha_insert_row is issued. Cursor closes on unlocking the handler. * Containing the same key in index means unique constraint violation even in usual terms. So we fetch left and right neighbours and check that they have same key prefix, excluding from the key only the period part. If it doesnt match, then there's no such neighbour, and the check passes. Otherwise, we check if this neighbour intersects with the considered key. * the check does introduce new error and fails with ER_DUPP_KEY error. This might break REPLACE workflow and should be fixed separately
-rw-r--r--mysql-test/main/parser.result37
-rw-r--r--mysql-test/main/parser.test2
-rw-r--r--mysql-test/suite/period/r/create.result8
-rw-r--r--mysql-test/suite/period/r/overlaps.result167
-rw-r--r--mysql-test/suite/period/t/overlaps.test152
-rw-r--r--sql/handler.cc139
-rw-r--r--sql/handler.h16
-rw-r--r--sql/key.cc2
-rw-r--r--sql/key.h2
-rw-r--r--sql/lex.h1
-rw-r--r--sql/share/errmsg-utf8.txt6
-rw-r--r--sql/sql_class.cc3
-rw-r--r--sql/sql_class.h6
-rw-r--r--sql/sql_show.cc31
-rw-r--r--sql/sql_table.cc108
-rw-r--r--sql/sql_yacc.yy13
-rw-r--r--sql/structs.h1
-rw-r--r--sql/table.cc15
-rw-r--r--sql/table.h11
-rw-r--r--sql/unireg.cc11
-rw-r--r--storage/cassandra/ha_cassandra.cc2
21 files changed, 677 insertions, 56 deletions
diff --git a/mysql-test/main/parser.result b/mysql-test/main/parser.result
index fa40ffa6942..ff51ebe61b4 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 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 ')' at line 1
+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 006f409e6b7..7a669bf236d 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/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 <table definition>, Syntax Rules, 2)a)
# 2) If a <table period definition> 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..f56c55d7f87
--- /dev/null
+++ b/mysql-test/suite/period/r/overlaps.result
@@ -0,0 +1,167 @@
+create or replace table t(id int, s date, e date,
+period for p(s,e),
+primary key(id, p without overlaps));
+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=MyISAM DEFAULT CHARSET=latin1
+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: Can't write; duplicate key in table 't'
+insert into t values (1, '2003-04-01', '2003-06-01');
+ERROR 23000: Can't write; duplicate key in table 't'
+insert into t values (1, '2003-05-15', '2003-06-15');
+ERROR 23000: Can't write; duplicate key in table 't'
+insert into t values (1, '2003-04-01', '2003-08-01');
+ERROR 23000: Can't write; duplicate key in table 't'
+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';
+# intersect left/right, strict inclusion/containment
+update t set e= '2003-04-01' where s = '2003-01-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+update t set s= '2003-04-01' where s = '2003-05-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+update t set s= '2003-03-10', e= '2003-03-20' where s = '2003-01-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+update t set s= '2003-04-01', e= '2003-08-01' where s = '2003-03-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+# inclusion/containment with partial match
+update t set s= '2003-03-01', e= '2003-04-01' where s = '2003-01-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+update t set s= '2003-04-01', e= '2003-05-01' where s = '2003-01-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+update t set s= '2003-03-01' where s = '2003-05-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+update t set e= '2003-05-01' where s = '2003-01-01';
+ERROR 23000: Can't write; duplicate key in table 't'
+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)` should not contain period fields in order to make it 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');
+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=MyISAM 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: Can't write; duplicate key in table 't'
+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) NOT 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=MyISAM 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 (2, 1, '2003-05-01', '2003-07-01');
+create or replace table t(id int, s date, e date,
+period for p(s,e)) engine=innodb;
+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: Can't write; duplicate key in table 't'
+# 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=InnoDB 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=InnoDB 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)
+) partition by key (x);
+ERROR HY000: Period WITHOUT OVERLAPS is not implemented for partitioned tables
+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);
+ERROR HY000: Period WITHOUT OVERLAPS is not implemented for partitioned tables
+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 HY000: Period WITHOUT OVERLAPS is not implemented for partitioned tables
+create or replace table t2 (x int, s date, e date,
+period for p (s, e),
+key(x, p without overlaps));
+ERROR HY000: Period WITHOUT OVERLAPS is only allowed for unique keys
+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..65a3532a5eb
--- /dev/null
+++ b/mysql-test/suite/period/t/overlaps.test
@@ -0,0 +1,152 @@
+--source include/have_innodb.inc
+--source include/have_partition.inc
+
+create or replace table t(id int, s date, e date,
+ period for p(s,e),
+ primary key(id, p without overlaps));
+
+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_KEY
+insert into t values (1, '2003-02-01', '2003-04-01');
+--error ER_DUP_KEY
+insert into t values (1, '2003-04-01', '2003-06-01');
+--error ER_DUP_KEY
+insert into t values (1, '2003-05-15', '2003-06-15');
+--error ER_DUP_KEY
+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';
+
+--echo # intersect left/right, strict inclusion/containment
+--error ER_DUP_KEY
+update t set e= '2003-04-01' where s = '2003-01-01';
+--error ER_DUP_KEY
+update t set s= '2003-04-01' where s = '2003-05-01';
+--error ER_DUP_KEY
+update t set s= '2003-03-10', e= '2003-03-20' where s = '2003-01-01';
+--error ER_DUP_KEY
+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_KEY
+update t set s= '2003-03-01', e= '2003-04-01' where s = '2003-01-01';
+--error ER_DUP_KEY
+update t set s= '2003-04-01', e= '2003-05-01' where s = '2003-01-01';
+--error ER_DUP_KEY
+update t set s= '2003-03-01' where s = '2003-05-01';
+--error ER_DUP_KEY
+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));
+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_KEY
+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));
+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');
+insert into t values (2, 1, '2003-05-01', '2003-07-01');
+
+create or replace table t(id int, s date, e date,
+ period for p(s,e)) engine=innodb;
+
+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');
+
+--error ER_DUP_KEY
+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;
+show create table t;
+alter table t drop y;
+
+create or replace table t1 like t;
+show create table t1;
+
+--error ER_PERIOD_WITHOUT_OVERLAPS_PARTITIONED
+create or replace table t1 (x int, s date, e date,
+ period for p(s,e),
+ primary key(x, p without overlaps)
+) partition by key (x);
+
+create or replace table t1 (x int, s date, e date,
+ period for p(s,e),
+ primary key(x, p without overlaps));
+--error ER_PERIOD_WITHOUT_OVERLAPS_PARTITIONED
+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);
+--error ER_PERIOD_WITHOUT_OVERLAPS_PARTITIONED
+alter table t1 add primary key (x, p without overlaps);
+
+--error ER_PERIOD_WITHOUT_OVERLAPS_NON_UNIQUE
+create or replace table t2 (x int, s date, e date,
+ period for p (s, e),
+ key(x, p without overlaps));
+
+create or replace database test;
diff --git a/sql/handler.cc b/sql/handler.cc
index 8a099992d7f..d46a02f33a3 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -6411,6 +6411,13 @@ int handler::ha_external_lock(THD *thd, int lock_type)
mysql_audit_external_lock(thd, table_share, lock_type);
}
+ if (lock_type == F_UNLCK && check_overlaps_handler)
+ {
+ check_overlaps_handler->ha_external_lock(table->in_use, F_UNLCK);
+ check_overlaps_handler->close();
+ check_overlaps_handler= NULL;
+ }
+
if (MYSQL_HANDLER_RDLOCK_DONE_ENABLED() ||
MYSQL_HANDLER_WRLOCK_DONE_ENABLED() ||
MYSQL_HANDLER_UNLOCK_DONE_ENABLED())
@@ -6651,6 +6658,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);
@@ -6680,6 +6691,7 @@ int handler::ha_write_row(const uchar *buf)
#endif /* WITH_WSREP */
}
+end:
DEBUG_SYNC_C("ha_write_row_end");
DBUG_RETURN(error);
}
@@ -6699,6 +6711,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);
@@ -6944,6 +6959,130 @@ void handler::set_lock_type(enum thr_lock_type lock)
table->reginfo.lock_type= lock;
}
+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;
+ if (!check_overlaps_buffer)
+ check_overlaps_buffer= (uchar*)alloc_root(&table_share->mem_root,
+ table_share->max_unique_length
+ + table_share->reclength);
+ auto *record_buffer= check_overlaps_buffer + table_share->max_unique_length;
+ auto *handler= this;
+ if (handler->inited != NONE)
+ {
+ if (!check_overlaps_handler)
+ {
+ check_overlaps_handler= clone(table_share->normalized_path.str,
+ &table_share->mem_root);
+ int error= -1;
+ if (check_overlaps_handler != NULL)
+ error= check_overlaps_handler->ha_external_lock(table->in_use, F_RDLCK);
+ if (error)
+ return error;
+ }
+ handler= check_overlaps_handler;
+ }
+
+ for (uint key_nr= 0; key_nr < table_share->keys; 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;
+
+ key_copy(check_overlaps_buffer, new_data, key_info, 0);
+ 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;
+ }
+
+ int error= handler->ha_index_init(key_nr, 0);
+ if (error)
+ return error;
+
+ for (int run= 0; run < 2; run++)
+ {
+ if (run == 0)
+ {
+ error = handler->ha_index_read_map(record_buffer,
+ check_overlaps_buffer,
+ key_part_map((1 << key_parts) - 1),
+ HA_READ_KEY_OR_PREV);
+ if (error == HA_ERR_KEY_NOT_FOUND)
+ continue;
+ }
+ else
+ {
+ error = handler->ha_index_next(record_buffer);
+ if (error == HA_ERR_END_OF_FILE)
+ continue;
+ }
+
+ if (error)
+ {
+ handler->ha_index_end();
+ return error;
+ }
+
+ /* In case of update it could appear that the nearest neighbour is
+ * a record we are updating. It means, that there are no overlaps
+ * from this side. */
+ if (is_update && memcmp(old_data + table->s->null_bytes,
+ record_buffer + table->s->null_bytes,
+ table->s->reclength - table->s->null_bytes) == 0)
+ {
+ continue;
+ }
+
+ uint period_key_part_nr= key_parts - 2;
+ int cmp_res= 0;
+ for (uint part_nr= 0; !cmp_res && part_nr < period_key_part_nr; part_nr++)
+ {
+ Field *f= key_info->key_part[part_nr].field;
+ cmp_res= f->cmp(f->ptr_in_record(new_data),
+ f->ptr_in_record(record_buffer));
+ }
+ if (cmp_res)
+ continue; /* key is different => no overlaps */
+
+ int period_cmp[2][2]= {/* l1 > l2, l1 > r2, r1 > l2, r1 > r2 */};
+ for (int i= 0; i < 2; i++)
+ {
+ for (int j= 0; j < 2; j++)
+ {
+ Field *lhs= key_info->key_part[period_key_part_nr + i].field;
+ Field *rhs= key_info->key_part[period_key_part_nr + j].field;
+
+ period_cmp[i][j]= lhs->cmp(lhs->ptr_in_record(new_data),
+ rhs->ptr_in_record(record_buffer));
+ }
+ }
+
+ if ((period_cmp[0][0] <= 0 && period_cmp[1][0] > 0)
+ || (period_cmp[0][0] >= 0 && period_cmp[0][1] < 0))
+ {
+ handler->ha_index_end();
+ return HA_ERR_FOUND_DUPP_KEY;
+ }
+ }
+ error= handler->ha_index_end();
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
#ifdef WITH_WSREP
/**
@details
diff --git a/sql/handler.h b/sql/handler.h
index fa42922ec0b..7f11228f9ce 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1991,11 +1991,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;
@@ -2011,6 +2013,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
{
@@ -3007,6 +3010,8 @@ protected:
Table_flags cached_table_flags; /* Set on init() and open() */
ha_rows estimation_rows_to_insert;
+ uchar *check_overlaps_buffer;
+ handler *check_overlaps_handler;
public:
handlerton *ht; /* storage engine of this handler */
uchar *ref; /* Pointer to current row */
@@ -3152,8 +3157,9 @@ private:
public:
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(0),
- estimation_rows_to_insert(0), ht(ht_arg),
- ref(0), end_range(NULL),
+ estimation_rows_to_insert(0),
+ check_overlaps_buffer(NULL), check_overlaps_handler(NULL),
+ ht(ht_arg), ref(0), end_range(NULL),
implicit_emptied(0),
mark_trx_read_write_done(0),
check_table_binlog_row_based_done(0),
@@ -3263,6 +3269,8 @@ public:
DBUG_ASSERT(cached_table_flags < (HA_LAST_TABLE_FLAG << 1));
return cached_table_flags;
}
+ /** PRIMARY KEY WITHOUT OVERLAPS check is done globally */
+ int ha_check_overlaps(const uchar *old_data, const uchar* new_data);
/**
These functions represent the public interface to *users* of the
handler class, hence they are *not* virtual. For the inheritance
diff --git a/sql/key.cc b/sql/key.cc
index bf50094a9e4..24fac92da44 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -112,7 +112,7 @@ int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field,
@param with_zerofill skipped bytes in the key buffer to be filled with 0
*/
-void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
+void key_copy(uchar *to_key, const uchar *from_record, const KEY *key_info,
uint key_length, bool with_zerofill)
{
uint length;
diff --git a/sql/key.h b/sql/key.h
index 45f58c75655..871373bfcd5 100644
--- a/sql/key.h
+++ b/sql/key.h
@@ -25,7 +25,7 @@ typedef struct st_key_part_info KEY_PART_INFO;
int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field,
uint *key_length, uint *keypart);
-void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
+void key_copy(uchar *to_key, const uchar *from_record, const KEY *key_info,
uint key_length, bool with_zerofill= FALSE);
void key_restore(uchar *to_record, const uchar *from_key, KEY *key_info,
uint key_length);
diff --git a/sql/lex.h b/sql/lex.h
index f3fc1513369..33ac24ac176 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -453,6 +453,7 @@ static SYMBOL symbols[] = {
{ "OUTER", SYM(OUTER)},
{ "OUTFILE", SYM(OUTFILE)},
{ "OVER", SYM(OVER_SYM)},
+ { "OVERLAPS", SYM(OVERLAPS)},
{ "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 68b0b6bd586..d8038be4320 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7943,3 +7943,9 @@ ER_WARN_HISTORY_ROW_START_TIME
eng "Table `%s.%s` history row start '%s' is later than row end '%s'"
ER_PART_STARTS_BEYOND_INTERVAL
eng "%`s: STARTS is later than query time, first history partition may exceed INTERVAL value"
+ER_KEY_CONTAINS_PERIOD_FIELDS
+ eng "Key %`s should not contain period fields in order to make it WITHOUT OVERLAPS"
+ER_PERIOD_WITHOUT_OVERLAPS_PARTITIONED
+ eng "Period WITHOUT OVERLAPS is not implemented for partitioned tables"
+ER_PERIOD_WITHOUT_OVERLAPS_NON_UNIQUE
+ eng "Period WITHOUT OVERLAPS is only allowed for unique keys"
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 4577f1007be..13d0c6920ad 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 8d501b4870c..e89f4676eaa 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -357,13 +357,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;
}
@@ -374,7 +376,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 c4a721bfa38..7f4c66125ab 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2225,6 +2225,14 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
+ 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;
@@ -2259,7 +2267,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
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)
@@ -2279,6 +2291,14 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
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)
@@ -2312,15 +2332,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
}
- 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 2322c6d222d..70cf15e429e 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4233,6 +4233,11 @@ 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->without_overlaps)
+ create_info->period_info.unique_keys++;
+
key_info++;
}
@@ -4532,15 +4537,13 @@ 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 auto &row_start_field= create_info->vers_info.as_row.start;
+ const auto &row_end_field= create_info->vers_info.as_row.end;
+ DBUG_ASSERT(!create_info->versioned() || (row_start_field && row_end_field));
List_iterator<Key> key_it(alter_info->key_list);
Key *key= NULL;
@@ -4549,25 +4552,57 @@ static bool vers_prepare_keys(THD *thd, HA_CREATE_INFO *create_info,
if (key->type != Key::PRIMARY && key->type != Key::UNIQUE)
continue;
- Key_part_spec *key_part= NULL;
- List_iterator<Key_part_spec> part_it(key->columns);
- while ((key_part=part_it++))
+ if (create_info->versioned())
{
- if (!my_strcasecmp(system_charset_info,
- row_start_field,
- key_part->field_name.str) ||
-
- !my_strcasecmp(system_charset_info,
- row_end_field,
- key_part->field_name.str))
- break;
+ Key_part_spec *key_part=NULL;
+ List_iterator<Key_part_spec> 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));
}
- 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);
+ key_it.rewind();
+ while ((key=key_it++))
+ {
+ if (key->without_overlaps)
+ {
+ if (key->type != Key::PRIMARY && key->type != Key::UNIQUE)
+ {
+ my_error(ER_PERIOD_WITHOUT_OVERLAPS_NON_UNIQUE, MYF(0), key->period.str);
+ return true;
+ }
+ 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;
+ }
+ if (thd->work_part_info)
+ {
+ my_error(ER_PERIOD_WITHOUT_OVERLAPS_PARTITIONED, MYF(0));
+ return true;
+ }
+ const auto &period_start= create_info->period_info.period.start;
+ const auto &period_end= create_info->period_info.period.end;
+ List_iterator<Key_part_spec> 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);
+ return true;
+ }
+ }
+ key->columns.push_back(new Key_part_spec(&period_start, 0));
+ key->columns.push_back(new Key_part_spec(&period_end, 0));
+ }
}
return false;
@@ -4807,12 +4842,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))
@@ -8441,8 +8473,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)
@@ -8581,6 +8617,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key= new (thd->mem_root) 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)
@@ -10148,6 +10186,14 @@ do_continue:;
table->file->check_if_supported_inplace_alter(&altered_table,
&ha_alter_info);
+ Key *k;
+ for (List_iterator<Key> 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 2bf7295c742..7a4367b96c7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -959,6 +959,7 @@ End SQL_MODE_ORACLE_SPECIFIC */
%token <kwd> OPEN_SYM /* SQL-2003-R */
%token <kwd> OPTIONS_SYM
%token <kwd> OPTION /* SQL-2003-N */
+%token <kwd> OVERLAPS
%token <kwd> OWNER_SYM
%token <kwd> PACK_KEYS_SYM
%token <kwd> PAGE_SYM
@@ -7001,6 +7002,11 @@ key_list:
{
Lex->last_key->columns.push_back($3, thd->mem_root);
}
+ | key_list ',' ident WITHOUT OVERLAPS
+ {
+ Lex->last_key->without_overlaps= true;
+ Lex->last_key->period= $3;
+ }
| key_part order_dir
{
Lex->last_key->columns.push_back($1, thd->mem_root);
@@ -10526,6 +10532,12 @@ function_call_generic:
$1, $3)))
MYSQL_YYABORT;
}
+ | OVERLAPS '(' 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,
@@ -15524,6 +15536,7 @@ keyword_sp_var_and_label:
| ONE_SYM
| ONLINE_SYM
| ONLY_SYM
+ | OVERLAPS
| 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 ab18eb75f65..7808812b12e 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -2261,6 +2261,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (init_period_from_extra2(&period, pos, end))
goto err;
status_var_increment(thd->status_var.feature_application_time_periods);
+
+ const uchar *key_pos= pos + 2 * frm_fieldno_size;
+ 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 (frm_ident_stored_size(period.name.length)
+ + frm_ident_stored_size(period.constr_name.length)
+ + (period.unique_keys + 1) * frm_keyno_size + 2 * frm_fieldno_size
+ != extra2.application_period.length)
+ goto err;
}
if (extra2.field_data_type_info.length &&
diff --git a/sql/table.h b/sql/table.h
index cfb5fc77d47..7043df7f76a 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -815,6 +815,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];
@@ -1781,10 +1782,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(const uchar *data, uint16 fieldno)
+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 frm_ident_stored_size(size_t len)
+{ return len + (len > 255 ? 3 : 1); }
class select_unit;
class TMP_TABLE_PARAM;
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 7130b3e5d8a..1e95242786e 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -256,6 +256,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
size_t period_info_len= create_info->period_info.name
? extra2_str_size(create_info->period_info.name.length)
+ extra2_str_size(create_info->period_info.constr->name.length)
+ + frm_keyno_size * (create_info->period_info.unique_keys + 1)
+ 2 * frm_fieldno_size
: 0;
uint e_unique_hash_extra_parts= 0;
@@ -485,6 +486,16 @@ 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;
+ 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/storage/cassandra/ha_cassandra.cc b/storage/cassandra/ha_cassandra.cc
index 410150b088f..9e28fd68fcb 100644
--- a/storage/cassandra/ha_cassandra.cc
+++ b/storage/cassandra/ha_cassandra.cc
@@ -2170,7 +2170,7 @@ int ha_cassandra::info(uint flag)
}
-void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
+void key_copy(uchar *to_key, const uchar *from_record, const KEY *key_info,
uint key_length, bool with_zerofill);