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:
Nikita Malyavin 2019-11-26 19:22:04 +10:00 committed by Sergei Golubchik
parent 0515577d12
commit 259fb1cbed
20 changed files with 826 additions and 76 deletions

View file

@ -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;
#

View file

@ -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;

View file

@ -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...

View file

@ -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:

View 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;

View 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;

View file

@ -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);

View file

@ -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:
/*

View file

@ -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)},

View file

@ -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"

View file

@ -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);
}

View file

@ -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() {}

View file

@ -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

View file

@ -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))
{

View file

@ -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

View file

@ -164,6 +164,7 @@ typedef struct st_key {
double actual_rec_per_key(uint i);
bool without_overlaps;
} KEY;

View file

@ -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()
{

View file

@ -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;

View file

@ -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())

View file

@ -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 {