mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
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 not introduce new error and fails with ER_DUPP_KEY error. This might break REPLACE workflow and should be fixed separately
This commit is contained in:
parent
0515577d12
commit
259fb1cbed
20 changed files with 826 additions and 76 deletions
|
@ -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;
|
||||
#
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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:
|
||||
|
|
207
mysql-test/suite/period/r/overlaps.result
Normal file
207
mysql-test/suite/period/r/overlaps.result
Normal file
|
@ -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;
|
204
mysql-test/suite/period/t/overlaps.test
Normal file
204
mysql-test/suite/period/t/overlaps.test
Normal file
|
@ -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;
|
118
sql/handler.cc
118
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);
|
||||
|
|
|
@ -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:
|
||||
/*
|
||||
|
|
|
@ -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)},
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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
|
||||
|
|
115
sql/sql_table.cc
115
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> 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<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));
|
||||
}
|
||||
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<Key_part_spec> 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<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,
|
||||
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<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))
|
||||
{
|
||||
|
|
|
@ -966,6 +966,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_SYM
|
||||
%token <kwd> OWNER_SYM
|
||||
%token <kwd> PACK_KEYS_SYM
|
||||
%token <kwd> PAGE_SYM
|
||||
|
@ -1501,7 +1502,7 @@ End SQL_MODE_ORACLE_SPECIFIC */
|
|||
option_type opt_var_type opt_var_ident_type
|
||||
|
||||
%type <key_type>
|
||||
opt_unique constraint_key_type fulltext spatial
|
||||
constraint_key_type fulltext spatial
|
||||
|
||||
%type <key_alg>
|
||||
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
|
||||
|
|
|
@ -164,6 +164,7 @@ typedef struct st_key {
|
|||
|
||||
double actual_rec_per_key(uint i);
|
||||
|
||||
bool without_overlaps;
|
||||
} KEY;
|
||||
|
||||
|
||||
|
|
59
sql/table.cc
59
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()
|
||||
{
|
||||
|
|
13
sql/table.h
13
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<Item> &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;
|
||||
|
|
|
@ -147,12 +147,6 @@ bool has_extra2_field_flags(List<Create_field> &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_field> &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())
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue