MDEV-14820 System versioning is applied incorrectly to CTEs

Make sure that SELECT_LEX_UNIT::derived, behaves as documented
(points to the "TABLE_LIST representing this union in the
embedding select"). For recursive CTE this was not necessarily
the case, it could've pointed to the TABLE_LIST inside the CTE,
not in the embedding select.

To fix:
* don't update unit->derived in mysql_derived_prepare(), pass derived
  as an argument to st_select_lex_unit::prepare()
* prefer to set unit->derived in TABLE_LIST::init_derived()
  to the TABLE_LIST in the embedding select, not to the recursive
  reference. Fail if there are many TABLE_LISTs in the embedding
  select with conflicting FOR SYSTEM_TIME clauses.

cleanup:
* remove redundant THD* argument from st_select_lex_unit::prepare()
This commit is contained in:
Sergei Golubchik 2018-04-18 14:29:48 +02:00
parent 0f956a0676
commit 531acda484
14 changed files with 431 additions and 69 deletions

View File

@ -9,4 +9,3 @@
# Do not use any TAB characters for whitespace. # Do not use any TAB characters for whitespace.
# #
############################################################################## ##############################################################################
cte: MDEV-14820

View File

@ -1,9 +1,9 @@
set time_zone="+00:00";
set default_storage_engine=innodb; set default_storage_engine=innodb;
create or replace table dept ( create or replace table dept (
dept_id int(10) primary key, dept_id int(10) primary key,
name varchar(100) name varchar(100)
) ) with system versioning;
with system versioning;
create or replace table emp ( create or replace table emp (
emp_id int(10) primary key, emp_id int(10) primary key,
dept_id int(10) not null, dept_id int(10) not null,
@ -18,16 +18,51 @@ constraint `mgr-fk`
foreign key (mgr) references emp (emp_id) foreign key (mgr) references emp (emp_id)
on delete restrict on delete restrict
on update restrict on update restrict
) ) with system versioning;
with system versioning;
insert into dept (dept_id, name) values (10, "accounting"); insert into dept (dept_id, name) values (10, "accounting");
insert into emp (emp_id, name, salary, dept_id, mgr) values insert into emp (emp_id, name, salary, dept_id, mgr) values
(1, "bill", 1000, 10, null), (1, "bill", 1000, 10, null),
(20, "john", 500, 10, 1), (20, "john", 500, 10, 1),
(30, "jane", 750, 10,1 ); (30, "jane", 750, 10,1 );
select max(sys_trx_start) into @ts_1 from emp; select row_start into @ts_1 from emp where name="jane";
update emp set mgr=30 where name ="john"; update emp set mgr=30 where name ="john";
select sys_trx_start into @ts_2 from emp where name="john"; explain extended
with ancestors as (
select e.emp_id, e.name, e.mgr, e.salary from emp as e where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary from emp as e
) select * from ancestors for system_time as of @ts_1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 8 100.00
2 DERIVED e ALL NULL NULL NULL NULL 4 100.00 Using where
3 UNION e ALL NULL NULL NULL NULL 4 100.00 Using where
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
Warnings:
Note 1003 with ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` where `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors`
select row_start into @ts_2 from emp where name="john";
explain extended /* All report to 'Bill' */
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_1 as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_1 as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 4 100.00
2 DERIVED e ALL NULL NULL NULL NULL 4 100.00 Using where
3 RECURSIVE UNION e ALL mgr-fk NULL NULL NULL 4 100.00 Using where
3 RECURSIVE UNION <derived2> ref key0 key0 4 test.e.mgr 2 100.00
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
Warnings:
Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors`
/* All report to 'Bill' */ /* All report to 'Bill' */
with recursive with recursive
ancestors ancestors
@ -36,7 +71,7 @@ as
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_1 as e from emp for system_time as of timestamp @ts_1 as e
where name = 'bill' where name = 'bill'
union union
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_1 as e, from emp for system_time as of timestamp @ts_1 as e,
ancestors as a ancestors as a
@ -47,25 +82,186 @@ emp_id name mgr salary
1 bill NULL 1000 1 bill NULL 1000
20 john 1 500 20 john 1 500
30 jane 1 750 30 jane 1 750
/* Expected 3 rows */ explain extended with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of timestamp @ts_1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 4 100.00
2 DERIVED e ALL NULL NULL NULL NULL 4 100.00 Using where
3 RECURSIVE UNION e ALL mgr-fk NULL NULL NULL 4 100.00 Using where
3 RECURSIVE UNION <derived2> ref key0 key0 4 test.e.mgr 2 100.00
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
Warnings:
Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors`
with recursive with recursive
ancestors ancestors
as as
( (
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_2 as e from emp as e
where name = 'bill' where name = 'bill'
union union
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_2 as e, from emp as e,
ancestors as a ancestors as a
where e.mgr = a.emp_id where e.mgr = a.emp_id
) )
select * from ancestors; select * from ancestors for system_time as of timestamp @ts_1;
emp_id name mgr salary emp_id name mgr salary
1 bill NULL 1000 1 bill NULL 1000
20 john 1 500
30 jane 1 750 30 jane 1 750
20 john 30 500 explain extended with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select name from emp where emp_id in (select emp_id from ancestors for system_time as of timestamp @ts_1);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <subquery4> ALL distinct_key NULL NULL NULL 4 100.00
1 PRIMARY emp ALL PRIMARY NULL NULL NULL 4 75.00 Using where; Using join buffer (flat, BNL join)
4 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 4 100.00
2 DERIVED e ALL NULL NULL NULL NULL 4 100.00 Using where
3 RECURSIVE UNION e ALL mgr-fk NULL NULL NULL 4 100.00 Using where
3 RECURSIVE UNION <derived2> ref key0 key0 4 test.e.mgr 2 100.00
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
Warnings:
Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME ALL `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `test`.`emp`.`name` AS `name` from `test`.`emp` FOR SYSTEM_TIME ALL semi join (`ancestors`) where `test`.`emp`.`emp_id` = `ancestors`.`emp_id` and `test`.`emp`.`row_end` = TIMESTAMP'2038-01-19 03:14:07.999999'
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select name from emp where emp_id in (select emp_id from ancestors for system_time as of timestamp @ts_1);
name
bill
john
jane
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of @ts_2,
ancestors for system_time as of @ts_2 a2;
emp_id name mgr salary emp_id name mgr salary
1 bill NULL 1000 1 bill NULL 1000
30 jane 1 750 1 bill NULL 1000
20 john 30 500 1 bill NULL 1000
1 bill NULL 1000 30 jane 1 750
30 jane 1 750 30 jane 1 750
20 john 30 500 30 jane 1 750
1 bill NULL 1000 20 john 30 500
30 jane 1 750 20 john 30 500
20 john 30 500 20 john 30 500
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of @ts_2,
ancestors for system_time as of now() a2;
ERROR HY000: Conflicting FOR SYSTEM_TIME clauses in WITH RECURSIVE
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors,
ancestors for system_time as of @ts_2 a2;
ERROR HY000: Conflicting FOR SYSTEM_TIME clauses in WITH RECURSIVE
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of @ts_2,
ancestors a2;
ERROR HY000: Conflicting FOR SYSTEM_TIME clauses in WITH RECURSIVE
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of @ts_2
where emp_id in (select * from ancestors);
ERROR HY000: Conflicting FOR SYSTEM_TIME clauses in WITH RECURSIVE
# SYSTEM_TIME to internal recursive instance is prohibited
with recursive cte as
(
select * from emp
union all
select * from cte for system_time as of @ts_1
)
select * from cte;
ERROR HY000: Table `cte` is not system-versioned
create or replace table emp ( emp_id int, name varchar(127), mgr int) with system versioning; create or replace table emp ( emp_id int, name varchar(127), mgr int) with system versioning;
create or replace table addr ( emp_id int, address varchar(100)) with system versioning; create or replace table addr ( emp_id int, address varchar(100)) with system versioning;
insert emp values (1, 'bill', 0), (2, 'bill', 1), (3, 'kate', 1); insert emp values (1, 'bill', 0), (2, 'bill', 1), (3, 'kate', 1);

View File

@ -1,10 +1,10 @@
-- source include/have_innodb.inc -- source include/have_innodb.inc
set time_zone="+00:00";
set default_storage_engine=innodb; set default_storage_engine=innodb;
create or replace table dept ( create or replace table dept (
dept_id int(10) primary key, dept_id int(10) primary key,
name varchar(100) name varchar(100)
) ) with system versioning;
with system versioning;
create or replace table emp ( create or replace table emp (
emp_id int(10) primary key, emp_id int(10) primary key,
@ -20,21 +20,29 @@ create or replace table emp (
foreign key (mgr) references emp (emp_id) foreign key (mgr) references emp (emp_id)
on delete restrict on delete restrict
on update restrict on update restrict
) ) with system versioning;
with system versioning;
insert into dept (dept_id, name) values (10, "accounting"); insert into dept (dept_id, name) values (10, "accounting");
insert into emp (emp_id, name, salary, dept_id, mgr) values insert into emp (emp_id, name, salary, dept_id, mgr) values
(1, "bill", 1000, 10, null), (1, "bill", 1000, 10, null),
(20, "john", 500, 10, 1), (20, "john", 500, 10, 1),
(30, "jane", 750, 10,1 ); (30, "jane", 750, 10,1 );
select max(sys_trx_start) into @ts_1 from emp; select row_start into @ts_1 from emp where name="jane";
update emp set mgr=30 where name ="john"; update emp set mgr=30 where name ="john";
select sys_trx_start into @ts_2 from emp where name="john";
explain extended
with ancestors as (
select e.emp_id, e.name, e.mgr, e.salary from emp as e where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary from emp as e
) select * from ancestors for system_time as of @ts_1;
select row_start into @ts_2 from emp where name="john";
let $q=
/* All report to 'Bill' */ /* All report to 'Bill' */
with recursive with recursive
ancestors ancestors
@ -43,7 +51,7 @@ as
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_1 as e from emp for system_time as of timestamp @ts_1 as e
where name = 'bill' where name = 'bill'
union union
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_1 as e, from emp for system_time as of timestamp @ts_1 as e,
ancestors as a ancestors as a
@ -51,21 +59,138 @@ as
) )
select * from ancestors; select * from ancestors;
/* Expected 3 rows */ eval explain extended $q;
eval $q;
let $q=with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of timestamp @ts_1;
eval explain extended $q;
eval $q;
let $q=with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select name from emp where emp_id in (select emp_id from ancestors for system_time as of timestamp @ts_1);
eval explain extended $q;
eval $q;
with recursive with recursive
ancestors ancestors
as as
( (
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_2 as e from emp as e
where name = 'bill' where name = 'bill'
union union
select e.emp_id, e.name, e.mgr, e.salary select e.emp_id, e.name, e.mgr, e.salary
from emp for system_time as of timestamp @ts_2 as e, from emp as e,
ancestors as a ancestors as a
where e.mgr = a.emp_id where e.mgr = a.emp_id
) )
select * from ancestors; select * from ancestors for system_time as of @ts_2,
ancestors for system_time as of @ts_2 a2;
--error ER_CONFLICTING_FOR_SYSTEM_TIME
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of @ts_2,
ancestors for system_time as of now() a2;
--error ER_CONFLICTING_FOR_SYSTEM_TIME
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors,
ancestors for system_time as of @ts_2 a2;
--error ER_CONFLICTING_FOR_SYSTEM_TIME
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of @ts_2,
ancestors a2;
--error ER_CONFLICTING_FOR_SYSTEM_TIME
with recursive
ancestors
as
(
select e.emp_id, e.name, e.mgr, e.salary
from emp as e
where name = 'bill'
union
select e.emp_id, e.name, e.mgr, e.salary
from emp as e,
ancestors as a
where e.mgr = a.emp_id
)
select * from ancestors for system_time as of @ts_2
where emp_id in (select * from ancestors);
--echo # SYSTEM_TIME to internal recursive instance is prohibited
--error ER_VERS_NOT_VERSIONED
with recursive cte as
(
select * from emp
union all
select * from cte for system_time as of @ts_1
)
select * from cte;
create or replace table emp ( emp_id int, name varchar(127), mgr int) with system versioning; create or replace table emp ( emp_id int, name varchar(127), mgr int) with system versioning;
create or replace table addr ( emp_id int, address varchar(100)) with system versioning; create or replace table addr ( emp_id int, address varchar(100)) with system versioning;

View File

@ -3674,7 +3674,7 @@ int subselect_single_select_engine::prepare(THD *thd)
int subselect_union_engine::prepare(THD *thd_arg) int subselect_union_engine::prepare(THD *thd_arg)
{ {
set_thd(thd_arg); set_thd(thd_arg);
return unit->prepare(thd, result, SELECT_NO_UNLOCK); return unit->prepare(unit->derived, result, SELECT_NO_UNLOCK);
} }
int subselect_uniquesubquery_engine::prepare(THD *) int subselect_uniquesubquery_engine::prepare(THD *)

View File

@ -7859,8 +7859,8 @@ ER_VERS_ALTER_ENGINE_PROHIBITED
ER_VERS_RANGE_PROHIBITED ER_VERS_RANGE_PROHIBITED
eng "SYSTEM_TIME range selector is not allowed" eng "SYSTEM_TIME range selector is not allowed"
ER_UNUSED_26 ER_CONFLICTING_FOR_SYSTEM_TIME
eng "You should never see it" eng "Conflicting FOR SYSTEM_TIME clauses in WITH RECURSIVE"
ER_VERS_TABLE_MUST_HAVE_COLUMNS ER_VERS_TABLE_MUST_HAVE_COLUMNS
eng "Table %`s must have at least one versioned column" eng "Table %`s must have at least one versioned column"

View File

@ -978,7 +978,7 @@ bool With_element::prepare_unreferenced(THD *thd)
thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
if (!spec->prepared && if (!spec->prepared &&
(spec->prepare(thd, 0, 0) || (spec->prepare(spec->derived, 0, 0) ||
rename_columns_of_derived_unit(thd, spec) || rename_columns_of_derived_unit(thd, spec) ||
check_duplicate_names(thd, first_sl->item_list, 1))) check_duplicate_names(thd, first_sl->item_list, 1)))
rc= true; rc= true;

View File

@ -748,8 +748,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
} }
} }
unit->derived= derived;
/* /*
Above cascade call of prepare is important for PS protocol, but after it Above cascade call of prepare is important for PS protocol, but after it
is called we can check if we really need prepare for this derived is called we can check if we really need prepare for this derived
@ -767,7 +765,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
// st_select_lex_unit::prepare correctly work for single select // st_select_lex_unit::prepare correctly work for single select
if ((res= unit->prepare(thd, derived->derived_result, 0))) if ((res= unit->prepare(derived, derived->derived_result, 0)))
goto exit; goto exit;
if (derived->with && if (derived->with &&
(res= derived->with->rename_columns_of_derived_unit(thd, unit))) (res= derived->with->rename_columns_of_derived_unit(thd, unit)))

View File

@ -864,7 +864,8 @@ public:
bool is_excluded() { return prev == NULL; } bool is_excluded() { return prev == NULL; }
/* UNION methods */ /* UNION methods */
bool prepare(THD *thd, select_result *result, ulong additional_options); bool prepare(TABLE_LIST *derived_arg, select_result *sel_result,
ulong additional_options);
bool optimize(); bool optimize();
bool exec(); bool exec();
bool exec_recursive(); bool exec_recursive();

View File

@ -1530,7 +1530,7 @@ static int mysql_test_select(Prepared_statement *stmt,
It is not SELECT COMMAND for sure, so setup_tables will be called as It is not SELECT COMMAND for sure, so setup_tables will be called as
usual, and we pass 0 as setup_tables_done_option usual, and we pass 0 as setup_tables_done_option
*/ */
if (unit->prepare(thd, 0, 0)) if (unit->prepare(unit->derived, 0, 0))
goto error; goto error;
if (!lex->describe && !thd->lex->analyze_stmt && !stmt->is_sql_prepare()) if (!lex->describe && !thd->lex->analyze_stmt && !stmt->is_sql_prepare())
{ {
@ -1701,7 +1701,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
thd->lex->used_tables= 0; // Updated by setup_fields thd->lex->used_tables= 0; // Updated by setup_fields
/* Calls JOIN::prepare */ /* Calls JOIN::prepare */
DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option)); DBUG_RETURN(lex->unit.prepare(lex->unit.derived, 0, setup_tables_done_option));
} }
/** /**

View File

@ -25647,7 +25647,8 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
unit->fake_select_lex->type= unit_operation_text[unit->common_op()]; unit->fake_select_lex->type= unit_operation_text[unit->common_op()];
unit->fake_select_lex->options|= SELECT_DESCRIBE; unit->fake_select_lex->options|= SELECT_DESCRIBE;
} }
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE))) if (!(res= unit->prepare(unit->derived, result,
SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec(); res= unit->exec();
} }
else else

View File

@ -36,7 +36,7 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
{ {
DBUG_ENTER("mysql_union"); DBUG_ENTER("mysql_union");
bool res; bool res;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | if (!(res= unit->prepare(unit->derived, result, SELECT_NO_UNLOCK |
setup_tables_done_option))) setup_tables_done_option)))
res= unit->exec(); res= unit->exec();
res|= unit->cleanup(); res|= unit->cleanup();
@ -807,10 +807,11 @@ bool st_select_lex_unit::join_union_item_types(THD *thd_arg,
} }
bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
select_result *sel_result,
ulong additional_options) ulong additional_options)
{ {
SELECT_LEX *lex_select_save= thd_arg->lex->current_select; SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *sl, *first_sl= first_select(); SELECT_LEX *sl, *first_sl= first_select();
bool is_recursive= with_element && with_element->is_recursive; bool is_recursive= with_element && with_element->is_recursive;
bool is_rec_result_table_created= false; bool is_rec_result_table_created= false;
@ -821,7 +822,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
bool instantiate_tmp_table= false; bool instantiate_tmp_table= false;
bool single_tvc= !first_sl->next_select() && first_sl->tvc; bool single_tvc= !first_sl->next_select() && first_sl->tvc;
DBUG_ENTER("st_select_lex_unit::prepare"); DBUG_ENTER("st_select_lex_unit::prepare");
DBUG_ASSERT(thd == thd_arg);
DBUG_ASSERT(thd == current_thd); DBUG_ASSERT(thd == current_thd);
describe= additional_options & SELECT_DESCRIBE; describe= additional_options & SELECT_DESCRIBE;
@ -873,7 +873,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
prepared= 1; prepared= 1;
saved_error= FALSE; saved_error= FALSE;
thd_arg->lex->current_select= sl= first_sl; thd->lex->current_select= sl= first_sl;
found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS;
is_union_select= is_unit_op() || fake_select_lex || single_tvc; is_union_select= is_unit_op() || fake_select_lex || single_tvc;
@ -902,7 +902,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
while (last->next_select()) while (last->next_select())
last= last->next_select(); last= last->next_select();
if (!(tmp_result= union_result= if (!(tmp_result= union_result=
new (thd_arg->mem_root) select_union_direct(thd_arg, sel_result, new (thd->mem_root) select_union_direct(thd, sel_result,
last))) last)))
goto err; /* purecov: inspected */ goto err; /* purecov: inspected */
fake_select_lex= NULL; fake_select_lex= NULL;
@ -911,11 +911,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
else else
{ {
if (!is_recursive) if (!is_recursive)
union_result= new (thd_arg->mem_root) select_unit(thd_arg); union_result= new (thd->mem_root) select_unit(thd);
else else
{ {
with_element->rec_result= with_element->rec_result=
new (thd_arg->mem_root) select_union_recursive(thd_arg); new (thd->mem_root) select_union_recursive(thd);
union_result= with_element->rec_result; union_result= with_element->rec_result;
fake_select_lex= NULL; fake_select_lex= NULL;
} }
@ -933,10 +933,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
{ {
if (sl->tvc) if (sl->tvc)
{ {
if (sl->tvc->prepare(thd_arg, sl, tmp_result, this)) if (sl->tvc->prepare(thd, sl, tmp_result, this))
goto err; goto err;
} }
else if (prepare_join(thd_arg, first_sl, tmp_result, additional_options, else if (prepare_join(thd, first_sl, tmp_result, additional_options,
is_union_select)) is_union_select))
goto err; goto err;
types= first_sl->item_list; types= first_sl->item_list;
@ -947,10 +947,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
{ {
if (sl->tvc) if (sl->tvc)
{ {
if (sl->tvc->prepare(thd_arg, sl, tmp_result, this)) if (sl->tvc->prepare(thd, sl, tmp_result, this))
goto err; goto err;
} }
else if (prepare_join(thd_arg, sl, tmp_result, additional_options, else if (prepare_join(thd, sl, tmp_result, additional_options,
is_union_select)) is_union_select))
goto err; goto err;
@ -970,7 +970,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
{ {
if (with_element) if (with_element)
{ {
if (derived->with->rename_columns_of_derived_unit(thd, this)) if (derived_arg->with->rename_columns_of_derived_unit(thd, this))
goto err; goto err;
if (check_duplicate_names(thd, sl->item_list, 0)) if (check_duplicate_names(thd, sl->item_list, 0))
goto err; goto err;
@ -981,7 +981,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (first_sl->item_list.elements != sl->item_list.elements) if (first_sl->item_list.elements != sl->item_list.elements)
{ {
my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
ER_THD(thd_arg, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
MYF(0)); MYF(0));
goto err; goto err;
} }
@ -995,20 +995,20 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
sl->next_select() == with_element->first_recursive)) sl->next_select() == with_element->first_recursive))
{ {
ulonglong create_options; ulonglong create_options;
create_options= (first_sl->options | thd_arg->variables.option_bits | create_options= (first_sl->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS); TMP_TABLE_ALL_COLUMNS);
// Join data types for all non-recursive parts of a recursive UNION // Join data types for all non-recursive parts of a recursive UNION
if (join_union_item_types(thd, types, union_part_count + 1)) if (join_union_item_types(thd, types, union_part_count + 1))
goto err; goto err;
if (union_result->create_result_table(thd, &types, if (union_result->create_result_table(thd, &types,
MY_TEST(union_distinct), MY_TEST(union_distinct),
create_options, &derived->alias, create_options,
false, &derived_arg->alias, false,
instantiate_tmp_table, false, instantiate_tmp_table, false,
0)) 0))
goto err; goto err;
if (!derived->table) if (!derived_arg->table)
derived->table= derived->derived_result->table= derived_arg->table= derived_arg->derived_result->table=
with_element->rec_result->rec_tables.head(); with_element->rec_result->rec_tables.head();
with_element->mark_as_with_prepared_anchor(); with_element->mark_as_with_prepared_anchor();
is_rec_result_table_created= true; is_rec_result_table_created= true;
@ -1079,7 +1079,7 @@ cont:
} }
create_options= (first_sl->options | thd_arg->variables.option_bits | create_options= (first_sl->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS); TMP_TABLE_ALL_COLUMNS);
/* /*
Force the temporary table to be a MyISAM table if we're going to use Force the temporary table to be a MyISAM table if we're going to use
@ -1107,7 +1107,7 @@ cont:
Query_arena *arena, backup_arena; Query_arena *arena, backup_arena;
arena= thd->activate_stmt_arena_if_needed(&backup_arena); arena= thd->activate_stmt_arena_if_needed(&backup_arena);
intersect_mark= new (thd_arg->mem_root) Item_int(thd, 0); intersect_mark= new (thd->mem_root) Item_int(thd, 0);
if (arena) if (arena)
thd->restore_active_arena(arena, &backup_arena); thd->restore_active_arena(arena, &backup_arena);
@ -1151,7 +1151,7 @@ cont:
result_table_list.maybe_null_exec= save_maybe_null; result_table_list.maybe_null_exec= save_maybe_null;
} }
thd_arg->lex->current_select= lex_select_save; thd->lex->current_select= lex_select_save;
if (!item_list.elements) if (!item_list.elements)
{ {
Query_arena *arena, backup_arena; Query_arena *arena, backup_arena;
@ -1191,7 +1191,7 @@ cont:
*/ */
fake_select_lex->item_list= item_list; fake_select_lex->item_list= item_list;
thd_arg->lex->current_select= fake_select_lex; thd->lex->current_select= fake_select_lex;
/* /*
We need to add up n_sum_items in order to make the correct We need to add up n_sum_items in order to make the correct
@ -1219,12 +1219,12 @@ cont:
} }
} }
thd_arg->lex->current_select= lex_select_save; thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error || thd_arg->is_fatal_error); DBUG_RETURN(saved_error || thd->is_fatal_error);
err: err:
thd_arg->lex->current_select= lex_select_save; thd->lex->current_select= lex_select_save;
(void) cleanup(); (void) cleanup();
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }

View File

@ -530,7 +530,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
/* prepare select to resolve all fields */ /* prepare select to resolve all fields */
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
if (unit->prepare(thd, 0, 0)) if (unit->prepare(unit->derived, 0, 0))
{ {
/* /*
some errors from prepare are reported to user, if is not then some errors from prepare are reported to user, if is not then

View File

@ -8117,7 +8117,21 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
(first_table && first_table->is_multitable())) (first_table && first_table->is_multitable()))
set_multitable(); set_multitable();
unit->derived= this; if (!unit->derived)
unit->derived= this;
else if (!is_with_table_recursive_reference() && unit->derived != this)
{
if (unit->derived->is_with_table_recursive_reference())
unit->derived= this;
else if (vers_conditions.eq(unit->derived->vers_conditions))
vers_conditions.empty();
else
{
my_error(ER_CONFLICTING_FOR_SYSTEM_TIME, MYF(0));
return TRUE;
}
}
if (init_view && !view) if (init_view && !view)
{ {
/* This is all what we can do for a derived table for now. */ /* This is all what we can do for a derived table for now. */
@ -8867,6 +8881,26 @@ void vers_select_conds_t::resolve_units(bool timestamps_only)
end.resolve_unit(timestamps_only); end.resolve_unit(timestamps_only);
} }
bool vers_select_conds_t::eq(const vers_select_conds_t &conds)
{
if (type != conds.type)
return false;
switch (type) {
case SYSTEM_TIME_UNSPECIFIED:
case SYSTEM_TIME_ALL:
return true;
case SYSTEM_TIME_BEFORE:
DBUG_ASSERT(0);
case SYSTEM_TIME_AS_OF:
return start.eq(conds.start);
case SYSTEM_TIME_FROM_TO:
case SYSTEM_TIME_BETWEEN:
return start.eq(conds.start) && end.eq(conds.end);
}
DBUG_ASSERT(0);
return false;
}
void Vers_history_point::resolve_unit(bool timestamps_only) void Vers_history_point::resolve_unit(bool timestamps_only)
{ {
if (item && unit == VERS_UNDEFINED) if (item && unit == VERS_UNDEFINED)
@ -8888,6 +8922,12 @@ void Vers_history_point::fix_item()
item->decimals= 6; item->decimals= 6;
} }
bool Vers_history_point::eq(const vers_history_point_t &point)
{
return unit == point.unit && item->eq(point.item, false);
}
void Vers_history_point::print(String *str, enum_query_type query_type, void Vers_history_point::print(String *str, enum_query_type query_type,
const char *prefix, size_t plen) const char *prefix, size_t plen)
{ {

View File

@ -1829,6 +1829,7 @@ public:
void empty() { unit= VERS_UNDEFINED; item= NULL; } void empty() { unit= VERS_UNDEFINED; item= NULL; }
void print(String *str, enum_query_type, const char *prefix, size_t plen); void print(String *str, enum_query_type, const char *prefix, size_t plen);
void resolve_unit(bool timestamps_only); void resolve_unit(bool timestamps_only);
bool eq(const vers_history_point_t &point);
}; };
struct vers_select_conds_t struct vers_select_conds_t
@ -1878,6 +1879,7 @@ struct vers_select_conds_t
{ {
return !from_query && type != SYSTEM_TIME_UNSPECIFIED; return !from_query && type != SYSTEM_TIME_UNSPECIFIED;
} }
bool eq(const vers_select_conds_t &conds);
}; };
/* /*