From 459f40a2326c141684c5832659b2e2e0d5968bbd Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 12 Dec 2017 21:30:49 +0300 Subject: [PATCH] SQL: create..select revisited [closes #370] --- include/mysql_com.h | 1 + mysql-test/suite/versioning/common.inc | 25 +- mysql-test/suite/versioning/r/create.result | 274 ++++++++++---- .../suite/versioning/r/select_sp.result | 4 +- mysql-test/suite/versioning/t/create.test | 187 +++++++--- mysql-test/suite/versioning/t/select_sp.test | 4 +- sql/handler.cc | 333 ++++++++++-------- sql/handler.h | 65 ++-- sql/sql_base.cc | 9 +- sql/sql_class.h | 7 + sql/sql_insert.cc | 25 +- sql/sql_parse.cc | 3 +- sql/sql_select.cc | 4 +- sql/sql_table.cc | 2 +- sql/sql_yacc.yy | 2 +- sql/table.cc | 15 +- storage/innobase/handler/ha_innodb.cc | 5 +- 17 files changed, 619 insertions(+), 346 deletions(-) diff --git a/include/mysql_com.h b/include/mysql_com.h index 32942d77671..12746b2001b 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -198,6 +198,7 @@ enum enum_indicator_type #define VERS_SYS_END_FLAG (1 << 28) /* autogenerated column declared with `generated always as row end` (see II.a SQL Standard).*/ +#define VERS_SYSTEM_FIELD VERS_SYS_START_FLAG | VERS_SYS_END_FLAG #define VERS_UPDATE_UNVERSIONED_FLAG (1 << 29) /* column that doesn't support system versioning when table itself supports it*/ diff --git a/mysql-test/suite/versioning/common.inc b/mysql-test/suite/versioning/common.inc index bc7a69b24e2..00ce41cda71 100644 --- a/mysql-test/suite/versioning/common.inc +++ b/mysql-test/suite/versioning/common.inc @@ -27,17 +27,17 @@ returns varchar(255) deterministic begin declare e varchar(255); - select lower(engine) from information_schema.engines where support='DEFAULT' into e; + select engine from information_schema.engines where support='DEFAULT' into e; return e; end~~ -create function if not exists sys_datatype() +create function if not exists sys_datatype(engine varchar(255)) returns varchar(255) deterministic begin - if default_engine() = 'innodb' then - return 'bigint unsigned'; - elseif default_engine() = 'myisam' then + if engine = 'InnoDB' then + return 'bigint(20) unsigned'; + elseif engine = 'MyISAM' then return 'timestamp(6)'; end if; return NULL; @@ -47,9 +47,9 @@ create function if not exists current_row(sys_trx_end varbinary(255)) returns int deterministic begin - if default_engine() = 'innodb' then + if default_engine() = 'InnoDB' then return sys_trx_end = 18446744073709551615; - elseif default_engine() = 'myisam' then + elseif default_engine() = 'MyISAM' then return sys_trx_end = timestamp'2038-01-19 03:14:07.999999'; end if; return NULL; @@ -59,9 +59,9 @@ create function if not exists sys_commit_ts(sys_field varchar(255)) returns varchar(255) deterministic begin - if default_engine() = 'innodb' then + if default_engine() = 'InnoDB' then return concat('vtq_commit_ts(', sys_field, ')'); - elseif default_engine() = 'myisam' then + elseif default_engine() = 'MyISAM' then return sys_field; end if; return NULL; @@ -70,9 +70,9 @@ end~~ create procedure if not exists innodb_verify_vtq(recs int) begin declare i int default 1; - if default_engine() = 'innodb' then + if default_engine() = 'InnoDB' then call verify_vtq; - elseif default_engine() = 'myisam' then + elseif default_engine() = 'MyISAM' then create temporary table tmp (No int, A bool, B bool, C bool, D bool); while i <= recs do insert into tmp values (i, 1, 1, 1, 1); @@ -99,5 +99,6 @@ end~~ delimiter ;~~ let $default_engine= `select default_engine()`; -let sys_datatype= `select sys_datatype()`; +let $sys_datatype= `select sys_datatype(default_engine())`; +let $sys_datatype_uc= `select upper(sys_datatype(default_engine()))`; --enable_query_log diff --git a/mysql-test/suite/versioning/r/create.result b/mysql-test/suite/versioning/r/create.result index f2c173d2828..d00881bfd5f 100644 --- a/mysql-test/suite/versioning/r/create.result +++ b/mysql-test/suite/versioning/r/create.result @@ -3,25 +3,25 @@ create function if not exists non_default_engine() returns varchar(255) deterministic begin -if default_engine() = 'innodb' then -return 'myisam'; +if default_engine() = 'InnoDB' then +return 'MyISAM'; end if; -return 'innodb'; +return 'InnoDB'; end~~ create table t1 ( x1 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start comment 'start', -Sys_end SYS_TRX_TYPE generated always as row end comment 'end', +Sys_start SYS_DATATYPE generated always as row start comment 'start', +Sys_end SYS_DATATYPE generated always as row end comment 'end', period for system_time (Sys_start, Sys_end) ) with system versioning; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `x1` int(10) unsigned DEFAULT NULL, - `Sys_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START COMMENT 'start', - `Sys_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END COMMENT 'end', + `Sys_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START COMMENT 'start', + `Sys_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END COMMENT 'end', PERIOD FOR SYSTEM_TIME (`Sys_start`, `Sys_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING # Implicit fields test create or replace table t1 ( x2 int unsigned @@ -30,28 +30,28 @@ show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `x2` int(10) unsigned DEFAULT NULL, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING create or replace table t1 ( x3 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start, -Sys_end SYS_TRX_TYPE generated always as row end, +Sys_start SYS_DATATYPE generated always as row start, +Sys_end SYS_DATATYPE generated always as row end, period for system_time (x, Sys_end) ) with system versioning; ERROR HY000: PERIOD FOR SYSTEM_TIME must use columns `Sys_start` and `Sys_end` create or replace table t1 ( x4 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start, -Sys_end2 SYS_TRX_TYPE generated always as row end, +Sys_start SYS_DATATYPE generated always as row start, +Sys_end2 SYS_DATATYPE generated always as row end, period for system_time (Sys_start, Sys_end) ) with system versioning; ERROR HY000: PERIOD FOR SYSTEM_TIME must use columns `Sys_start` and `Sys_end2` create or replace table t1 ( x5 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start, -Sys_end SYS_TRX_TYPE generated always as row end, +Sys_start SYS_DATATYPE generated always as row start, +Sys_end SYS_DATATYPE generated always as row end, period for system_time (Sys_start, x) ) with system versioning; ERROR HY000: PERIOD FOR SYSTEM_TIME must use columns `Sys_start` and `Sys_end` @@ -62,29 +62,29 @@ period for system_time (Sys_start, Sys_end) ERROR HY000: Wrong parameters for `t1`: missing 'AS ROW START' create or replace table t1 ( x7 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start, -Sys_end SYS_TRX_TYPE generated always as row end, +Sys_start SYS_DATATYPE generated always as row start, +Sys_end SYS_DATATYPE generated always as row end, period for system_time (Sys_start, Sys_end) ); ERROR HY000: Wrong parameters for `t1`: missing 'WITH SYSTEM VERSIONING' create or replace table t1 ( x8 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start, -Sys_end SYS_TRX_TYPE generated always as row end, +Sys_start SYS_DATATYPE generated always as row start, +Sys_end SYS_DATATYPE generated always as row end, period for system_time (sys_insert, sys_remove) ) with system versioning; ERROR HY000: PERIOD FOR SYSTEM_TIME must use columns `Sys_start` and `Sys_end` create or replace table t1 ( x9 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start, -Sys_end SYS_TRX_TYPE generated always as row end, +Sys_start SYS_DATATYPE generated always as row start, +Sys_end SYS_DATATYPE generated always as row end, period for system_time (Sys_start, Sys_end) ); ERROR HY000: Wrong parameters for `t1`: missing 'WITH SYSTEM VERSIONING' create or replace table t1 ( x10 int unsigned, -Sys_start SYS_TRX_TYPE generated always as row start, -Sys_end SYS_TRX_TYPE generated always as row end, +Sys_start SYS_DATATYPE generated always as row start, +Sys_end SYS_DATATYPE generated always as row end, period for system_time (Sys_start, Sys_start) ); ERROR HY000: Wrong parameters for `t1`: missing 'WITH SYSTEM VERSIONING' @@ -125,10 +125,10 @@ Table Create Table t1 CREATE TABLE `t1` ( `A1` int(11) DEFAULT NULL, `B` int(11) DEFAULT NULL WITHOUT SYSTEM VERSIONING, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING create or replace table t1 ( A2 int with system versioning, B int @@ -138,10 +138,10 @@ Table Create Table t1 CREATE TABLE `t1` ( `A2` int(11) DEFAULT NULL, `B` int(11) DEFAULT NULL, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING create or replace table t1 ( A3 int, B int without system versioning @@ -155,10 +155,10 @@ Table Create Table t1 CREATE TABLE `t1` ( `A4` int(11) DEFAULT NULL, `B` int(11) DEFAULT NULL WITHOUT SYSTEM VERSIONING, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING create or replace table t1 ( A5 int with system versioning, B int without system versioning @@ -168,10 +168,10 @@ Table Create Table t1 CREATE TABLE `t1` ( `A5` int(11) DEFAULT NULL, `B` int(11) DEFAULT NULL WITHOUT SYSTEM VERSIONING, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING create or replace table t1 ( A6 int with system versioning, B int without system versioning @@ -181,10 +181,10 @@ Table Create Table t1 CREATE TABLE `t1` ( `A6` int(11) DEFAULT NULL, `B` int(11) DEFAULT NULL WITHOUT SYSTEM VERSIONING, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING create or replace table t1 ( A7 int without system versioning ); @@ -198,10 +198,10 @@ show create table tt1; Table Create Table tt1 CREATE TABLE `tt1` ( `a` int(11) DEFAULT NULL, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING drop table tt1; create temporary table tt1 like t1; Warnings: @@ -211,86 +211,176 @@ show create table tt1; Table Create Table tt1 CREATE TEMPORARY TABLE `tt1` ( `a` int(11) DEFAULT NULL -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 +# CREATE TABLE ... SELECT create or replace table t1 (x int) with system versioning; create or replace table t0( y int, -st SYS_TRX_TYPE generated always as row start, -en SYS_TRX_TYPE generated always as row end, +st SYS_DATATYPE generated always as row start, +en SYS_DATATYPE generated always as row end, period for system_time (st, en) ) with system versioning; +## For non-versioned table: +### 1. implicit system fields are not included create or replace table t2 as select * from t1; show create table t2; Table Create Table t2 CREATE TABLE `t2` ( `x` int(11) DEFAULT NULL -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 +### 2. explicit system fields are included create or replace table t3 as select * from t0; +select * from t0; +y st en show create table t3; Table Create Table t3 CREATE TABLE `t3` ( - `y` int(11) DEFAULT NULL -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 + `y` int(11) DEFAULT NULL, + `st` SYS_DATATYPE, + `en` SYS_DATATYPE +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 +## For versioned table insert into t1 values (1); +select sys_trx_start from t1 into @sys_trx_start; insert into t0 values (2); +select st from t0 into @st; +### 1. implicit system fields are included as implicit create or replace table t2 with system versioning as select * from t1; show create table t2; Table Create Table t2 CREATE TABLE `t2` ( `x` int(11) DEFAULT NULL, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING -select * from t2; +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +#### sys_trx_start, sys_trx_end are copied; wildcard not expanded +select * from t2 where sys_trx_start = @sys_trx_start; x 1 +### 2. explicit system fields are included as non-system create or replace table t3 with system versioning as select * from t0; show create table t3; Table Create Table t3 CREATE TABLE `t3` ( `y` int(11) DEFAULT NULL, - `st` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `en` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, - PERIOD FOR SYSTEM_TIME (`st`, `en`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING + `st` SYS_DATATYPE, + `en` SYS_DATATYPE, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, + PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +#### st, en are plain fields now select * from t3 where y > 2; y st en +select y from t3 where st = @st and sys_trx_start > @st; +y +2 +### 3. explicit system fields are kept as system +create or replace table t3 ( +st SYS_DATATYPE generated always as row start, +en SYS_DATATYPE generated always as row end, +period for system_time (st, en) +) with system versioning as select * from t0; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `y` int(11) DEFAULT NULL, + `st` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `en` SYS_DATATYPE GENERATED ALWAYS AS ROW END, + PERIOD FOR SYSTEM_TIME (`st`, `en`) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +select y from t3 where st = @st; +y +2 +### 4. system fields not or wrongly selected +create or replace table t3 with system versioning select x from t1; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `x` int(11) DEFAULT NULL, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, + PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +select * from t3; +x +1 +create or replace table t3 with system versioning select x, sys_trx_start from t1; +ERROR HY000: Wrong parameters for `t3`: missing 'AS ROW END' +create or replace table t3 with system versioning select x, sys_trx_end from t1; +ERROR HY000: Wrong parameters for `t3`: missing 'AS ROW START' +# Prepare checking for historical row +delete from t1; +select sys_trx_end from t1 for system_time all into @sys_trx_end; delete from t0; -create or replace table t1 (x int) with system versioning; +select en from t0 for system_time all into @en; +## Combinations of versioned + non-versioned create or replace table t2 (y int); +insert into t2 values (3); create or replace table t3 with system versioning select * from t1 for system_time all, t2; show create table t3; Table Create Table t3 CREATE TABLE `t3` ( `x` int(11) DEFAULT NULL, - `sys_trx_start` SYS_TRX_TYPE GENERATED ALWAYS AS ROW START, - `sys_trx_end` SYS_TRX_TYPE GENERATED ALWAYS AS ROW END, + `sys_trx_start` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` SYS_DATATYPE GENERATED ALWAYS AS ROW END, `y` int(11) DEFAULT NULL, PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) -) ENGINE=INNODB_OR_MYISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING -create or replace table t2 with system versioning as select * from t0; -create or replace table t3 with system versioning select x, y, t1.sys_trx_start, t2.en from t1, t2; -ERROR HY000: Wrong parameters for `t3`: system fields selected from different tables +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +select * from t3 for system_time all where sys_trx_start = @sys_trx_start and sys_trx_end = @sys_trx_end; +x y +1 3 +create or replace table t2 like t0; insert into t2 values (1), (2); delete from t2 where y = 2; create or replace table t3 select * from t2 for system_time all; -select st, en from t2 where y = 1 into @st, @en; +select st, en from t3 where y = 1 into @st, @en; select y from t2 for system_time all where st = @st and en = @en; y 1 -select st, en from t2 for system_time all where y = 2 into @st, @en; +select st, en from t3 where y = 2 into @st, @en; select y from t2 for system_time all where st = @st and en = @en; y 2 -create or replace table t1 (a int) with system versioning engine INNODB_OR_MYISAM; -create or replace table t2 as select a, sys_trx_start, sys_trx_end from t1 for system_time all; -create or replace table t2 with system versioning engine INNODB_OR_MYISAM as select a, sys_trx_start, sys_trx_end from t1 for system_time all; -ERROR HY000: `sys_trx_start` must be of type SYS_TRX_TYPE for versioned table `t2` -create or replace table t1 (a int, id int) with system versioning engine INNODB_OR_MYISAM; +## Default engine detection +create or replace table t1 (a int) with system versioning engine NON_DEFAULT_ENGINE; +create or replace table t2 +as select a, sys_trx_start, sys_trx_end from t1 for system_time all; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL, + `sys_trx_start` NON_SYS_DATATYPE DEFAULT NULL, + `sys_trx_end` NON_SYS_DATATYPE DEFAULT NULL +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 +create or replace table t2 with system versioning +as select a, sys_trx_start, sys_trx_end from t1; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL, + `sys_trx_start` NON_SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` NON_SYS_DATATYPE GENERATED ALWAYS AS ROW END, + PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) +) ENGINE=NON_DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +create or replace table t2 with system versioning engine DEFAULT_ENGINE +as select a, sys_trx_start, sys_trx_end from t1 for system_time all; +ERROR HY000: `sys_trx_start` must be of type SYS_DATATYPE for versioned table `t2` +create or replace table t1 (a int, id int) with system versioning engine NON_DEFAULT_ENGINE; create or replace table t2 (b int, id int); -create or replace table t3 as -select t2.b, t1.a, t1.sys_trx_start, t1.sys_trx_end from t2 inner join t1 on t2.id=t1.id; +create or replace table t3 with system versioning +as select t2.b, t1.a, t1.sys_trx_start, t1.sys_trx_end from t2 inner join t1 on t2.id=t1.id; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `b` int(11) DEFAULT NULL, + `a` int(11) DEFAULT NULL, + `sys_trx_start` NON_SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `sys_trx_end` NON_SYS_DATATYPE GENERATED ALWAYS AS ROW END, + PERIOD FOR SYSTEM_TIME (`sys_trx_start`, `sys_trx_end`) +) ENGINE=NON_DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING +## Errors create or replace table t (sys_trx_start int); alter table t with system versioning; ERROR 42S21: Duplicate column name 'sys_trx_start' @@ -307,5 +397,43 @@ Sys_end timestamp(6) generated always as row end, period for system_time (Sys_start, Sys_end) ) with system versioning; ERROR HY000: Duplicate ROW START column `Sys_start` +## System fields detection +create or replace table t1 (x int) with system versioning; +create or replace table t2 ( +y int, +st SYS_DATATYPE generated always as row start, +en SYS_DATATYPE generated always as row end, +period for system_time (st, en) +) with system versioning; +create or replace table t3 +as select x, y, sys_trx_start, sys_trx_end, st, en from t1, t2; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `x` int(11) DEFAULT NULL, + `y` int(11) DEFAULT NULL, + `sys_trx_start` SYS_DATATYPE, + `sys_trx_end` SYS_DATATYPE, + `st` SYS_DATATYPE, + `en` SYS_DATATYPE +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 +create or replace table t3 ( +y int, +st SYS_DATATYPE generated always as row start, +en SYS_DATATYPE generated always as row end, +period for system_time (st, en) +) with system versioning +as select x, y, sys_trx_start, sys_trx_end, st, en from t1, t2; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `x` int(11) DEFAULT NULL, + `y` int(11) DEFAULT NULL, + `sys_trx_start` SYS_DATATYPE, + `sys_trx_end` SYS_DATATYPE, + `st` SYS_DATATYPE GENERATED ALWAYS AS ROW START, + `en` SYS_DATATYPE GENERATED ALWAYS AS ROW END, + PERIOD FOR SYSTEM_TIME (`st`, `en`) +) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING drop database test; create database test; diff --git a/mysql-test/suite/versioning/r/select_sp.result b/mysql-test/suite/versioning/r/select_sp.result index 01b511ffc68..4ac9009df7a 100644 --- a/mysql-test/suite/versioning/r/select_sp.result +++ b/mysql-test/suite/versioning/r/select_sp.result @@ -1,7 +1,7 @@ create procedure test_01() begin declare engine varchar(255) default default_engine(); -declare sys_type varchar(255) default sys_datatype(); +declare sys_type varchar(255) default sys_datatype(default_engine()); declare fields varchar(255) default sys_commit_ts('sys_start'); set @str= concat(' create table t1( @@ -55,7 +55,7 @@ end~~ create or replace procedure test_02() begin declare engine varchar(255) default default_engine(); -declare sys_type varchar(255) default sys_datatype(); +declare sys_type varchar(255) default sys_datatype(default_engine()); declare fields varchar(255) default sys_commit_ts('sys_start'); set @str0= concat('( x int, diff --git a/mysql-test/suite/versioning/t/create.test b/mysql-test/suite/versioning/t/create.test index b4d9ec9e1da..c55e5b36a8b 100644 --- a/mysql-test/suite/versioning/t/create.test +++ b/mysql-test/suite/versioning/t/create.test @@ -10,35 +10,39 @@ create function if not exists non_default_engine() returns varchar(255) deterministic begin - if default_engine() = 'innodb' then - return 'myisam'; + if default_engine() = 'InnoDB' then + return 'MyISAM'; end if; - return 'innodb'; + return 'InnoDB'; end~~ delimiter ;~~ ---let $sys_datatype= `select sys_datatype()` ---let $default_engine= `select default_engine()` ---let $non_default_engine= `select non_default_engine()` +let $non_default_engine= `select non_default_engine()`; +let $non_sys_datatype= `select sys_datatype(non_default_engine())`; +let $non_sys_datatype_uc= `select upper(sys_datatype(non_default_engine()))`; +let $sys_datatype_null= $sys_datatype NULL DEFAULT NULL; +let $sys_datatype_default_null= $sys_datatype DEFAULT NULL; +let $sys_datatype_not_null= $sys_datatype NOT NULL DEFAULT '0000-00-00 00:00:00.000000'; +let $non_sys_datatype_null= $non_sys_datatype NULL; ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE NULL '' eval create table t1 ( x1 int unsigned, Sys_start $sys_datatype generated always as row start comment 'start', Sys_end $sys_datatype generated always as row end comment 'end', period for system_time (Sys_start, Sys_end) ) with system versioning; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t1; --echo # Implicit fields test create or replace table t1 ( x2 int unsigned ) with system versioning; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t1; ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE --error ER_VERS_PERIOD_COLUMNS eval create or replace table t1 ( x3 int unsigned, @@ -47,7 +51,7 @@ eval create or replace table t1 ( period for system_time (x, Sys_end) ) with system versioning; ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE --error ER_VERS_PERIOD_COLUMNS eval create or replace table t1 ( x4 int unsigned, @@ -56,7 +60,7 @@ eval create or replace table t1 ( period for system_time (Sys_start, Sys_end) ) with system versioning; ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE --error ER_VERS_PERIOD_COLUMNS eval create or replace table t1 ( x5 int unsigned, @@ -71,7 +75,7 @@ create or replace table t1 ( period for system_time (Sys_start, Sys_end) ) with system versioning; ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE --error ER_MISSING eval create or replace table t1 ( x7 int unsigned, @@ -80,7 +84,7 @@ eval create or replace table t1 ( period for system_time (Sys_start, Sys_end) ); ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE --error ER_VERS_PERIOD_COLUMNS eval create or replace table t1 ( x8 int unsigned, @@ -89,7 +93,7 @@ eval create or replace table t1 ( period for system_time (sys_insert, sys_remove) ) with system versioning; ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE --error ER_MISSING eval create or replace table t1 ( x9 int unsigned, @@ -98,7 +102,7 @@ eval create or replace table t1 ( period for system_time (Sys_start, Sys_end) ); ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE --error ER_MISSING eval create or replace table t1 ( x10 int unsigned, @@ -145,14 +149,14 @@ create or replace table t1 ( A1 int with system versioning, B int ); ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t1; create or replace table t1 ( A2 int with system versioning, B int ) with system versioning; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t1; create or replace table t1 ( @@ -164,21 +168,21 @@ create or replace table t1 ( A4 int, B int without system versioning ) with system versioning; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t1; create or replace table t1 ( A5 int with system versioning, B int without system versioning ); ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t1; create or replace table t1 ( A6 int with system versioning, B int without system versioning ) with system versioning; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t1; create or replace table t1 ( @@ -193,17 +197,17 @@ create or replace table t1 ( # CREATE TABLE ... LIKE create or replace table t1 (a int) with system versioning; create table tt1 like t1; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table tt1; drop table tt1; create temporary table tt1 like t1; --echo # Temporary is stripped from versioning ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table tt1; -# CREATE TABLE ... SELECT +--echo # CREATE TABLE ... SELECT create or replace table t1 (x int) with system versioning; ---replace_result "bigint unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE eval create or replace table t0( y int, st $sys_datatype generated always as row start, @@ -211,70 +215,111 @@ eval create or replace table t0( period for system_time (st, en) ) with system versioning; -## For non-versioned table: -### 1. system fields are not inherited (hidden and not hidden) +--echo ## For non-versioned table: +--echo ### 1. implicit system fields are not included create or replace table t2 as select * from t1; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t2; +--echo ### 2. explicit system fields are included create or replace table t3 as select * from t0; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM +select * from t0; +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE $sys_datatype_null SYS_DATATYPE $sys_datatype_not_null SYS_DATATYPE $sys_datatype_default_null SYS_DATATYPE show create table t3; -### 2. hidden fields are inherited as hidden -### TODO: non-system hidden fields - -## For versioned table system fields are inherited as is. +--echo ## For versioned table insert into t1 values (1); +select sys_trx_start from t1 into @sys_trx_start; insert into t0 values (2); +select st from t0 into @st; +--echo ### 1. implicit system fields are included as implicit create or replace table t2 with system versioning as select * from t1; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE show create table t2; -# implicit system fields are hidden -select * from t2; +--echo #### sys_trx_start, sys_trx_end are copied; wildcard not expanded +select * from t2 where sys_trx_start = @sys_trx_start; +--echo ### 2. explicit system fields are included as non-system create or replace table t3 with system versioning as select * from t0; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE $sys_datatype_null SYS_DATATYPE $sys_datatype_not_null SYS_DATATYPE $sys_datatype_default_null SYS_DATATYPE show create table t3; -# explicit system fields are not hidden +--echo #### st, en are plain fields now select * from t3 where y > 2; +select y from t3 where st = @st and sys_trx_start > @st; -delete from t0; - -## Combinations of versioned + non-versioned -create or replace table t1 (x int) with system versioning; -create or replace table t2 (y int); -create or replace table t3 with system versioning select * from t1 for system_time all, t2; ---replace_result InnoDB INNODB_OR_MYISAM MyISAM INNODB_OR_MYISAM "bigint(20) unsigned" SYS_TRX_TYPE timestamp(6) SYS_TRX_TYPE +--echo ### 3. explicit system fields are kept as system +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE +eval create or replace table t3 ( + st $sys_datatype generated always as row start, + en $sys_datatype generated always as row end, + period for system_time (st, en) +) with system versioning as select * from t0; +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE $sys_datatype_null SYS_DATATYPE $sys_datatype_not_null SYS_DATATYPE $sys_datatype_default_null SYS_DATATYPE show create table t3; +select y from t3 where st = @st; -create or replace table t2 with system versioning as select * from t0; ---error ER_VERS_DIFFERENT_TABLES -create or replace table t3 with system versioning select x, y, t1.sys_trx_start, t2.en from t1, t2; +--echo ### 4. system fields not or wrongly selected +create or replace table t3 with system versioning select x from t1; +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE $sys_datatype_null SYS_DATATYPE $sys_datatype_not_null SYS_DATATYPE $sys_datatype_default_null SYS_DATATYPE +show create table t3; +select * from t3; +--error ER_MISSING +create or replace table t3 with system versioning select x, sys_trx_start from t1; +--error ER_MISSING +create or replace table t3 with system versioning select x, sys_trx_end from t1; +--echo # Prepare checking for historical row +delete from t1; +select sys_trx_end from t1 for system_time all into @sys_trx_end; +delete from t0; +select en from t0 for system_time all into @en; + +--echo ## Combinations of versioned + non-versioned +create or replace table t2 (y int); +insert into t2 values (3); +create or replace table t3 with system versioning select * from t1 for system_time all, t2; +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE +show create table t3; +select * from t3 for system_time all where sys_trx_start = @sys_trx_start and sys_trx_end = @sys_trx_end; + +create or replace table t2 like t0; insert into t2 values (1), (2); delete from t2 where y = 2; create or replace table t3 select * from t2 for system_time all; -select st, en from t2 where y = 1 into @st, @en; +select st, en from t3 where y = 1 into @st, @en; select y from t2 for system_time all where st = @st and en = @en; -select st, en from t2 for system_time all where y = 2 into @st, @en; +select st, en from t3 where y = 2 into @st, @en; select y from t2 for system_time all where st = @st and en = @en; ---replace_result innodb INNODB_OR_MYISAM myisam INNODB_OR_MYISAM +--echo ## Default engine detection +--replace_result $non_default_engine NON_DEFAULT_ENGINE $non_sys_datatype NON_SYS_DATATYPE eval create or replace table t1 (a int) with system versioning engine $non_default_engine; -create or replace table t2 as select a, sys_trx_start, sys_trx_end from t1 for system_time all; ---replace_result innodb INNODB_OR_MYISAM myisam INNODB_OR_MYISAM "BIGINT(20) UNSIGNED" SYS_TRX_TYPE "TIMESTAMP(6)" SYS_TRX_TYPE ---error ER_VERS_FIELD_WRONG_TYPE -eval create or replace table t2 with system versioning engine $default_engine as select a, sys_trx_start, sys_trx_end from t1 for system_time all; +create or replace table t2 +as select a, sys_trx_start, sys_trx_end from t1 for system_time all; +--replace_result $default_engine DEFAULT_ENGINE $non_sys_datatype NON_SYS_DATATYPE $non_sys_datatype_null NON_SYS_DATATYPE +show create table t2; ---replace_result innodb INNODB_OR_MYISAM myisam INNODB_OR_MYISAM +create or replace table t2 with system versioning +as select a, sys_trx_start, sys_trx_end from t1; +--replace_result $non_default_engine NON_DEFAULT_ENGINE $non_sys_datatype NON_SYS_DATATYPE +show create table t2; + +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype_uc SYS_DATATYPE +--error ER_VERS_FIELD_WRONG_TYPE +eval create or replace table t2 with system versioning engine $default_engine +as select a, sys_trx_start, sys_trx_end from t1 for system_time all; + +--replace_result $non_default_engine NON_DEFAULT_ENGINE eval create or replace table t1 (a int, id int) with system versioning engine $non_default_engine; create or replace table t2 (b int, id int); -create or replace table t3 as - select t2.b, t1.a, t1.sys_trx_start, t1.sys_trx_end from t2 inner join t1 on t2.id=t1.id; +create or replace table t3 with system versioning +as select t2.b, t1.a, t1.sys_trx_start, t1.sys_trx_end from t2 inner join t1 on t2.id=t1.id; +--replace_result $non_default_engine NON_DEFAULT_ENGINE $non_sys_datatype NON_SYS_DATATYPE $non_sys_datatype_null NON_SYS_DATATYPE +show create table t3; +--echo ## Errors create or replace table t (sys_trx_start int); --error ER_DUP_FIELDNAME alter table t with system versioning; @@ -295,5 +340,31 @@ create or replace table t1 ( period for system_time (Sys_start, Sys_end) ) with system versioning; +--echo ## System fields detection +create or replace table t1 (x int) with system versioning; +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE +eval create or replace table t2 ( + y int, + st $sys_datatype generated always as row start, + en $sys_datatype generated always as row end, + period for system_time (st, en) +) with system versioning; + +create or replace table t3 +as select x, y, sys_trx_start, sys_trx_end, st, en from t1, t2; +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE $sys_datatype_null SYS_DATATYPE $sys_datatype_not_null SYS_DATATYPE $sys_datatype_default_null SYS_DATATYPE +show create table t3; + +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE +eval create or replace table t3 ( + y int, + st $sys_datatype generated always as row start, + en $sys_datatype generated always as row end, + period for system_time (st, en) +) with system versioning +as select x, y, sys_trx_start, sys_trx_end, st, en from t1, t2; +--replace_result $default_engine DEFAULT_ENGINE $sys_datatype SYS_DATATYPE $sys_datatype_null SYS_DATATYPE $sys_datatype_not_null SYS_DATATYPE $sys_datatype_default_null SYS_DATATYPE +show create table t3; + drop database test; create database test; diff --git a/mysql-test/suite/versioning/t/select_sp.test b/mysql-test/suite/versioning/t/select_sp.test index 199e19484b5..eb1d15663bc 100644 --- a/mysql-test/suite/versioning/t/select_sp.test +++ b/mysql-test/suite/versioning/t/select_sp.test @@ -5,7 +5,7 @@ delimiter ~~; create procedure test_01() begin declare engine varchar(255) default default_engine(); - declare sys_type varchar(255) default sys_datatype(); + declare sys_type varchar(255) default sys_datatype(default_engine()); declare fields varchar(255) default sys_commit_ts('sys_start'); set @str= concat(' @@ -66,7 +66,7 @@ end~~ create or replace procedure test_02() begin declare engine varchar(255) default default_engine(); - declare sys_type varchar(255) default sys_datatype(); + declare sys_type varchar(255) default sys_datatype(default_engine()); declare fields varchar(255) default sys_commit_ts('sys_start'); set @str0= concat('( diff --git a/sql/handler.cc b/sql/handler.cc index 0193646984e..3cdabe9c1a9 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6738,21 +6738,21 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) DBUG_RETURN(res); } -bool Vers_parse_info::is_trx_start(const char *name) const +bool Vers_parse_info::is_start(const char *name) const { DBUG_ASSERT(name); return as_row.start && as_row.start == LString_i(name); } -bool Vers_parse_info::is_trx_end(const char *name) const +bool Vers_parse_info::is_end(const char *name) const { DBUG_ASSERT(name); return as_row.end && as_row.end == LString_i(name); } -bool Vers_parse_info::is_trx_start(const Create_field &f) const +bool Vers_parse_info::is_start(const Create_field &f) const { return f.flags & VERS_SYS_START_FLAG; } -bool Vers_parse_info::is_trx_end(const Create_field &f) const +bool Vers_parse_info::is_end(const Create_field &f) const { return f.flags & VERS_SYS_END_FLAG; } @@ -6818,143 +6818,40 @@ static bool vers_change_sys_field(THD *thd, const char *field_name, return false; } +const LString Vers_parse_info::default_start= "sys_trx_start"; +const LString Vers_parse_info::default_end= "sys_trx_end"; + bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, - bool integer_fields) + bool integer_fields, int *added) { // If user specified some of these he must specify the others too. Do nothing. - if (as_row.start || as_row.end || system_time.start || system_time.end) + if (*this) return false; alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; - static const LString sys_trx_start= "sys_trx_start"; - static const LString sys_trx_end= "sys_trx_end"; - - system_time= start_end_t(sys_trx_start, sys_trx_end); + system_time= start_end_t(default_start, default_end); as_row= system_time; - return vers_create_sys_field(thd, sys_trx_start, alter_info, - VERS_SYS_START_FLAG, - integer_fields) || - vers_create_sys_field(thd, sys_trx_end, alter_info, - VERS_SYS_END_FLAG, - integer_fields); -} - -bool Vers_parse_info::check_and_fix_implicit( - THD *thd, - Alter_info *alter_info, - HA_CREATE_INFO *create_info, - const char* table_name) -{ - DBUG_ASSERT(!without_system_versioning); - - SELECT_LEX &slex= thd->lex->select_lex; - int vers_tables= 0; - bool from_select= slex.item_list.elements ? true : false; - - if (from_select) + if (vers_create_sys_field(thd, default_start, alter_info, VERS_SYS_START_FLAG, integer_fields) || + vers_create_sys_field(thd, default_end, alter_info, VERS_SYS_END_FLAG, integer_fields)) { - for (TABLE_LIST *table= slex.table_list.first; table; table= table->next_local) - { - if (table->table && table->table->versioned()) - vers_tables++; - } - } - - // CREATE ... SELECT: if at least one table in SELECT is versioned, - // then created table will be versioned. - if (thd->variables.vers_force) - { - with_system_versioning= true; - create_info->options|= HA_VERSIONED_TABLE; - } - - // Possibly override default storage engine to match one used in source table. - if (from_select && with_system_versioning && - !(create_info->used_fields & HA_CREATE_USED_ENGINE)) - { - List_iterator_fast it(alter_info->create_list); - while (Create_field *f= it++) - { - if (is_trx_start(*f) || is_trx_end(*f)) - { - create_info->db_type= f->field->orig_table->file->ht; - break; - } - } - } - - if (!need_check()) - return false; - - if (!versioned_fields && unversioned_fields && !with_system_versioning) - { - // All is correct but this table is not versioned. - create_info->options&= ~HA_VERSIONED_TABLE; - return false; - } - - if ((system_time.start || system_time.end || as_row.start || as_row.end) && - !with_system_versioning) - { - my_error(ER_MISSING, MYF(0), table_name, "WITH SYSTEM VERSIONING"); return true; } + if (added) + *added+= 2; + return false; +} - TABLE *orig_table= NULL; - List_iterator it(alter_info->create_list); - while (Create_field *f= it++) - { - if (is_trx_start(*f)) - { - if (!as_row.start) // not inited in CREATE ... SELECT - { - DBUG_ASSERT(vers_tables > 0); - if (orig_table && orig_table != f->field->orig_table) - { - err_different_tables: - my_error(ER_VERS_DIFFERENT_TABLES, MYF(0), table_name); - return true; - } - orig_table= f->field->orig_table; - as_row.start= f->field_name; - system_time.start= as_row.start; - } - continue; - } - if (is_trx_end(*f)) - { - if (!as_row.end) - { - DBUG_ASSERT(vers_tables > 0); - if (orig_table && orig_table != f->field->orig_table) - { - goto err_different_tables; - } - orig_table= f->field->orig_table; - as_row.end= f->field_name; - system_time.end= as_row.end; - } - continue; - } - - if ((f->versioning == Column_definition::VERSIONING_NOT_SET && - !with_system_versioning) || - f->versioning == Column_definition::WITHOUT_VERSIONING) - { - f->flags|= VERS_UPDATE_UNVERSIONED_FLAG; - } - } - - bool integer_fields= ha_check_storage_engine_flag(create_info->db_type, +bool Table_scope_and_contents_source_st::vers_native(THD *thd) const +{ + bool integer_fields= ha_check_storage_engine_flag(db_type, HTON_NATIVE_SYS_VERSIONING); #ifdef WITH_PARTITION_STORAGE_ENGINE if (partition_info *info= thd->work_part_info) { - if (!(create_info->used_fields & HA_CREATE_USED_ENGINE) && - info->partitions.elements) + if (!(used_fields & HA_CREATE_USED_ENGINE) && info->partitions.elements) { partition_element *element= static_cast(info->partitions.elem(0)); @@ -6966,16 +6863,174 @@ bool Vers_parse_info::check_and_fix_implicit( } } #endif + return integer_fields; +} - if (fix_implicit(thd, alter_info, integer_fields)) +bool Table_scope_and_contents_source_st::vers_fix_system_fields( + THD *thd, + Alter_info *alter_info, + const TABLE_LIST &create_table, + const TABLE_LIST *select_tables, + List *items, + bool *versioned_write) +{ + DBUG_ASSERT(!vers_info.without_system_versioning); + int vers_tables= 0; + + if (select_tables) + { + for (const TABLE_LIST *table= select_tables; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + vers_tables++; + } + } + + // CREATE ... SELECT: if at least one table in SELECT is versioned, + // then created table will be versioned. + if (thd->variables.vers_force) + { + vers_info.with_system_versioning= true; + options|= HA_VERSIONED_TABLE; + } + + // Possibly override default storage engine to match one used in source table. + if (vers_tables && vers_info.with_system_versioning && + !(used_fields & HA_CREATE_USED_ENGINE)) + { + List_iterator_fast it(alter_info->create_list); + while (Create_field *f= it++) + { + if (vers_info.is_start(*f) || vers_info.is_end(*f)) + { + if (f->field) + { + db_type= f->field->orig_table->file->ht; + } + break; + } + } + } + + if (!vers_info.need_check()) + return false; + + if (!vers_info.versioned_fields && + vers_info.unversioned_fields && + !vers_info.with_system_versioning) + { + // All is correct but this table is not versioned. + options&= ~HA_VERSIONED_TABLE; + return false; + } + + if (!vers_info.with_system_versioning && vers_info) + { + my_error(ER_MISSING, MYF(0), create_table.table_name, "WITH SYSTEM VERSIONING"); + return true; + } + + if (vers_tables) + { + DBUG_ASSERT(options & HA_VERSIONED_TABLE); + DBUG_ASSERT(versioned_write); + *versioned_write= true; + } + + List_iterator it(alter_info->create_list); + bool explicit_declared= vers_info.as_row.start || vers_info.as_row.end; + while (Create_field *f= it++) + { + if ((f->versioning == Column_definition::VERSIONING_NOT_SET && + !vers_info.with_system_versioning) || + f->versioning == Column_definition::WITHOUT_VERSIONING) + { + f->flags|= VERS_UPDATE_UNVERSIONED_FLAG; + } + + /* Assign selected implicit fields when no explicit fields */ + if (!vers_tables || explicit_declared) + continue; + + DBUG_ASSERT(versioned_write); + if (vers_info.is_start(*f) && + vers_info.default_start == f->field_name) + { + if (vers_info.as_row.start) + it.remove(); + else + { + vers_info.set_start(f->field_name); + *versioned_write= false; + } + continue; + } + if (vers_info.is_end(*f) && + vers_info.default_end == f->field_name) + { + if (vers_info.as_row.end) + it.remove(); + else + { + vers_info.set_end(f->field_name); + *versioned_write= false; + } + continue; + } + } // while (Create_field *f= it++) + + /* Assign selected system fields to explicit system fields if any */ + if (vers_tables) + { + it.rewind(); + while (Create_field *f= it++) + { + uint flags_left= VERS_SYSTEM_FIELD; + if (flags_left && (vers_info.is_start(*f) || vers_info.is_end(*f)) && !f->field) + { + uint sys_flag= f->flags & flags_left; + flags_left-= sys_flag; + List_iterator_fast it2(*items); + while (Item *item= it2++) + { + if (item->type() != Item::FIELD_ITEM) + continue; + Field *fld= static_cast(item)->field; + DBUG_ASSERT(fld); + if ((fld->flags & sys_flag) && + LString_i(f->field_name) == fld->field_name) + { + f->field= fld; + *versioned_write= false; + } + } // while (item) + } // if (flags_left ... + } // while (Create_field *f= it++) + } // if (vers_tables) + + bool integer_fields= vers_native(thd); + int added= 0; + if (vers_info.fix_implicit(thd, alter_info, integer_fields, &added)) return true; - int plain_cols= 0; // column doesn't have WITH or WITHOUT SYSTEM VERSIONING - int vers_cols= 0; // column has WITH SYSTEM VERSIONING + DBUG_ASSERT(added >= 0); + if (vers_tables) + { + DBUG_ASSERT(items); + while (added--) + { + items->push_back( + new (thd->mem_root) Item_default_value(thd, thd->lex->current_context()), + thd->mem_root); + } + } + + int plain_cols= 0; // columns don't have WITH or WITHOUT SYSTEM VERSIONING + int vers_cols= 0; // columns have WITH SYSTEM VERSIONING it.rewind(); while (const Create_field *f= it++) { - if (is_trx_start(*f) || is_trx_end(*f)) + if (vers_info.is_start(*f) || vers_info.is_end(*f)) continue; if (f->versioning == Column_definition::VERSIONING_NOT_SET) @@ -6984,21 +7039,18 @@ bool Vers_parse_info::check_and_fix_implicit( vers_cols++; } - bool table_with_system_versioning= - as_row.start || as_row.end || system_time.start || system_time.end; - if (!thd->lex->tmp_table() && // CREATE from SELECT (Create_fields are not yet added) - !from_select && + !select_tables && vers_cols == 0 && - (plain_cols == 0 || !table_with_system_versioning)) + (plain_cols == 0 || !vers_info)) { - my_error(ER_VERS_NO_COLS_DEFINED, MYF(0), table_name); + my_error(ER_VERS_NO_COLS_DEFINED, MYF(0), create_table.table_name); return true; } - return check_with_conditions(table_name) || - check_generated_type(table_name, alter_info, integer_fields); + return vers_info.check_with_conditions(create_table.table_name) || + vers_info.check_generated_type(create_table.table_name, alter_info, integer_fields); } static bool add_field_to_drop_list(THD *thd, Alter_info *alter_info, @@ -7035,13 +7087,12 @@ static bool is_adding_primary_key(Alter_info *alter_info) return false; } -bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, +bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE *table) { TABLE_SHARE *share= table->s; - bool integer_fields= ha_check_storage_engine_flag(create_info->db_type, - HTON_NATIVE_SYS_VERSIONING); + bool integer_fields= create_info->vers_native(thd); const char *table_name= share->table_name.str; if (!need_check() && !share->versioned) @@ -7184,12 +7235,12 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, { const char *name= d->name; Field *f= NULL; - if (!done_start && is_trx_start(name)) + if (!done_start && is_start(name)) { f= share->vers_start_field(); done_start= true; } - else if (!done_end && is_trx_end(name)) + else if (!done_end && is_end(name)) { f= share->vers_end_field(); done_end= true; @@ -7316,7 +7367,7 @@ bool Vers_parse_info::check_generated_type(const char *table_name, List_iterator it(alter_info->create_list); while (Create_field *f= it++) { - if (is_trx_start(*f) || is_trx_end(*f)) + if (is_start(*f) || is_end(*f)) { if (integer_fields) { diff --git a/sql/handler.h b/sql/handler.h index 6fbb61c902c..44e18e17a1e 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1735,18 +1735,33 @@ struct Vers_parse_info start_end_t system_time; start_end_t as_row; - void set_period_for_system_time(LString start, LString end) + void set_system_time(LString start, LString end) { system_time.start= start; system_time.end= end; } -private: - bool is_trx_start(const char *name) const; - bool is_trx_end(const char *name) const; - bool is_trx_start(const Create_field &f) const; - bool is_trx_end(const Create_field &f) const; - bool fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields); +protected: + friend struct Table_scope_and_contents_source_st; + void set_start(const LEX_CSTRING field_name) + { + as_row.start= field_name; + system_time.start= field_name; + } + void set_end(const LEX_CSTRING field_name) + { + as_row.end= field_name; + system_time.end= field_name; + } + bool is_start(const char *name) const; + bool is_end(const char *name) const; + bool is_start(const Create_field &f) const; + bool is_end(const Create_field &f) const; + bool fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields, int *added= NULL); + operator bool() const + { + return as_row.start || as_row.end || system_time.start || system_time.end; + } bool need_check() const { return @@ -1754,40 +1769,30 @@ private: unversioned_fields || with_system_versioning || without_system_versioning || - system_time.start || - system_time.end || - as_row.start || - as_row.end; + *this; } bool check_with_conditions(const char *table_name) const; bool check_generated_type(const char *table_name, Alter_info *alter_info, bool integer_fields) const; public: - bool check_and_fix_implicit(THD *thd, Alter_info *alter_info, - HA_CREATE_INFO *create_info, - const char *table_name); - bool check_and_fix_alter(THD *thd, Alter_info *alter_info, - HA_CREATE_INFO *create_info, TABLE *table); + static const LString default_start; + static const LString default_end; + + bool fix_alter_info(THD *thd, Alter_info *alter_info, + HA_CREATE_INFO *create_info, TABLE *table); bool fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_info, TABLE_LIST &src_table, TABLE_LIST &table); - /** User has added 'WITH SYSTEM VERSIONING' to table definition */ + /** Table definition has 'WITH/WITHOUT SYSTEM VERSIONING' */ bool with_system_versioning : 1; - - /** Use has added 'WITHOUT SYSTEM VERSIONING' to ALTER TABLE */ bool without_system_versioning : 1; /** - At least one field was specified 'WITH SYSTEM VERSIONING'. Useful for - error handling. + At least one field was specified 'WITH/WITHOUT SYSTEM VERSIONING'. + Useful for error handling. */ bool versioned_fields : 1; - - /** - At least one field was specified 'WITHOUT SYSTEM VERSIONING'. Useful for - error handling. - */ bool unversioned_fields : 1; }; @@ -1867,6 +1872,14 @@ struct Table_scope_and_contents_source_st Vers_parse_info vers_info; + bool vers_fix_system_fields(THD *thd, Alter_info *alter_info, + const TABLE_LIST &create_table, + const TABLE_LIST *select_table= NULL, + List *items= NULL, + bool *versioned_write= NULL); + + bool vers_native(THD *thd) const; + void init() { bzero(this, sizeof(*this)); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4f4ea2a864c..8983c3646b7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7642,10 +7642,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, enum_sql_command sql_command= thd->lex->sql_command; unsigned int create_options= thd->lex->create_info.options; - if ( - sql_command == SQLCOM_CREATE_TABLE ? - sys_field && !(create_options & HA_VERSIONED_TABLE) : ( - sys_field ? + if (sys_field ? (sql_command == SQLCOM_CREATE_VIEW || slex->nest_level > 0 || vers_hide == VERS_HIDE_FULL || @@ -7654,8 +7651,10 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, (vers_hide == VERS_HIDE_AUTO && ( vers_type == FOR_SYSTEM_TIME_UNSPECIFIED || vers_type == FOR_SYSTEM_TIME_AS_OF))))) : - (fl & HIDDEN_FLAG))) + (fl & HIDDEN_FLAG)) { + if (sql_command != SQLCOM_CREATE_TABLE || + !(create_options & HA_VERSIONED_TABLE)) continue; } } diff --git a/sql/sql_class.h b/sql/sql_class.h index 3cc114bd366..51a53ea30b3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5062,6 +5062,7 @@ class select_insert :public select_result_interceptor { ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not COPY_INFO info; bool insert_into_view; + bool versioned_write; select_insert(THD *thd_arg, TABLE_LIST *table_list_par, TABLE *table_par, List *fields_par, List *update_fields, List *update_values, @@ -5122,6 +5123,12 @@ public: const THD *get_thd(void) { return thd; } const HA_CREATE_INFO *get_create_info() { return create_info; }; int prepare2(void) { return 0; } + +private: + TABLE *create_table_from_items(THD *thd, + List *items, + MYSQL_LOCK **lock, + TABLEOP_HOOKS *hooks); }; #include diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 950635d74e3..4acfc0cbf4a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3608,7 +3608,8 @@ select_insert::select_insert(THD *thd_arg, TABLE_LIST *table_list_par, select_result_interceptor(thd_arg), table_list(table_list_par), table(table_par), fields(fields_par), autoinc_value_of_last_inserted_row(0), - insert_into_view(table_list_par && table_list_par->view != 0) + insert_into_view(table_list_par && table_list_par->view != 0), + versioned_write(false) { bzero((char*) &info,sizeof(info)); info.handle_duplicates= duplic; @@ -3643,6 +3644,8 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) check_insert_fields(thd, table_list, *fields, values, !insert_into_view, 1, &map)); + versioned_write= table_list->table->versioned(); + if (!res && fields->elements) { bool saved_abort_on_warning= thd->abort_on_warning; @@ -3846,8 +3849,6 @@ int select_insert::send_data(List &values) DBUG_RETURN(0); thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields - if (table->versioned()) - table->vers_update_fields(); store_values(values); if (table->default_field && table->update_default_fields(0, info.ignore)) DBUG_RETURN(1); @@ -3857,6 +3858,9 @@ int select_insert::send_data(List &values) table->auto_increment_field_not_null= FALSE; DBUG_RETURN(1); } + table->vers_write= versioned_write; + if (table->versioned()) + table->vers_update_fields(); if (table_list) // Not CREATE ... SELECT { switch (table_list->view_check_option(thd, info.ignore)) { @@ -3868,6 +3872,7 @@ int select_insert::send_data(List &values) } error= write_record(thd, table, &info); + table->vers_write= table->versioned(); table->auto_increment_field_not_null= FALSE; if (!error) @@ -4141,10 +4146,7 @@ Field *Item::create_field_for_create_select(TABLE *table) @retval 0 Error */ -static TABLE *create_table_from_items(THD *thd, - Table_specification_st *create_info, - TABLE_LIST *create_table, - Alter_info *alter_info, +TABLE *select_create::create_table_from_items(THD *thd, List *items, MYSQL_LOCK **lock, TABLEOP_HOOKS *hooks) @@ -4205,8 +4207,8 @@ static TABLE *create_table_from_items(THD *thd, alter_info->create_list.push_back(cr_field, thd->mem_root); } - if (create_info->vers_info.check_and_fix_implicit( - thd, alter_info, create_info, create_table->table_name)) + if (create_info->vers_fix_system_fields(thd, alter_info, *create_table, + select_tables, items, &versioned_write)) { DBUG_RETURN(NULL); } @@ -4417,10 +4419,7 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) DEBUG_SYNC(thd,"create_table_select_before_check_if_exists"); - if (!(table= create_table_from_items(thd, create_info, - create_table, - alter_info, &values, - &extra_lock, hook_ptr))) + if (!(table= create_table_from_items(thd, &values, &extra_lock, hook_ptr))) /* abort() deletes table */ DBUG_RETURN(-1); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a255c477cee..8a718b62d5c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4243,8 +4243,7 @@ mysql_execute_command(THD *thd) } else { - if (create_info.vers_info.check_and_fix_implicit( - thd, &alter_info, &create_info, create_table->table_name)) + if (create_info.vers_fix_system_fields(thd, &alter_info, *create_table)) { goto end_with_restore_list; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 184cad22f67..f053bea6b40 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4002,7 +4002,9 @@ void JOIN::exec_inner() while (Item *item= it++) { Item_transformer transformer= &Item::vers_transformer; - it.replace(item->transform(thd, transformer, NULL)); + Item *new_item= item->transform(thd, transformer, NULL); + if (new_item) // Item_default_value::transform() may return NULL + it.replace(new_item); } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ba5eb36173b..6008e0bb77a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9024,7 +9024,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, if (check_engine(thd, alter_ctx.new_db, alter_ctx.new_name, create_info)) DBUG_RETURN(true); - if (create_info->vers_info.check_and_fix_alter(thd, alter_info, create_info, + if (create_info->vers_info.fix_alter_info(thd, alter_info, create_info, table)) { DBUG_RETURN(true); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6e34e1974a1..3d637105b3a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6451,7 +6451,7 @@ period_for_system_time: PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' ident ',' ident ')' { Vers_parse_info &info= Lex->vers_get_info(); - info.set_period_for_system_time($4, $6); + info.set_system_time($4, $6); } ; diff --git a/sql/table.cc b/sql/table.cc index 72acfc5a9ee..5ca8e32a6a2 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -7742,19 +7742,24 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors) void TABLE::vers_update_fields() { - DBUG_ENTER("vers_update_fields"); + bitmap_set_bit(write_set, vers_start_field()->field_index); + bitmap_set_bit(write_set, vers_end_field()->field_index); if (versioned_by_sql()) { - bitmap_set_bit(write_set, vers_start_field()->field_index); + if (!vers_write) + return; if (vers_start_field()->set_time()) DBUG_ASSERT(0); } + else + { + vers_start_field()->set_notnull(); + if (!vers_write) + return; + } - bitmap_set_bit(write_set, vers_end_field()->field_index); vers_end_field()->set_max(); - - DBUG_VOID_RETURN; } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 2fc1b00851e..8c8b02326f1 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -8397,10 +8397,7 @@ no_commit: innobase_srv_conc_enter_innodb(m_prebuilt); vers_set_fields = - (table->versioned_write() - && (sql_command != SQLCOM_CREATE_TABLE || table->s->vtmd)) - ? ROW_INS_VERSIONED - : ROW_INS_NORMAL; + table->versioned_write() ? ROW_INS_VERSIONED : ROW_INS_NORMAL; /* Step-5: Execute insert graph that will result in actual insert. */ error = row_insert_for_mysql((byte*) record, m_prebuilt, vers_set_fields);