MDEV-334: Backport of UNION ALL optimization from mysql-5.7.

Although the original code of mysql-5.7 was adjusted
to the current MariaDB code the main ideas of the optimization
were preserved.
This commit is contained in:
Igor Babaev 2014-10-14 09:36:50 -07:00
parent fec5ab5a56
commit 3c4bb0e872
31 changed files with 853 additions and 327 deletions

View File

@ -102,7 +102,6 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 8 1 PRIMARY <derived2> ALL NULL NULL NULL NULL 8
2 DERIVED t1 ALL NULL NULL NULL NULL 4 2 DERIVED t1 ALL NULL NULL NULL NULL 4
3 UNION t1 ALL NULL NULL NULL NULL 4 3 UNION t1 ALL NULL NULL NULL NULL 4
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
CREATE TABLE t2 (a int not null); CREATE TABLE t2 (a int not null);
insert into t2 values(1); insert into t2 values(1);
select * from (select * from t1 where t1.a=(select a from t2 where t2.a=t1.a)) a; select * from (select * from t1 where t1.a=(select a from t2 where t2.a=t1.a)) a;

View File

@ -126,7 +126,7 @@ group by
a.text, b.id, b.betreff a.text, b.id, b.betreff
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Incorrect usage/placement of 'MATCH()' ERROR 42000: Table 'b' from one of the SELECTs cannot be used in field list
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -142,7 +142,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Incorrect usage/placement of 'MATCH()' ERROR 42000: Table 'b' from one of the SELECTs cannot be used in field list
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -158,7 +158,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(betreff) against ('+abc' in boolean mode) desc; match(betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Incorrect usage/placement of 'MATCH()' text id betreff
(select b.id, b.betreff from t3 b) union (select b.id, b.betreff from t3 b) union
(select b.id, b.betreff from t3 b) (select b.id, b.betreff from t3 b)
order by match(betreff) against ('+abc' in boolean mode) desc; order by match(betreff) against ('+abc' in boolean mode) desc;

View File

@ -3138,7 +3138,6 @@ explain partitions select 1 from t1 union all select 2;
id select_type table partitions type possible_keys key key_len ref rows Extra id select_type table partitions type possible_keys key key_len ref rows Extra
1 PRIMARY t1 NULL system NULL NULL NULL NULL 0 const row not found 1 PRIMARY t1 NULL system NULL NULL NULL NULL 0 const row not found
2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
NULL UNION RESULT <union1,2> NULL ALL NULL NULL NULL NULL NULL
drop table t1; drop table t1;
create table t1 (a bigint unsigned not null) partition by range(a) ( create table t1 (a bigint unsigned not null) partition by range(a) (
partition p0 values less than (10), partition p0 values less than (10),

View File

@ -1052,6 +1052,8 @@ create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5); select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5);
ERROR HY000: Incorrect usage of UNION and LIMIT
select 10.5 IN (SELECT * from t1 UNION SELECT 1.5 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
drop table t1; drop table t1;
create table t1 (a int, b int, c varchar(10)); create table t1 (a int, b int, c varchar(10));

View File

@ -1056,6 +1056,8 @@ create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5); select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5);
ERROR HY000: Incorrect usage of UNION and LIMIT
select 10.5 IN (SELECT * from t1 UNION SELECT 1.5 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
drop table t1; drop table t1;
create table t1 (a int, b int, c varchar(10)); create table t1 (a int, b int, c varchar(10));

View File

@ -1059,6 +1059,8 @@ create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5); select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5);
ERROR HY000: Incorrect usage of UNION and LIMIT
select 10.5 IN (SELECT * from t1 UNION SELECT 1.5 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
drop table t1; drop table t1;
create table t1 (a int, b int, c varchar(10)); create table t1 (a int, b int, c varchar(10));

View File

@ -1055,6 +1055,8 @@ create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5); select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5);
ERROR HY000: Incorrect usage of UNION and LIMIT
select 10.5 IN (SELECT * from t1 UNION SELECT 1.5 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
drop table t1; drop table t1;
create table t1 (a int, b int, c varchar(10)); create table t1 (a int, b int, c varchar(10));

View File

@ -1058,6 +1058,8 @@ create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5); select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5);
ERROR HY000: Incorrect usage of UNION and LIMIT
select 10.5 IN (SELECT * from t1 UNION SELECT 1.5 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
drop table t1; drop table t1;
create table t1 (a int, b int, c varchar(10)); create table t1 (a int, b int, c varchar(10));

View File

@ -1055,6 +1055,8 @@ create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5); select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5);
ERROR HY000: Incorrect usage of UNION and LIMIT
select 10.5 IN (SELECT * from t1 UNION SELECT 1.5 LIMIT 1);
ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' ERROR 42000: This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
drop table t1; drop table t1;
create table t1 (a int, b int, c varchar(10)); create table t1 (a int, b int, c varchar(10));

View File

@ -107,7 +107,6 @@ explain select a,b from t1 union all select a,b from t2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 4 1 PRIMARY t1 ALL NULL NULL NULL NULL 4
2 UNION t2 ALL NULL NULL NULL NULL 4 2 UNION t2 ALL NULL NULL NULL NULL 4
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
explain select xx from t1 union select 1; explain select xx from t1 union select 1;
ERROR 42S22: Unknown column 'xx' in 'field list' ERROR 42S22: Unknown column 'xx' in 'field list'
explain select a,b from t1 union select 1; explain select a,b from t1 union select 1;
@ -341,14 +340,13 @@ select found_rows();
found_rows() found_rows()
4 4
(SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1) UNION SELECT * FROM t2 LIMIT 1; (SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1) UNION SELECT * FROM t2 LIMIT 1;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION all SELECT * FROM t2 LIMIT 2;
a a
1 1
3
select found_rows(); select found_rows();
found_rows() found_rows()
6 4
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION all SELECT * FROM t2 LIMIT 2;
ERROR HY000: Incorrect usage of UNION and LIMIT
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION all SELECT * FROM t2 LIMIT 2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION all SELECT * FROM t2 LIMIT 2;
a a
1 1
@ -362,7 +360,7 @@ a
2 2
select found_rows(); select found_rows();
found_rows() found_rows()
6 5
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 100; SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 100;
a a
1 1
@ -374,31 +372,11 @@ select found_rows();
found_rows() found_rows()
5 5
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 100 UNION SELECT * FROM t2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 100 UNION SELECT * FROM t2;
a ERROR HY000: Incorrect usage of UNION and LIMIT
1
2
3
4
5
select found_rows();
found_rows()
5
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2;
a ERROR HY000: Incorrect usage of UNION and LIMIT
1
3
4
5
select found_rows();
found_rows()
6
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2 LIMIT 2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2 LIMIT 2;
a ERROR HY000: Incorrect usage of UNION and LIMIT
1
3
select found_rows();
found_rows()
6
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 2,2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 2,2;
a a
3 3
@ -407,13 +385,7 @@ select found_rows();
found_rows() found_rows()
5 5
SELECT SQL_CALC_FOUND_ROWS * FROM t1 limit 2,2 UNION SELECT * FROM t2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 limit 2,2 UNION SELECT * FROM t2;
a ERROR HY000: Incorrect usage of UNION and LIMIT
3
4
5
select found_rows();
found_rows()
5
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a desc LIMIT 1; SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a desc LIMIT 1;
a a
5 5
@ -430,7 +402,7 @@ drop temporary table t1;
create table t1 select a from t1 union select a from t2; create table t1 select a from t1 union select a from t2;
ERROR 42S01: Table 't1' already exists ERROR 42S01: Table 't1' already exists
select a from t1 union select a from t2 order by t2.a; select a from t1 union select a from t2 order by t2.a;
ERROR 42S22: Unknown column 't2.a' in 'order clause' ERROR 42000: Table 't2' from one of the SELECTs cannot be used in field list
drop table t1,t2; drop table t1,t2;
select length(version()) > 1 as `*` UNION select 2; select length(version()) > 1 as `*` UNION select 2;
* *
@ -1202,32 +1174,32 @@ foo
bar bar
drop table t1; drop table t1;
CREATE TABLE t1 ( CREATE TABLE t1 (
a ENUM('ä','ö','ü') character set utf8 not null default 'ü', a ENUM('ä','ö','ü') character set utf8 not null default 'ü',
b ENUM("one", "two") character set utf8, b ENUM("one", "two") character set utf8,
c ENUM("one", "two") c ENUM("one", "two")
); );
show create table t1; show create table t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`a` enum('ä','ö','ü') CHARACTER SET utf8 NOT NULL DEFAULT 'ü', `a` enum('ä','ö','ü') CHARACTER SET utf8 NOT NULL DEFAULT 'ü',
`b` enum('one','two') CHARACTER SET utf8 DEFAULT NULL, `b` enum('one','two') CHARACTER SET utf8 DEFAULT NULL,
`c` enum('one','two') DEFAULT NULL `c` enum('one','two') DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t1 values ('ä', 'one', 'one'), ('ö', 'two', 'one'), ('ü', NULL, NULL); insert into t1 values ('ä', 'one', 'one'), ('ö', 'two', 'one'), ('ü', NULL, NULL);
create table t2 select NULL union select a from t1; create table t2 select NULL union select a from t1;
show columns from t2; show columns from t2;
Field Type Null Key Default Extra Field Type Null Key Default Extra
NULL enum('ä','ö','ü') YES NULL NULL enum('ä','ö','ü') YES NULL
drop table t2; drop table t2;
create table t2 select a from t1 union select NULL; create table t2 select a from t1 union select NULL;
show columns from t2; show columns from t2;
Field Type Null Key Default Extra Field Type Null Key Default Extra
a enum('ä','ö','ü') YES NULL a enum('ä','ö','ü') YES NULL
drop table t2; drop table t2;
create table t2 select a from t1 union select a from t1; create table t2 select a from t1 union select a from t1;
show columns from t2; show columns from t2;
Field Type Null Key Default Extra Field Type Null Key Default Extra
a varchar(1) NO a varchar(2) NO
drop table t2; drop table t2;
create table t2 select a from t1 union select c from t1; create table t2 select a from t1 union select c from t1;
drop table t2; drop table t2;
@ -1616,11 +1588,18 @@ a
EXPLAIN EXTENDED EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
ERROR 42000: Incorrect usage/placement of 'MATCH()' id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
2 UNION t1 ALL NULL NULL NULL NULL 2 100.00
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by (match `a` against ('+abc' in boolean mode))
# Should not crash # Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
ERROR 42000: Incorrect usage/placement of 'MATCH()' a
1
2
# Should not crash # Should not crash
(SELECT * FROM t1) UNION (SELECT * FROM t1) (SELECT * FROM t1) UNION (SELECT * FROM t1)
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
@ -1638,7 +1617,7 @@ NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
3 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00 Using where 3 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
Warnings: Warnings:
Note 1276 Field or reference 'a' of SELECT #3 was resolved in SELECT #-1 Note 1276 Field or reference 'a' of SELECT #3 was resolved in SELECT #-1
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by (select `a` from `test`.`t2` where (`test`.`t2`.`b` = 12)) Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by <expr_cache><>((select `a` from `test`.`t2` where (`test`.`t2`.`b` = 12)))
# Should not crash # Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY (SELECT a FROM t2 WHERE b = 12); ORDER BY (SELECT a FROM t2 WHERE b = 12);
@ -1901,3 +1880,94 @@ id select_type table type possible_keys key key_len ref rows Extra
3 UNION t1 ALL NULL NULL NULL NULL 4 3 UNION t1 ALL NULL NULL NULL NULL 4
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
drop table t1; drop table t1;
#
# WL#1763 Avoid creating temporary table in UNION ALL
#
EXPLAIN SELECT 1 UNION ALL SELECT 1 LIMIT 1 OFFSET 1;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used
# Bug #17579498 CHANGES IN DATATYPE OF THE RESULT QUERY IN UNION.
CREATE TABLE t1 (a TIME);
CREATE TABLE t2 (b DATETIME);
CREATE TABLE t3
SELECT a FROM t1 UNION ALL SELECT b FROM t2;
SELECT column_name, column_type
FROM information_schema.columns
WHERE TABLE_NAME='t3';
column_name column_type
a datetime
DROP TABLE t1, t2, t3;
# Bug #17602922 RESULT DIFFERENCES IN UNION QUERIES WITH IN
# (SUBQUERY-UNION ALL)
CREATE TABLE t1 (a VARCHAR(1));
INSERT INTO t1 VALUES (NULL);
INSERT INTO t1 VALUES (NULL);
INSERT INTO t1 VALUES ('j');
INSERT INTO t1 VALUES ('k');
INSERT INTO t1 VALUES ('r');
INSERT INTO t1 VALUES ('r');
INSERT INTO t1 VALUES ('h');
SELECT a FROM t1 WHERE a IN (SELECT 'r' FROM t1 UNION ALL SELECT 'j');
a
j
r
r
CREATE TABLE t2
SELECT a FROM t1 WHERE a IN (SELECT 'r' FROM t1 UNION ALL SELECT 'j');
SELECT * FROM t2;
a
j
r
r
DROP TABLE t1, t2;
CREATE TABLE t1 (a INT PRIMARY KEY);
CREATE TABLE t2 (a INT PRIMARY KEY);
INSERT INTO t2 VALUES (1);
SELECT a, SUM(a) FROM t2 UNION ALL SELECT a, MIN(a) FROM t1 ;
a SUM(a)
1 1
NULL NULL
SELECT FOUND_ROWS();
FOUND_ROWS()
2
DROP TABLE t1, t2;
# Bug #17669551 CRASH/ASSERT AT SELECT_CREATE::PREPARE2 AT
# SQL_INSERT.CC
CREATE TABLE t1 (a INT);
CREATE TABLE t2 SELECT a, a FROM t1 UNION ALL SELECT a, a FROM t1;
ERROR 42S21: Duplicate column name 'a'
DROP TABLE t1;
# Bug #17694956 RESULT DIFFERENCES IN UNION ALL QUERIES WITH LIMIT
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
(SELECT a FROM t1 ORDER BY a LIMIT 0) UNION ALL SELECT a FROM t1;
a
1
DROP TABLE t1;
# Bug #17708480 FOUND_ROWS() VALUE DO NOT MATCH WITH UNION ALL QUERIES
CREATE TABLE t1 (a INT) ENGINE=MEMORY;
CREATE TABLE t2 (a INT) ENGINE=MEMORY;
INSERT INTO t2 VALUES (1);
SELECT COUNT(*) FROM (
SELECT * FROM t2 UNION ALL SELECT * FROM t1) q;
COUNT(*)
1
SELECT SQL_CALC_FOUND_ROWS * FROM t2 UNION ALL SELECT * FROM t1;
a
1
SELECT FOUND_ROWS();
FOUND_ROWS()
1
SELECT COUNT(*) FROM (
SELECT * FROM t1 UNION ALL SELECT * FROM t2) q;
COUNT(*)
1
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION ALL SELECT * FROM t2;
a
1
SELECT FOUND_ROWS();
FOUND_ROWS()
1
DROP TABLE t1, t2;
# End of WL1763 tests

View File

@ -140,16 +140,6 @@ key_len NULL
ref NULL ref NULL
rows NULL rows NULL
Extra No tables used Extra No tables used
id NULL
select_type UNION RESULT
table <union2,3>
type ALL
possible_keys NULL
key NULL
key_len NULL
ref NULL
rows NULL
Extra
# #
# Demonstrate that the reported SELECT statement # Demonstrate that the reported SELECT statement
# no longer produces warnings. # no longer produces warnings.
@ -344,16 +334,6 @@ key_len NULL
ref NULL ref NULL
rows NULL rows NULL
Extra No tables used Extra No tables used
id NULL
select_type UNION RESULT
table <union2,3>
type ALL
possible_keys NULL
key NULL
key_len NULL
ref NULL
rows NULL
Extra
# Lock the record. # Lock the record.
select 1 from t1 natural join (select 3 as a, 2 as b union all select 1 from t1 natural join (select 3 as a, 2 as b union all
select 3 as a, 1 as b) as t2 for update; select 3 as a, 1 as b) as t2 for update;

View File

@ -158,11 +158,18 @@ a
EXPLAIN EXTENDED EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
ERROR 42000: Incorrect usage/placement of 'MATCH()' id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
2 UNION t1 ALL NULL NULL NULL NULL 2 100.00
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by (match `a` against ('+abc' in boolean mode))
# Should not crash # Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
ERROR 42000: Incorrect usage/placement of 'MATCH()' a
1
2
# Should not crash # Should not crash
EXPLAIN EXTENDED EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
@ -174,7 +181,7 @@ NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
3 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00 Using where 3 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
Warnings: Warnings:
Note 1276 Field or reference 'a' of SELECT #3 was resolved in SELECT #-1 Note 1276 Field or reference 'a' of SELECT #3 was resolved in SELECT #-1
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by (select `a` from `test`.`t2` where (`test`.`t2`.`b` = 12)) Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by <expr_cache><>((select `a` from `test`.`t2` where (`test`.`t2`.`b` = 12)))
# Should not crash # Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY (SELECT a FROM t2 WHERE b = 12); ORDER BY (SELECT a FROM t2 WHERE b = 12);

View File

@ -129,7 +129,7 @@ group by
a.text, b.id, b.betreff a.text, b.id, b.betreff
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Incorrect usage/placement of 'MATCH()' ERROR 42000: Table 'b' from one of the SELECTs cannot be used in field list
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -145,7 +145,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Incorrect usage/placement of 'MATCH()' ERROR 42000: Table 'b' from one of the SELECTs cannot be used in field list
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -161,7 +161,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(betreff) against ('+abc' in boolean mode) desc; match(betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Incorrect usage/placement of 'MATCH()' text id betreff
(select b.id, b.betreff from t3 b) union (select b.id, b.betreff from t3 b) union
(select b.id, b.betreff from t3 b) (select b.id, b.betreff from t3 b)
order by match(betreff) against ('+abc' in boolean mode) desc; order by match(betreff) against ('+abc' in boolean mode) desc;

View File

@ -177,13 +177,11 @@ SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a + 12;
--echo # Should not crash --echo # Should not crash
--error ER_CANT_USE_OPTION_HERE
EXPLAIN EXTENDED EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
--echo # Should not crash --echo # Should not crash
--error ER_CANT_USE_OPTION_HERE
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);

View File

@ -93,7 +93,7 @@ CREATE TABLE t3 (
FULLTEXT KEY betreff (betreff) FULLTEXT KEY betreff (betreff)
) ENGINE = InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=996 ; ) ENGINE = InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=996 ;
--error ER_CANT_USE_OPTION_HERE --error ER_TABLENAME_NOT_ALLOWED_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -113,7 +113,7 @@ group by
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
--error ER_CANT_USE_OPTION_HERE --error ER_TABLENAME_NOT_ALLOWED_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -130,7 +130,6 @@ where
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
--error ER_CANT_USE_OPTION_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join

View File

@ -80,7 +80,7 @@ CREATE TABLE t3 (
FULLTEXT KEY betreff (betreff) FULLTEXT KEY betreff (betreff)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=996 ; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=996 ;
--error ER_CANT_USE_OPTION_HERE --error ER_TABLENAME_NOT_ALLOWED_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -100,7 +100,7 @@ group by
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
--error ER_CANT_USE_OPTION_HERE --error ER_TABLENAME_NOT_ALLOWED_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -117,7 +117,6 @@ where
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
--error ER_CANT_USE_OPTION_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join

View File

@ -569,8 +569,10 @@ drop table t1, t2;
create table t1 (a float); create table t1 (a float);
-- error ER_NOT_SUPPORTED_YET -- error ER_NOT_SUPPORTED_YET
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
-- error ER_NOT_SUPPORTED_YET -- error ER_WRONG_USAGE
select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5); select 10.5 IN (SELECT * from t1 LIMIT 1 UNION SELECT 1.5);
-- error ER_NOT_SUPPORTED_YET
select 10.5 IN (SELECT * from t1 UNION SELECT 1.5 LIMIT 1);
drop table t1; drop table t1;
create table t1 (a int, b int, c varchar(10)); create table t1 (a int, b int, c varchar(10));

View File

@ -216,14 +216,12 @@ select found_rows();
select found_rows(); select found_rows();
(SELECT SQL_CALC_FOUND_ROWS * FROM t1) UNION all (SELECT * FROM t2 LIMIT 1); (SELECT SQL_CALC_FOUND_ROWS * FROM t1) UNION all (SELECT * FROM t2 LIMIT 1);
select found_rows(); select found_rows();
# This used to work in 4.0 but not anymore in 4.1
--error 1064
(SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1) UNION SELECT * FROM t2 LIMIT 1; (SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1) UNION SELECT * FROM t2 LIMIT 1;
#select found_rows(); select found_rows();
# In these case found_rows() should work # In these case found_rows() should work
--error ER_WRONG_USAGE
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION all SELECT * FROM t2 LIMIT 2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION all SELECT * FROM t2 LIMIT 2;
select found_rows();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION all SELECT * FROM t2 LIMIT 2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION all SELECT * FROM t2 LIMIT 2;
select found_rows(); select found_rows();
@ -232,16 +230,16 @@ SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 2;
select found_rows(); select found_rows();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 100; SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 100;
select found_rows(); select found_rows();
--error ER_WRONG_USAGE
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 100 UNION SELECT * FROM t2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 100 UNION SELECT * FROM t2;
select found_rows(); --error ER_WRONG_USAGE
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2;
select found_rows(); --error ER_WRONG_USAGE
SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2 LIMIT 2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1 UNION SELECT * FROM t2 LIMIT 2;
select found_rows();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 2,2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 2,2;
select found_rows(); select found_rows();
--error ER_WRONG_USAGE
SELECT SQL_CALC_FOUND_ROWS * FROM t1 limit 2,2 UNION SELECT * FROM t2; SELECT SQL_CALC_FOUND_ROWS * FROM t1 limit 2,2 UNION SELECT * FROM t2;
select found_rows();
# Test some limits with ORDER BY # Test some limits with ORDER BY
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a desc LIMIT 1; SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a desc LIMIT 1;
@ -255,7 +253,7 @@ create temporary table t1 select a from t1 union select a from t2;
drop temporary table t1; drop temporary table t1;
--error ER_TABLE_EXISTS_ERROR --error ER_TABLE_EXISTS_ERROR
create table t1 select a from t1 union select a from t2; create table t1 select a from t1 union select a from t2;
--error 1054 --error ER_TABLENAME_NOT_ALLOWED_HERE
select a from t1 union select a from t2 order by t2.a; select a from t1 union select a from t2 order by t2.a;
drop table t1,t2; drop table t1,t2;
@ -723,12 +721,12 @@ drop table t1;
# Enum merging test # Enum merging test
# #
CREATE TABLE t1 ( CREATE TABLE t1 (
a ENUM('ä','ö','ü') character set utf8 not null default 'ü', a ENUM('ä','ö','ü') character set utf8 not null default 'ü',
b ENUM("one", "two") character set utf8, b ENUM("one", "two") character set utf8,
c ENUM("one", "two") c ENUM("one", "two")
); );
show create table t1; show create table t1;
insert into t1 values ('ä', 'one', 'one'), ('ö', 'two', 'one'), ('ü', NULL, NULL); insert into t1 values ('ä', 'one', 'one'), ('ö', 'two', 'one'), ('ü', NULL, NULL);
create table t2 select NULL union select a from t1; create table t2 select NULL union select a from t1;
show columns from t2; show columns from t2;
drop table t2; drop table t2;
@ -1086,13 +1084,11 @@ SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a + 12;
--echo # Should not crash --echo # Should not crash
--error ER_CANT_USE_OPTION_HERE
EXPLAIN EXTENDED EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
--echo # Should not crash --echo # Should not crash
--error ER_CANT_USE_OPTION_HERE
SELECT * FROM t1 UNION SELECT * FROM t1 SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE); ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
@ -1300,3 +1296,94 @@ create table t1 (a int);
insert t1 values (1),(2),(3),(1); insert t1 values (1),(2),(3),(1);
explain select 1 from dual where exists (select max(a) from t1 group by a union select a+2 from t1); explain select 1 from dual where exists (select max(a) from t1 group by a union select a+2 from t1);
drop table t1; drop table t1;
--echo #
--echo # WL#1763 Avoid creating temporary table in UNION ALL
--echo #
EXPLAIN SELECT 1 UNION ALL SELECT 1 LIMIT 1 OFFSET 1;
--echo # Bug #17579498 CHANGES IN DATATYPE OF THE RESULT QUERY IN UNION.
CREATE TABLE t1 (a TIME);
CREATE TABLE t2 (b DATETIME);
CREATE TABLE t3
SELECT a FROM t1 UNION ALL SELECT b FROM t2;
SELECT column_name, column_type
FROM information_schema.columns
WHERE TABLE_NAME='t3';
DROP TABLE t1, t2, t3;
--echo # Bug #17602922 RESULT DIFFERENCES IN UNION QUERIES WITH IN
--echo # (SUBQUERY-UNION ALL)
CREATE TABLE t1 (a VARCHAR(1));
INSERT INTO t1 VALUES (NULL);
INSERT INTO t1 VALUES (NULL);
INSERT INTO t1 VALUES ('j');
INSERT INTO t1 VALUES ('k');
INSERT INTO t1 VALUES ('r');
INSERT INTO t1 VALUES ('r');
INSERT INTO t1 VALUES ('h');
SELECT a FROM t1 WHERE a IN (SELECT 'r' FROM t1 UNION ALL SELECT 'j');
CREATE TABLE t2
SELECT a FROM t1 WHERE a IN (SELECT 'r' FROM t1 UNION ALL SELECT 'j');
SELECT * FROM t2;
DROP TABLE t1, t2;
CREATE TABLE t1 (a INT PRIMARY KEY);
CREATE TABLE t2 (a INT PRIMARY KEY);
INSERT INTO t2 VALUES (1);
SELECT a, SUM(a) FROM t2 UNION ALL SELECT a, MIN(a) FROM t1 ;
SELECT FOUND_ROWS();
DROP TABLE t1, t2;
--echo # Bug #17669551 CRASH/ASSERT AT SELECT_CREATE::PREPARE2 AT
--echo # SQL_INSERT.CC
CREATE TABLE t1 (a INT);
--error ER_DUP_FIELDNAME
CREATE TABLE t2 SELECT a, a FROM t1 UNION ALL SELECT a, a FROM t1;
DROP TABLE t1;
--echo # Bug #17694956 RESULT DIFFERENCES IN UNION ALL QUERIES WITH LIMIT
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
(SELECT a FROM t1 ORDER BY a LIMIT 0) UNION ALL SELECT a FROM t1;
DROP TABLE t1;
--echo # Bug #17708480 FOUND_ROWS() VALUE DO NOT MATCH WITH UNION ALL QUERIES
CREATE TABLE t1 (a INT) ENGINE=MEMORY;
CREATE TABLE t2 (a INT) ENGINE=MEMORY;
INSERT INTO t2 VALUES (1);
SELECT COUNT(*) FROM (
SELECT * FROM t2 UNION ALL SELECT * FROM t1) q;
SELECT SQL_CALC_FOUND_ROWS * FROM t2 UNION ALL SELECT * FROM t1;
SELECT FOUND_ROWS();
SELECT COUNT(*) FROM (
SELECT * FROM t1 UNION ALL SELECT * FROM t2) q;
SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION ALL SELECT * FROM t2;
SELECT FOUND_ROWS();
DROP TABLE t1, t2;
--echo # End of WL1763 tests

View File

@ -1430,7 +1430,7 @@ void Item_exists_subselect::fix_length_and_dec()
We need only 1 row to determine existence (i.e. any EXISTS that is not We need only 1 row to determine existence (i.e. any EXISTS that is not
an IN always requires LIMIT 1) an IN always requires LIMIT 1)
*/ */
thd->change_item_tree(&unit->global_parameters->select_limit, thd->change_item_tree(&unit->global_parameters()->select_limit,
new Item_int((int32) 1)); new Item_int((int32) 1));
DBUG_PRINT("info", ("Set limit to 1")); DBUG_PRINT("info", ("Set limit to 1"));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@ -2540,7 +2540,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
select_lex->having->top_level_item(); select_lex->having->top_level_item();
join_arg->having= select_lex->having; join_arg->having= select_lex->having;
} }
join_arg->thd->change_item_tree(&unit->global_parameters->select_limit, join_arg->thd->change_item_tree(&unit->global_parameters()->select_limit,
new Item_int((int32) 1)); new Item_int((int32) 1));
unit->select_limit_cnt= 1; unit->select_limit_cnt= 1;
@ -3593,7 +3593,7 @@ int subselect_single_select_engine::exec()
{ {
SELECT_LEX_UNIT *unit= select_lex->master_unit(); SELECT_LEX_UNIT *unit= select_lex->master_unit();
unit->set_limit(unit->global_parameters); unit->set_limit(unit->global_parameters());
if (join->optimize()) if (join->optimize())
{ {
thd->where= save_where; thd->where= save_where;
@ -4310,7 +4310,7 @@ subselect_single_select_engine::change_result(Item_subselect *si,
that would not require a lot of extra code that would be harder to manage that would not require a lot of extra code that would be harder to manage
than the current code. than the current code.
*/ */
DBUG_RETURN(select_lex->join->change_result(res)); DBUG_RETURN(select_lex->join->change_result(res, NULL));
} }
@ -4820,7 +4820,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/* Let our engine reuse this query plan for materialization. */ /* Let our engine reuse this query plan for materialization. */
materialize_join= materialize_engine->join; materialize_join= materialize_engine->join;
materialize_join->change_result(result); materialize_join->change_result(result, NULL);
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }

View File

@ -5224,7 +5224,7 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
if (!(new_sink= new select_value_catcher(subq_pred))) if (!(new_sink= new select_value_catcher(subq_pred)))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (new_sink->setup(&engine->select_lex->join->fields_list) || if (new_sink->setup(&engine->select_lex->join->fields_list) ||
engine->select_lex->join->change_result(new_sink) || engine->select_lex->join->change_result(new_sink, NULL) ||
engine->exec()) engine->exec())
{ {
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
@ -5576,8 +5576,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
Item_in_subselect::test_limit). However, once we allow this, here Item_in_subselect::test_limit). However, once we allow this, here
we should set the correct limit if given in the query. we should set the correct limit if given in the query.
*/ */
in_subs->unit->global_parameters->select_limit= NULL; in_subs->unit->global_parameters()->select_limit= NULL;
in_subs->unit->set_limit(unit->global_parameters); in_subs->unit->set_limit(unit->global_parameters());
/* /*
Set the limit of this JOIN object as well, because normally its being Set the limit of this JOIN object as well, because normally its being
set in the beginning of JOIN::optimize, which was already done. set in the beginning of JOIN::optimize, which was already done.

View File

@ -3914,6 +3914,23 @@ protected:
public: public:
select_result(); select_result();
virtual ~select_result() {}; virtual ~select_result() {};
/**
Change wrapped select_result.
Replace the wrapped result object with new_result and call
prepare() and prepare2() on new_result.
This base class implementation doesn't wrap other select_results.
@param new_result The new result object to wrap around
@retval false Success
@retval true Error
*/
virtual bool change_result(select_result *new_result)
{
return false;
}
virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u) virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{ {
unit= u; unit= u;
@ -4321,9 +4338,19 @@ public:
select_union() :write_err(0), table(0), records(0) { tmp_table_param.init(); } select_union() :write_err(0), table(0), records(0) { tmp_table_param.init(); }
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
/**
Do prepare() and prepare2() if they have been postponed until
column type information is computed (used by select_union_direct).
@param types Column types
@return false on success, true on failure
*/
virtual bool postponed_prepare(List<Item> &types)
{ return false; }
int send_data(List<Item> &items); int send_data(List<Item> &items);
bool send_eof(); bool send_eof();
bool flush(); virtual bool flush();
void cleanup(); void cleanup();
virtual bool create_result_table(THD *thd, List<Item> *column_types, virtual bool create_result_table(THD *thd, List<Item> *column_types,
bool is_distinct, ulonglong options, bool is_distinct, ulonglong options,
@ -4334,6 +4361,101 @@ public:
TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; } TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; }
}; };
/**
UNION result that is passed directly to the receiving select_result
without filling a temporary table.
Function calls are forwarded to the wrapped select_result, but some
functions are expected to be called only once for each query, so
they are only executed for the first SELECT in the union (execept
for send_eof(), which is executed only for the last SELECT).
This select_result is used when a UNION is not DISTINCT and doesn't
have a global ORDER BY clause. @see st_select_lex_unit::prepare().
*/
class select_union_direct :public select_union
{
private:
/* Result object that receives all rows */
select_result *result;
/* The last SELECT_LEX of the union */
SELECT_LEX *last_select_lex;
/* Wrapped result has received metadata */
bool done_send_result_set_metadata;
/* Wrapped result has initialized tables */
bool done_initialize_tables;
/* Accumulated limit_found_rows */
ulonglong limit_found_rows;
/* Number of rows offset */
ha_rows offset;
/* Number of rows limit + offset, @see select_union_direct::send_data() */
ha_rows limit;
public:
select_union_direct(select_result *result, SELECT_LEX *last_select_lex)
:result(result), last_select_lex(last_select_lex),
done_send_result_set_metadata(false), done_initialize_tables(false),
limit_found_rows(0)
{}
bool change_result(select_result *new_result);
uint field_count(List<Item> &fields) const
{
// Only called for top-level select_results, usually select_send
DBUG_ASSERT(false); /* purecov: inspected */
return 0; /* purecov: inspected */
}
bool postponed_prepare(List<Item> &types);
bool send_result_set_metadata(List<Item> &list, uint flags);
int send_data(List<Item> &items);
bool initialize_tables (JOIN *join= NULL);
bool send_eof();
bool flush() { return false; }
bool check_simple_select() const
{
/* Only called for top-level select_results, usually select_send */
DBUG_ASSERT(false); /* purecov: inspected */
return false; /* purecov: inspected */
}
void abort_result_set()
{
result->abort_result_set(); /* purecov: inspected */
}
void cleanup()
{
/*
Only called for top-level select_results, usually select_send,
and for the results of subquery engines
(select_<something>_subselect).
*/
DBUG_ASSERT(false); /* purecov: inspected */
}
void set_thd(THD *thd_arg)
{
/*
Only called for top-level select_results, usually select_send,
and for the results of subquery engines
(select_<something>_subselect).
*/
DBUG_ASSERT(false); /* purecov: inspected */
}
void reset_offset_limit_cnt()
{
// EXPLAIN should never output to a select_union_direct
DBUG_ASSERT(false); /* purecov: inspected */
}
void begin_dataset()
{
// Only called for sp_cursor::Select_fetch_into_spvars
DBUG_ASSERT(false); /* purecov: inspected */
}
};
/* Base subselect interface class */ /* Base subselect interface class */
class select_subselect :public select_result_interceptor class select_subselect :public select_result_interceptor
{ {

View File

@ -788,7 +788,7 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
if (!derived->is_merged_derived()) if (!derived->is_merged_derived())
{ {
JOIN *join= first_select->join; JOIN *join= first_select->join;
unit->set_limit(unit->global_parameters); unit->set_limit(unit->global_parameters());
unit->optimized= TRUE; unit->optimized= TRUE;
if ((res= join->optimize())) if ((res= join->optimize()))
goto err; goto err;
@ -905,7 +905,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
} }
else else
{ {
unit->set_limit(unit->global_parameters); unit->set_limit(unit->global_parameters());
if (unit->select_limit_cnt == HA_POS_ERROR) if (unit->select_limit_cnt == HA_POS_ERROR)
first_select->options&= ~OPTION_FOUND_ROWS; first_select->options&= ~OPTION_FOUND_ROWS;

View File

@ -229,6 +229,9 @@ int Explain_union::print_explain(Explain_query *query,
sel->print_explain(query, output, explain_flags, is_analyze); sel->print_explain(query, output, explain_flags, is_analyze);
} }
if (!using_tmp)
return 0;
/* Print a line with "UNION RESULT" */ /* Print a line with "UNION RESULT" */
List<Item> item_list; List<Item> item_list;
Item *item_null= new Item_null(); Item *item_null= new Item_null();

View File

@ -225,6 +225,7 @@ public:
const char *fake_select_type; const char *fake_select_type;
bool using_filesort; bool using_filesort;
bool using_tmp;
Table_access_tracker *get_fake_select_lex_tracker() Table_access_tracker *get_fake_select_lex_tracker()
{ {

View File

@ -470,7 +470,7 @@ void lex_start(THD *thd)
lex->unit.next= lex->unit.master= lex->unit.next= lex->unit.master=
lex->unit.link_next= lex->unit.return_to= 0; lex->unit.link_next= lex->unit.return_to= 0;
lex->unit.prev= lex->unit.link_prev= 0; lex->unit.prev= lex->unit.link_prev= 0;
lex->unit.slave= lex->unit.global_parameters= lex->current_select= lex->unit.slave= lex->current_select=
lex->all_selects_list= &lex->select_lex; lex->all_selects_list= &lex->select_lex;
lex->select_lex.master= &lex->unit; lex->select_lex.master= &lex->unit;
lex->select_lex.prev= &lex->unit.slave; lex->select_lex.prev= &lex->unit.slave;
@ -1819,7 +1819,6 @@ void st_select_lex_unit::init_query()
{ {
st_select_lex_node::init_query(); st_select_lex_node::init_query();
linkage= GLOBAL_OPTIONS_TYPE; linkage= GLOBAL_OPTIONS_TYPE;
global_parameters= first_select();
select_limit_cnt= HA_POS_ERROR; select_limit_cnt= HA_POS_ERROR;
offset_limit_cnt= 0; offset_limit_cnt= 0;
union_distinct= 0; union_distinct= 0;
@ -1828,6 +1827,7 @@ void st_select_lex_unit::init_query()
union_result= 0; union_result= 0;
table= 0; table= 0;
fake_select_lex= 0; fake_select_lex= 0;
saved_fake_select_lex= 0;
cleaned= 0; cleaned= 0;
item_list.empty(); item_list.empty();
describe= 0; describe= 0;
@ -1901,7 +1901,6 @@ void st_select_lex::init_select()
in_sum_expr= with_wild= 0; in_sum_expr= with_wild= 0;
options= 0; options= 0;
sql_cache= SQL_CACHE_UNSPECIFIED; sql_cache= SQL_CACHE_UNSPECIFIED;
braces= 0;
interval_list.empty(); interval_list.empty();
ftfunc_list_alloc.empty(); ftfunc_list_alloc.empty();
inner_sum_func_list= 0; inner_sum_func_list= 0;
@ -2214,6 +2213,7 @@ bool st_select_lex::test_limit()
} }
st_select_lex_unit* st_select_lex_unit::master_unit() st_select_lex_unit* st_select_lex_unit::master_unit()
{ {
return this; return this;
@ -2226,6 +2226,75 @@ st_select_lex* st_select_lex_unit::outer_select()
} }
ha_rows st_select_lex::get_offset()
{
ulonglong val= 0;
if (offset_limit)
{
// see comment for st_select_lex::get_limit()
bool fix_fields_successful= true;
if (!offset_limit->fixed)
{
fix_fields_successful= !offset_limit->fix_fields(master_unit()->thd,
NULL);
DBUG_ASSERT(fix_fields_successful);
}
val= fix_fields_successful ? offset_limit->val_uint() : HA_POS_ERROR;
}
return (ha_rows)val;
}
ha_rows st_select_lex::get_limit()
{
ulonglong val= HA_POS_ERROR;
if (select_limit)
{
/*
fix_fields() has not been called for select_limit. That's due to the
historical reasons -- this item could be only of type Item_int, and
Item_int does not require fix_fields(). Thus, fix_fields() was never
called for select_limit.
Some time ago, Item_splocal was also allowed for LIMIT / OFFSET clauses.
However, the fix_fields() behavior was not updated, which led to a crash
in some cases.
There is no single place where to call fix_fields() for LIMIT / OFFSET
items during the fix-fields-phase. Thus, for the sake of readability,
it was decided to do it here, on the evaluation phase (which is a
violation of design, but we chose the lesser of two evils).
We can call fix_fields() here, because select_limit can be of two
types only: Item_int and Item_splocal. Item_int::fix_fields() is trivial,
and Item_splocal::fix_fields() (or rather Item_sp_variable::fix_fields())
has the following properties:
1) it does not affect other items;
2) it does not fail.
Nevertheless DBUG_ASSERT was added to catch future changes in
fix_fields() implementation. Also added runtime check against a result
of fix_fields() in order to handle error condition in non-debug build.
*/
bool fix_fields_successful= true;
if (!select_limit->fixed)
{
fix_fields_successful= !select_limit->fix_fields(master_unit()->thd,
NULL);
DBUG_ASSERT(fix_fields_successful);
}
val= fix_fields_successful ? select_limit->val_uint() : HA_POS_ERROR;
}
return (ha_rows)val;
}
bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc) bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc)
{ {
return add_to_list(thd, order_list, item, asc); return add_to_list(thd, order_list, item, asc);
@ -2237,6 +2306,7 @@ bool st_select_lex::add_gorder_to_list(THD *thd, Item *item, bool asc)
return add_to_list(thd, gorder_list, item, asc); return add_to_list(thd, gorder_list, item, asc);
} }
bool st_select_lex::add_item_to_list(THD *thd, Item *item) bool st_select_lex::add_item_to_list(THD *thd, Item *item)
{ {
DBUG_ENTER("st_select_lex::add_item_to_list"); DBUG_ENTER("st_select_lex::add_item_to_list");
@ -2364,7 +2434,7 @@ void st_select_lex_unit::print(String *str, enum_query_type query_type)
if (sl->braces) if (sl->braces)
str->append(')'); str->append(')');
} }
if (fake_select_lex == global_parameters) if (fake_select_lex)
{ {
if (fake_select_lex->order_list.elements) if (fake_select_lex->order_list.elements)
{ {
@ -2375,6 +2445,8 @@ void st_select_lex_unit::print(String *str, enum_query_type query_type)
} }
fake_select_lex->print_limit(thd, str, query_type); fake_select_lex->print_limit(thd, str, query_type);
} }
else if (saved_fake_select_lex)
saved_fake_select_lex->print_limit(thd, str, query_type);
} }
@ -2425,7 +2497,7 @@ void st_select_lex::print_limit(THD *thd,
SELECT_LEX_UNIT *unit= master_unit(); SELECT_LEX_UNIT *unit= master_unit();
Item_subselect *item= unit->item; Item_subselect *item= unit->item;
if (item && unit->global_parameters == this) if (item && unit->global_parameters() == this)
{ {
Item_subselect::subs_type subs_type= item->substype(); Item_subselect::subs_type subs_type= item->substype();
if (subs_type == Item_subselect::EXISTS_SUBS || if (subs_type == Item_subselect::EXISTS_SUBS ||
@ -2817,97 +2889,39 @@ LEX::copy_db_to(char **p_db, size_t *p_db_length) const
return thd->copy_db_to(p_db, p_db_length); return thd->copy_db_to(p_db, p_db_length);
} }
/* /**
initialize limit counters Initialize offset and limit counters.
SYNOPSIS @param sl SELECT_LEX to get offset and limit from.
st_select_lex_unit::set_limit()
values - SELECT_LEX with initial values for counters
*/ */
void st_select_lex_unit::set_limit(st_select_lex *sl) void st_select_lex_unit::set_limit(st_select_lex *sl)
{ {
ha_rows select_limit_val;
ulonglong val;
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare()); DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
if (sl->select_limit)
{
Item *item = sl->select_limit;
/*
fix_fields() has not been called for sl->select_limit. That's due to the
historical reasons -- this item could be only of type Item_int, and
Item_int does not require fix_fields(). Thus, fix_fields() was never
called for sl->select_limit.
Some time ago, Item_splocal was also allowed for LIMIT / OFFSET clauses. offset_limit_cnt= sl->get_offset();
However, the fix_fields() behavior was not updated, which led to a crash select_limit_cnt= sl->get_limit();
in some cases. if (select_limit_cnt + offset_limit_cnt >= select_limit_cnt)
select_limit_cnt+= offset_limit_cnt;
There is no single place where to call fix_fields() for LIMIT / OFFSET
items during the fix-fields-phase. Thus, for the sake of readability,
it was decided to do it here, on the evaluation phase (which is a
violation of design, but we chose the lesser of two evils).
We can call fix_fields() here, because sl->select_limit can be of two
types only: Item_int and Item_splocal. Item_int::fix_fields() is trivial,
and Item_splocal::fix_fields() (or rather Item_sp_variable::fix_fields())
has the following specific:
1) it does not affect other items;
2) it does not fail.
Nevertheless DBUG_ASSERT was added to catch future changes in
fix_fields() implementation. Also added runtime check against a result
of fix_fields() in order to handle error condition in non-debug build.
*/
bool fix_fields_successful= true;
if (!item->fixed)
{
fix_fields_successful= !item->fix_fields(thd, NULL);
DBUG_ASSERT(fix_fields_successful);
}
val= fix_fields_successful ? item->val_uint() : HA_POS_ERROR;
}
else else
val= HA_POS_ERROR; select_limit_cnt= HA_POS_ERROR;
}
select_limit_val= (ha_rows)val;
#ifndef BIG_TABLES /**
/* Decide if a temporary table is needed for the UNION.
Check for overflow : ha_rows can be smaller then ulonglong if
BIG_TABLES is off. @retval true A temporary table is needed.
@retval false A temporary table is not needed.
*/ */
if (val != (ulonglong)select_limit_val)
select_limit_val= HA_POS_ERROR; bool st_select_lex_unit::union_needs_tmp_table()
#endif
if (sl->offset_limit)
{ {
Item *item = sl->offset_limit; return union_distinct != NULL ||
// see comment for sl->select_limit branch. global_parameters()->order_list.elements != 0 ||
bool fix_fields_successful= true; thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
if (!item->fixed) thd->lex->sql_command == SQLCOM_REPLACE_SELECT;
{
fix_fields_successful= !item->fix_fields(thd, NULL);
DBUG_ASSERT(fix_fields_successful);
} }
val= fix_fields_successful ? item->val_uint() : 0;
}
else
val= 0;
offset_limit_cnt= (ha_rows)val;
#ifndef BIG_TABLES
/* Check for truncation. */
if (val != (ulonglong)offset_limit_cnt)
offset_limit_cnt= HA_POS_ERROR;
#endif
select_limit_cnt= select_limit_val + offset_limit_cnt;
if (select_limit_cnt < select_limit_val)
select_limit_cnt= HA_POS_ERROR; // no limit
}
/** /**
@brief Set the initial purpose of this TABLE_LIST object in the list of used @brief Set the initial purpose of this TABLE_LIST object in the list of used
@ -3496,7 +3510,7 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
ulonglong save_options; ulonglong save_options;
int res; int res;
/* We need only 1 row to determine existence */ /* We need only 1 row to determine existence */
un->set_limit(un->global_parameters); un->set_limit(un->global_parameters());
un->thd->lex->current_select= sl; un->thd->lex->current_select= sl;
save_options= inner_join->select_options; save_options= inner_join->select_options;
if (options & SELECT_DESCRIBE) if (options & SELECT_DESCRIBE)
@ -3896,7 +3910,7 @@ void SELECT_LEX::update_used_tables()
} }
for (ORDER *order= group_list.first; order; order= order->next) for (ORDER *order= group_list.first; order; order= order->next)
(*order->item)->update_used_tables(); (*order->item)->update_used_tables();
if (!master_unit()->is_union() || master_unit()->global_parameters != this) if (!master_unit()->is_union() || master_unit()->global_parameters() != this)
{ {
for (ORDER *order= order_list.first; order; order= order->next) for (ORDER *order= order_list.first; order; order= order->next)
(*order->item)->update_used_tables(); (*order->item)->update_used_tables();
@ -4244,7 +4258,8 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
eu->add_select(sl->select_number); eu->add_select(sl->select_number);
eu->fake_select_type= "UNION RESULT"; eu->fake_select_type= "UNION RESULT";
eu->using_filesort= MY_TEST(global_parameters->order_list.first); eu->using_filesort= MY_TEST(global_parameters()->order_list.first);
eu->using_tmp= union_needs_tmp_table();
// Save the UNION node // Save the UNION node
output->add_node(eu); output->add_node(eu);

View File

@ -577,11 +577,28 @@ public:
any SELECT of this unit execution any SELECT of this unit execution
*/ */
List<Item> types; List<Item> types;
/* /**
Pointer to 'last' select or pointer to unit where stored Pointer to 'last' select, or pointer to select where we stored
global parameters for union global parameters for union.
If this is a union of multiple selects, the parser puts the global
parameters in fake_select_lex. If the union doesn't use a
temporary table, st_select_lex_unit::prepare() nulls out
fake_select_lex, but saves a copy in saved_fake_select_lex in
order to preserve the global parameters.
If it is not a union, first_select() is the last select.
@return select containing the global parameters
*/ */
st_select_lex *global_parameters; inline st_select_lex *global_parameters()
{
if (fake_select_lex != NULL)
return fake_select_lex;
else if (saved_fake_select_lex != NULL)
return saved_fake_select_lex;
return first_select();
};
//node on wich we should return current_select pointer after parsing subquery //node on wich we should return current_select pointer after parsing subquery
st_select_lex *return_to; st_select_lex *return_to;
/* LIMIT clause runtime counters */ /* LIMIT clause runtime counters */
@ -600,6 +617,11 @@ public:
ORDER BY and LIMIT ORDER BY and LIMIT
*/ */
st_select_lex *fake_select_lex; st_select_lex *fake_select_lex;
/**
SELECT_LEX that stores LIMIT and OFFSET for UNION ALL when no
fake_select_lex is used.
*/
st_select_lex *saved_fake_select_lex;
st_select_lex *union_distinct; /* pointer to the last UNION DISTINCT */ st_select_lex *union_distinct; /* pointer to the last UNION DISTINCT */
bool describe; /* union exec() called for EXPLAIN */ bool describe; /* union exec() called for EXPLAIN */
@ -645,6 +667,7 @@ public:
void set_limit(st_select_lex *values); void set_limit(st_select_lex *values);
void set_thd(THD *thd_arg) { thd= thd_arg; } void set_thd(THD *thd_arg) { thd= thd_arg; }
inline bool is_union (); inline bool is_union ();
bool union_needs_tmp_table();
void set_unique_exclude(); void set_unique_exclude();
@ -904,9 +927,26 @@ public:
*/ */
void cut_subtree() { slave= 0; } void cut_subtree() { slave= 0; }
bool test_limit(); bool test_limit();
/**
Get offset for LIMIT.
Evaluate offset item if necessary.
@return Number of rows to skip.
*/
ha_rows get_offset();
/**
Get limit.
Evaluate limit item if necessary.
@return Limit of rows in result.
*/
ha_rows get_limit();
friend void lex_start(THD *thd); friend void lex_start(THD *thd);
st_select_lex() : group_list_ptrs(NULL), n_sum_items(0), n_child_sum_items(0) st_select_lex() : group_list_ptrs(NULL), braces(0),
n_sum_items(0), n_child_sum_items(0)
{} {}
void make_empty_select() void make_empty_select()
{ {

View File

@ -5592,7 +5592,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
bool res; bool res;
/* assign global limit variable if limit is not given */ /* assign global limit variable if limit is not given */
{ {
SELECT_LEX *param= lex->unit.global_parameters; SELECT_LEX *param= lex->unit.global_parameters();
if (!param->explicit_limit) if (!param->explicit_limit)
param->select_limit= param->select_limit=
new Item_int((ulonglong) thd->variables.select_limit); new Item_int((ulonglong) thd->variables.select_limit);
@ -6675,7 +6675,6 @@ mysql_new_select(LEX *lex, bool move_down)
unit->first_select()->context.outer_context; unit->first_select()->context.outer_context;
} }
select_lex->master_unit()->global_parameters= select_lex;
select_lex->include_global((st_select_lex_node**)&lex->all_selects_list); select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
lex->current_select= select_lex; lex->current_select= select_lex;
/* /*
@ -7652,7 +7651,6 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
(SELECT ... LIMIT n) ORDER BY order_list [LIMIT m] (SELECT ... LIMIT n) ORDER BY order_list [LIMIT m]
just before the parser starts processing order_list just before the parser starts processing order_list
*/ */
global_parameters= fake_select_lex;
fake_select_lex->no_table_names_allowed= 1; fake_select_lex->no_table_names_allowed= 1;
thd_arg->lex->current_select= fake_select_lex; thd_arg->lex->current_select= fake_select_lex;
} }

View File

@ -351,7 +351,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
else else
{ {
SELECT_LEX_UNIT *unit= &lex->unit; SELECT_LEX_UNIT *unit= &lex->unit;
unit->set_limit(unit->global_parameters); unit->set_limit(unit->global_parameters());
/* /*
'options' of mysql_select will be set in JOIN, as far as JOIN for 'options' of mysql_select will be set in JOIN, as far as JOIN for
every PS/SP execution new, we will not need reset this flag if every PS/SP execution new, we will not need reset this flag if
@ -792,7 +792,8 @@ JOIN::prepare(Item ***rref_pointer_array,
ref_pointer_array= *rref_pointer_array; ref_pointer_array= *rref_pointer_array;
/* Resolve the ORDER BY that was skipped, then remove it. */ /* Resolve the ORDER BY that was skipped, then remove it. */
if (skip_order_by && select_lex != select_lex->master_unit()->global_parameters) if (skip_order_by && select_lex !=
select_lex->master_unit()->global_parameters())
{ {
if (setup_order(thd, (*rref_pointer_array), tables_list, fields_list, if (setup_order(thd, (*rref_pointer_array), tables_list, fields_list,
all_fields, select_lex->order_list.first)) all_fields, select_lex->order_list.first))
@ -2481,13 +2482,6 @@ void JOIN::exec_inner()
thd->set_examined_row_count(0); thd->set_examined_row_count(0);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/*
Don't reset the found rows count if there're no tables as
FOUND_ROWS() may be called. Never reset the examined row count here.
It must be accumulated from all join iterations of all join parts.
*/
if (table_count)
thd->limit_found_rows= 0;
/* /*
Evaluate expensive constant conditions that were not evaluated during Evaluate expensive constant conditions that were not evaluated during
@ -3091,7 +3085,6 @@ void JOIN::exec_inner()
*curr_fields_list), *curr_fields_list),
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure); error= do_select(curr_join, curr_fields_list, NULL, procedure);
thd->limit_found_rows= curr_join->send_records;
if (curr_join->order && curr_join->filesort_found_rows) if (curr_join->order && curr_join->filesort_found_rows)
{ {
/* Use info provided by filesort. */ /* Use info provided by filesort. */
@ -3257,7 +3250,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
if (select_lex->linkage != GLOBAL_OPTIONS_TYPE) if (select_lex->linkage != GLOBAL_OPTIONS_TYPE)
{ {
//here is EXPLAIN of subselect or derived table //here is EXPLAIN of subselect or derived table
if (join->change_result(result)) if (join->change_result(result, NULL))
{ {
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
@ -12088,6 +12081,14 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
if (having && having->val_int() == 0) if (having && having->val_int() == 0)
send_row=0; send_row=0;
} }
/* Update results for FOUND_ROWS */
if (!join->send_row_on_empty_set())
{
join->thd->set_examined_row_count(0);
join->thd->limit_found_rows= 0;
}
if (!(result->send_result_set_metadata(fields, if (!(result->send_result_set_metadata(fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))) Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{ {
@ -12097,9 +12098,6 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
if (!send_error) if (!send_error)
result->send_eof(); // Should be safe result->send_eof(); // Should be safe
} }
/* Update results for FOUND_ROWS */
join->thd->limit_found_rows= 0;
join->thd->set_examined_row_count(0);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@ -17300,6 +17298,9 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (error == NESTED_LOOP_QUERY_LIMIT) if (error == NESTED_LOOP_QUERY_LIMIT)
error= NESTED_LOOP_OK; /* select_limit used */ error= NESTED_LOOP_OK; /* select_limit used */
} }
join->thd->limit_found_rows= join->send_records;
if (error == NESTED_LOOP_NO_MORE_ROWS || join->thd->killed == ABORT_QUERY) if (error == NESTED_LOOP_NO_MORE_ROWS || join->thd->killed == ABORT_QUERY)
error= NESTED_LOOP_OK; error= NESTED_LOOP_OK;
@ -23257,7 +23258,7 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
/* rows */ /* rows */
item_list.push_back(item_null); item_list.push_back(item_null);
/* extra */ /* extra */
if (select_lex->master_unit()->global_parameters->order_list.first) if (select_lex->master_unit()->global_parameters()->order_list.first)
item_list.push_back(new Item_string("Using filesort", item_list.push_back(new Item_string("Using filesort",
14, cs)); 14, cs));
else else
@ -23934,16 +23935,19 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
if (unit->is_union()) if (unit->is_union())
{ {
unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // jost for initialization if (unit->union_needs_tmp_table())
{
unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // just for initialization
unit->fake_select_lex->type= "UNION RESULT"; unit->fake_select_lex->type= "UNION RESULT";
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(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec(); res= unit->exec();
} }
else else
{ {
thd->lex->current_select= first; thd->lex->current_select= first;
unit->set_limit(unit->global_parameters); unit->set_limit(unit->global_parameters());
res= mysql_select(thd, &first->ref_pointer_array, res= mysql_select(thd, &first->ref_pointer_array,
first->table_list.first, first->table_list.first,
first->with_wild, first->item_list, first->with_wild, first->item_list,
@ -24427,28 +24431,34 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
/** /**
change select_result object of JOIN. Change the select_result object of the JOIN.
@param res new select_result object If old_result is not used, forward the call to the current
select_result in case it is a wrapper around old_result.
@retval Call prepare() and prepare2() on the new select_result if we decide
FALSE OK to use it.
@retval
TRUE error @param new_result New select_result object
@param old_result Old select_result object (NULL to force change)
@retval false Success
@retval true Error
*/ */
bool JOIN::change_result(select_result *res) bool JOIN::change_result(select_result *new_result, select_result *old_result)
{ {
DBUG_ENTER("JOIN::change_result"); DBUG_ENTER("JOIN::change_result");
result= res; if (old_result == NULL || result == old_result)
if (tmp_join)
tmp_join->result= res;
if (!procedure && (result->prepare(fields_list, select_lex->master_unit()) ||
result->prepare2()))
{ {
DBUG_RETURN(TRUE); result= new_result;
if (result->prepare(fields_list, select_lex->master_unit()) ||
result->prepare2())
DBUG_RETURN(true); /* purecov: inspected */
DBUG_RETURN(false);
} }
DBUG_RETURN(FALSE); else
DBUG_RETURN(result->change_result(new_result));
} }

View File

@ -1413,7 +1413,7 @@ public:
having_value != Item::COND_FALSE); having_value != Item::COND_FALSE);
} }
bool empty_result() { return (zero_result_cause && !implicit_grouping); } bool empty_result() { return (zero_result_cause && !implicit_grouping); }
bool change_result(select_result *result); bool change_result(select_result *new_result, select_result *old_result);
bool is_top_level_join() const bool is_top_level_join() const
{ {
return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 || return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||

View File

@ -187,6 +187,103 @@ void select_union::cleanup()
} }
/**
Replace the current result with new_result and prepare it.
@param new_result New result pointer
@retval FALSE Success
@retval TRUE Error
*/
bool select_union_direct::change_result(select_result *new_result)
{
result= new_result;
return (result->prepare(unit->types, unit) || result->prepare2());
}
bool select_union_direct::postponed_prepare(List<Item> &types)
{
if (result != NULL)
return (result->prepare(types, unit) || result->prepare2());
else
return false;
}
bool select_union_direct::send_result_set_metadata(List<Item> &list, uint flags)
{
if (done_send_result_set_metadata)
return false;
done_send_result_set_metadata= true;
/*
Set global offset and limit to be used in send_data(). These can
be variables in prepared statements or stored programs, so they
must be reevaluated for each execution.
*/
offset= unit->global_parameters()->get_offset();
limit= unit->global_parameters()->get_limit();
if (limit + offset >= limit)
limit+= offset;
else
limit= HA_POS_ERROR; /* purecov: inspected */
return result->send_result_set_metadata(unit->types, flags);
}
int select_union_direct::send_data(List<Item> &items)
{
if (!limit)
return false;
limit--;
if (offset)
{
offset--;
return false;
}
fill_record(thd, table, table->field, items, true, false);
if (thd->is_error())
return true; /* purecov: inspected */
return result->send_data(unit->item_list);
}
bool select_union_direct::initialize_tables (JOIN *join)
{
if (done_initialize_tables)
return false;
done_initialize_tables= true;
return result->initialize_tables(join);
}
bool select_union_direct::send_eof()
{
// Reset for each SELECT_LEX, so accumulate here
limit_found_rows+= thd->limit_found_rows;
if (unit->thd->lex->current_select == last_select_lex)
{
thd->limit_found_rows= limit_found_rows;
// Reset and make ready for re-execution
done_send_result_set_metadata= false;
done_initialize_tables= false;
return result->send_eof();
}
else
return false;
}
/* /*
initialization procedures before fake_select_lex preparation() initialization procedures before fake_select_lex preparation()
@ -217,12 +314,12 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg,
*/ */
if (!fake_select_lex->first_execution && first_execution) if (!fake_select_lex->first_execution && first_execution)
{ {
for (ORDER *order= global_parameters->order_list.first; for (ORDER *order= global_parameters()->order_list.first;
order; order;
order= order->next) order= order->next)
order->item= &order->item_ptr; order->item= &order->item_ptr;
} }
for (ORDER *order= global_parameters->order_list.first; for (ORDER *order= global_parameters()->order_list.first;
order; order;
order=order->next) order=order->next)
{ {
@ -241,10 +338,18 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT_LEX *sl, *first_sl= first_select(); SELECT_LEX *sl, *first_sl= first_select();
select_result *tmp_result; select_result *tmp_result;
bool is_union_select; bool is_union_select;
bool instantiate_tmp_table= false;
DBUG_ENTER("st_select_lex_unit::prepare"); DBUG_ENTER("st_select_lex_unit::prepare");
describe= MY_TEST(additional_options & SELECT_DESCRIBE); describe= MY_TEST(additional_options & SELECT_DESCRIBE);
/*
Save fake_select_lex in case we don't need it for anything but
global parameters.
*/
if (saved_fake_select_lex == NULL) // Don't overwrite on PS second prepare
saved_fake_select_lex= fake_select_lex;
/* /*
result object should be reassigned even if preparing already done for result object should be reassigned even if preparing already done for
max/min subquery (ALL/ANY optimization) max/min subquery (ALL/ANY optimization)
@ -283,10 +388,22 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (is_union_select) if (is_union_select)
{ {
if (!(tmp_result= union_result= new select_union)) if (is_union() && !union_needs_tmp_table())
goto err; {
if (describe) SELECT_LEX *last= first_select();
tmp_result= sel_result; while (last->next_select())
last= last->next_select();
if (!(tmp_result= union_result= new select_union_direct(sel_result, last)))
goto err; /* purecov: inspected */
fake_select_lex= NULL;
instantiate_tmp_table= false;
}
else
{
if (!(tmp_result= union_result= new select_union()))
goto err; /* purecov: inspected */
instantiate_tmp_table= true;
}
} }
else else
tmp_result= sel_result; tmp_result= sel_result;
@ -387,6 +504,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
} }
} }
/*
If the query is using select_union_direct, we have postponed
preparation of the underlying select_result until column types
are known.
*/
if (union_result != NULL && union_result->postponed_prepare(types))
DBUG_RETURN(true);
if (is_union_select) if (is_union_select)
{ {
/* /*
@ -423,13 +548,13 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
the meaning of these accumulated flags and what to carry over to the the meaning of these accumulated flags and what to carry over to the
recipient query (SELECT_LEX). recipient query (SELECT_LEX).
*/ */
if (global_parameters->ftfunc_list->elements && if (global_parameters()->ftfunc_list->elements &&
global_parameters->order_list.elements && global_parameters()->order_list.elements &&
global_parameters != fake_select_lex) global_parameters() != fake_select_lex)
{ {
ORDER *ord; ORDER *ord;
Item_func::Functype ft= Item_func::FT_FUNC; Item_func::Functype ft= Item_func::FT_FUNC;
for (ord= global_parameters->order_list.first; ord; ord= ord->next) for (ord= global_parameters()->order_list.first; ord; ord= ord->next)
if ((*ord->item)->walk (&Item::find_function_processor, FALSE, if ((*ord->item)->walk (&Item::find_function_processor, FALSE,
(uchar *) &ft)) (uchar *) &ft))
{ {
@ -447,11 +572,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
from it (this should be removed in 5.2 when fulltext search is moved from it (this should be removed in 5.2 when fulltext search is moved
out of MyISAM). out of MyISAM).
*/ */
if (global_parameters->ftfunc_list->elements) if (global_parameters()->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM; create_options= create_options | TMP_TABLE_FORCE_MYISAM;
if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct), if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct),
create_options, "", FALSE, TRUE)) create_options, "", false,
instantiate_tmp_table))
goto err; goto err;
if (fake_select_lex && !fake_select_lex->first_cond_optimization) if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{ {
@ -485,7 +611,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (saved_error) if (saved_error)
goto err; goto err;
if (thd->stmt_arena->is_stmt_prepare()) if (fake_select_lex != NULL && thd->stmt_arena->is_stmt_prepare())
{ {
/* Validate the global parameters of this union */ /* Validate the global parameters of this union */
@ -511,14 +637,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
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
allocation in setup_ref_array(). allocation in setup_ref_array().
*/ */
fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items; fake_select_lex->n_child_sum_items+= global_parameters()->n_sum_items;
saved_error= fake_select_lex->join-> saved_error= fake_select_lex->join->
prepare(&fake_select_lex->ref_pointer_array, prepare(&fake_select_lex->ref_pointer_array,
fake_select_lex->table_list.first, fake_select_lex->table_list.first,
0, 0, 0, 0,
global_parameters->order_list.elements, // og_num global_parameters()->order_list.elements, // og_num
global_parameters->order_list.first, // order global_parameters()->order_list.first, // order
false, NULL, NULL, NULL, false, NULL, NULL, NULL,
fake_select_lex, this); fake_select_lex, this);
fake_select_lex->table_list.empty(); fake_select_lex->table_list.empty();
@ -570,7 +696,11 @@ bool st_select_lex_unit::optimize()
{ {
item->assigned(0); // We will reinit & rexecute unit item->assigned(0); // We will reinit & rexecute unit
item->reset(); item->reset();
if (table->created)
{
table->file->ha_delete_all_rows(); table->file->ha_delete_all_rows();
table->file->info(HA_STATUS_VARIABLE);
}
} }
/* re-enabling indexes for next subselect iteration */ /* re-enabling indexes for next subselect iteration */
if (union_distinct && table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL)) if (union_distinct && table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL))
@ -587,7 +717,7 @@ bool st_select_lex_unit::optimize()
else else
{ {
set_limit(sl); set_limit(sl);
if (sl == global_parameters || describe) if (sl == global_parameters() || describe)
{ {
offset_limit_cnt= 0; offset_limit_cnt= 0;
/* /*
@ -656,14 +786,17 @@ bool st_select_lex_unit::exec()
{ {
ha_rows records_at_start= 0; ha_rows records_at_start= 0;
thd->lex->current_select= sl; thd->lex->current_select= sl;
if (fake_select_lex)
{
if (sl != &thd->lex->select_lex) if (sl != &thd->lex->select_lex)
fake_select_lex->uncacheable|= sl->uncacheable; fake_select_lex->uncacheable|= sl->uncacheable;
else else
fake_select_lex->uncacheable= 0; fake_select_lex->uncacheable= 0;
}
{ {
set_limit(sl); set_limit(sl);
if (sl == global_parameters || describe) if (sl == global_parameters() || describe)
{ {
offset_limit_cnt= 0; offset_limit_cnt= 0;
/* /*
@ -690,6 +823,8 @@ bool st_select_lex_unit::exec()
sl->join->exec(); sl->join->exec();
if (sl == union_distinct) if (sl == union_distinct)
{ {
// This is UNION DISTINCT, so there should be a fake_select_lex
DBUG_ASSERT(fake_select_lex != NULL);
if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL)) if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
table->no_keyread=1; table->no_keyread=1;
@ -714,6 +849,8 @@ bool st_select_lex_unit::exec()
thd->lex->current_select= lex_select_save; thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error); DBUG_RETURN(saved_error);
} }
if (fake_select_lex != NULL)
{
/* Needed for the following test and for records_at_start in next loop */ /* Needed for the following test and for records_at_start in next loop */
int error= table->file->info(HA_STATUS_VARIABLE); int error= table->file->info(HA_STATUS_VARIABLE);
if(error) if(error)
@ -721,6 +858,7 @@ bool st_select_lex_unit::exec()
table->file->print_error(error, MYF(0)); table->file->print_error(error, MYF(0));
DBUG_RETURN(1); DBUG_RETURN(1);
} }
}
if (found_rows_for_union && !sl->braces && if (found_rows_for_union && !sl->braces &&
select_limit_cnt != HA_POS_ERROR) select_limit_cnt != HA_POS_ERROR)
{ {
@ -752,8 +890,6 @@ bool st_select_lex_unit::exec()
DBUG_EXECUTE_IF("show_explain_probe_union_read", DBUG_EXECUTE_IF("show_explain_probe_union_read",
dbug_serve_apcs(thd, 1);); dbug_serve_apcs(thd, 1););
/* Send result to 'result' */
saved_error= TRUE;
{ {
List<Item_func_match> empty_list; List<Item_func_match> empty_list;
empty_list.empty(); empty_list.empty();
@ -763,11 +899,15 @@ bool st_select_lex_unit::exec()
*/ */
thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX; thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
if (!thd->is_fatal_error) // Check if EOM if (fake_select_lex != NULL && !thd->is_fatal_error) // Check if EOM
{ {
set_limit(global_parameters); /* Send result to 'result' */
saved_error= true;
set_limit(global_parameters());
init_prepare_fake_select_lex(thd, first_execution); init_prepare_fake_select_lex(thd, first_execution);
JOIN *join= fake_select_lex->join; JOIN *join= fake_select_lex->join;
saved_error= false;
if (!join) if (!join)
{ {
/* /*
@ -799,7 +939,7 @@ bool st_select_lex_unit::exec()
for this (with a different join object) for this (with a different join object)
*/ */
if (!fake_select_lex->ref_pointer_array) if (!fake_select_lex->ref_pointer_array)
fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items; fake_select_lex->n_child_sum_items+= global_parameters()->n_sum_items;
if (!was_executed) if (!was_executed)
save_union_explain_part2(thd->lex->explain); save_union_explain_part2(thd->lex->explain);
@ -807,8 +947,8 @@ bool st_select_lex_unit::exec()
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array, saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list, &result_table_list,
0, item_list, NULL, 0, item_list, NULL,
global_parameters->order_list.elements, global_parameters()->order_list.elements,
global_parameters->order_list.first, global_parameters()->order_list.first,
NULL, NULL, NULL, NULL, NULL, NULL,
fake_select_lex->options | SELECT_NO_UNLOCK, fake_select_lex->options | SELECT_NO_UNLOCK,
result, this, fake_select_lex); result, this, fake_select_lex);
@ -830,8 +970,8 @@ bool st_select_lex_unit::exec()
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array, saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list, &result_table_list,
0, item_list, NULL, 0, item_list, NULL,
global_parameters->order_list.elements, global_parameters()->order_list.elements,
global_parameters->order_list.first, global_parameters()->order_list.first,
NULL, NULL, NULL, NULL, NULL, NULL,
fake_select_lex->options | SELECT_NO_UNLOCK, fake_select_lex->options | SELECT_NO_UNLOCK,
result, this, fake_select_lex); result, this, fake_select_lex);
@ -907,11 +1047,11 @@ bool st_select_lex_unit::cleanup()
Note: global_parameters and fake_select_lex are always Note: global_parameters and fake_select_lex are always
initialized for UNION initialized for UNION
*/ */
DBUG_ASSERT(global_parameters); DBUG_ASSERT(global_parameters());
if (global_parameters->order_list.elements) if (global_parameters()->order_list.elements)
{ {
ORDER *ord; ORDER *ord;
for (ord= global_parameters->order_list.first; ord; ord= ord->next) for (ord= global_parameters()->order_list.first; ord; ord= ord->next)
(*ord->item)->walk (&Item::cleanup_processor, 0, 0); (*ord->item)->walk (&Item::cleanup_processor, 0, 0);
} }
} }
@ -942,32 +1082,33 @@ void st_select_lex_unit::reinit_exec_mechanism()
} }
/* /**
change select_result object of unit Change the select_result object used to return the final result of
the unit, replacing occurences of old_result with new_result.
SYNOPSIS @param new_result New select_result object
st_select_lex_unit::change_result() @param old_result Old select_result object
result new select_result object
old_result old select_result object
RETURN @retval false Success
FALSE - OK @retval true Error
TRUE - error
*/ */
bool st_select_lex_unit::change_result(select_result_interceptor *new_result, bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
select_result_interceptor *old_result) select_result_interceptor *old_result)
{ {
bool res= FALSE;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{ {
if (sl->join && sl->join->result == old_result) if (sl->join)
if (sl->join->change_result(new_result)) if (sl->join->change_result(new_result, old_result))
return TRUE; return true; /* purecov: inspected */
} }
if (fake_select_lex && fake_select_lex->join) /*
res= fake_select_lex->join->change_result(new_result); If there were a fake_select_lex->join, we would have to change the
return (res); result of that also, but change_result() is called before such an
object is created.
*/
DBUG_ASSERT(fake_select_lex == NULL || fake_select_lex->join == NULL);
return false;
} }
/* /*

View File

@ -657,6 +657,17 @@ bool add_select_to_union_list(LEX *lex, bool is_union_distinct,
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
return TRUE; return TRUE;
} }
if (lex->current_select->order_list.first && !lex->current_select->braces)
{
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
return TRUE;
}
if (lex->current_select->explicit_limit && !lex->current_select->braces)
{
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "LIMIT");
return TRUE;
}
if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
{ {
my_parse_error(ER(ER_SYNTAX_ERROR)); my_parse_error(ER(ER_SYNTAX_ERROR));
@ -684,11 +695,14 @@ bool add_select_to_union_list(LEX *lex, bool is_union_distinct,
bool setup_select_in_parentheses(LEX *lex) bool setup_select_in_parentheses(LEX *lex)
{ {
SELECT_LEX * sel= lex->current_select; SELECT_LEX * sel= lex->current_select;
/*
if (sel->set_braces(1)) if (sel->set_braces(1))
{ {
my_parse_error(ER(ER_SYNTAX_ERROR)); my_parse_error(ER(ER_SYNTAX_ERROR));
return TRUE; return TRUE;
} }
*/
DBUG_ASSERT(sel->braces);
if (sel->linkage == UNION_TYPE && if (sel->linkage == UNION_TYPE &&
!sel->master_unit()->first_select()->braces && !sel->master_unit()->first_select()->braces &&
sel->master_unit()->first_select()->linkage == sel->master_unit()->first_select()->linkage ==
@ -704,10 +718,6 @@ bool setup_select_in_parentheses(LEX *lex)
my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY"); my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY");
return TRUE; return TRUE;
} }
/* select in braces, can't contain global parameters */
if (sel->master_unit()->fake_select_lex)
sel->master_unit()->global_parameters=
sel->master_unit()->fake_select_lex;
return FALSE; return FALSE;
} }
@ -8309,6 +8319,13 @@ select_init:
; ;
select_paren: select_paren:
{
/*
In order to correctly parse UNION's global ORDER BY we need to
set braces before parsing the clause.
*/
Lex->current_select->set_braces(true);
}
SELECT_SYM select_part2 SELECT_SYM select_part2
{ {
if (setup_select_in_parentheses(Lex)) if (setup_select_in_parentheses(Lex))
@ -8319,6 +8336,9 @@ select_paren:
/* The equivalent of select_paren for nested queries. */ /* The equivalent of select_paren for nested queries. */
select_paren_derived: select_paren_derived:
{
Lex->current_select->set_braces(true);
}
SELECT_SYM select_part2_derived SELECT_SYM select_part2_derived
{ {
if (setup_select_in_parentheses(Lex)) if (setup_select_in_parentheses(Lex))
@ -8331,18 +8351,8 @@ select_init2:
select_part2 select_part2
{ {
LEX *lex= Lex; LEX *lex= Lex;
SELECT_LEX * sel= lex->current_select; /* Parentheses carry no meaning here */
if (lex->current_select->set_braces(0)) lex->current_select->set_braces(false);
{
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
}
if (sel->linkage == UNION_TYPE &&
sel->master_unit()->first_select()->braces)
{
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
}
} }
union_clause union_clause
; ;
@ -8371,13 +8381,14 @@ select_into:
; ;
select_from: select_from:
FROM join_table_list where_clause group_clause having_clause FROM join_table_list
opt_order_clause opt_limit_clause procedure_clause
{ {
Select->context.table_list= Select->context.table_list=
Select->context.first_name_resolution_table= Select->context.first_name_resolution_table=
Select->table_list.first; Select->table_list.first;
} }
where_clause group_clause having_clause
opt_order_clause opt_limit_clause procedure_clause
| FROM DUAL_SYM where_clause opt_limit_clause | FROM DUAL_SYM where_clause opt_limit_clause
/* oracle compatibility: oracle always requires FROM clause, /* oracle compatibility: oracle always requires FROM clause,
and DUAL is system table without fields. and DUAL is system table without fields.
@ -10698,10 +10709,6 @@ table_factor:
my_parse_error(ER(ER_SYNTAX_ERROR)); my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT; MYSQL_YYABORT;
} }
/* select in braces, can't contain global parameters */
if (sel->master_unit()->fake_select_lex)
sel->master_unit()->global_parameters=
sel->master_unit()->fake_select_lex;
} }
if ($2->init_nested_join(lex->thd)) if ($2->init_nested_join(lex->thd))
MYSQL_YYABORT; MYSQL_YYABORT;
@ -11246,7 +11253,8 @@ order_clause:
"CUBE/ROLLUP", "ORDER BY"); "CUBE/ROLLUP", "ORDER BY");
MYSQL_YYABORT; MYSQL_YYABORT;
} }
if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) if (lex->sql_command != SQLCOM_ALTER_TABLE &&
!unit->fake_select_lex)
{ {
/* /*
A query of the of the form (SELECT ...) ORDER BY order_list is A query of the of the form (SELECT ...) ORDER BY order_list is
@ -11263,8 +11271,23 @@ order_clause:
unit->add_fake_select_lex(lex->thd)) unit->add_fake_select_lex(lex->thd))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
if (sel->master_unit()->is_union() && !sel->braces)
{
/*
At this point we don't know yet whether this is the last
select in union or not, but we move ORDER BY to
fake_select_lex anyway. If there would be one more select
in union mysql_new_select will correctly throw error.
*/
DBUG_ASSERT(sel->master_unit()->fake_select_lex);
lex->current_select= sel->master_unit()->fake_select_lex;
lex->push_context(&lex->current_select->context);
}
} }
order_list order_list
{
}
; ;
order_list: order_list:
@ -11285,6 +11308,13 @@ opt_limit_clause_init:
{ {
LEX *lex= Lex; LEX *lex= Lex;
SELECT_LEX *sel= lex->current_select; SELECT_LEX *sel= lex->current_select;
if (sel->master_unit()->is_union() && !sel->braces)
{
/* Move LIMIT that belongs to UNION to fake_select_lex */
Lex->current_select= sel->master_unit()->fake_select_lex;
DBUG_ASSERT(Select);
}
sel= lex->current_select;
sel->offset_limit= 0; sel->offset_limit= 0;
sel->select_limit= 0; sel->select_limit= 0;
lex->limit_rows_examined= 0; lex->limit_rows_examined= 0;
@ -11297,19 +11327,33 @@ opt_limit_clause:
| limit_clause {} | limit_clause {}
; ;
limit_clause_init:
LIMIT
{
SELECT_LEX *sel= Select;
if (sel->master_unit()->is_union() && !sel->braces)
{
/* Move LIMIT that belongs to UNION to fake_select_lex */
Lex->current_select= sel->master_unit()->fake_select_lex;
DBUG_ASSERT(Select);
}
}
;
limit_clause: limit_clause:
LIMIT limit_options limit_clause_init limit_options
{ {
SELECT_LEX *sel= Select; SELECT_LEX *sel= Select;
if (!sel->select_limit->basic_const_item() || if (!sel->select_limit->basic_const_item() ||
sel->select_limit->val_int() > 0) sel->select_limit->val_int() > 0)
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
} }
| LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option | limit_clause_init limit_options
ROWS_SYM EXAMINED_SYM limit_rows_option
{ {
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
} }
| LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option
{ {
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
} }
@ -12790,6 +12834,7 @@ flush_options:
YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO; YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO;
} }
opt_table_list opt_flush_lock opt_table_list opt_flush_lock
{}
| flush_options_list | flush_options_list
; ;
@ -15739,7 +15784,6 @@ union_order_or_limit:
SELECT_LEX *fake= unit->fake_select_lex; SELECT_LEX *fake= unit->fake_select_lex;
if (fake) if (fake)
{ {
unit->global_parameters= fake;
fake->no_table_names_allowed= 1; fake->no_table_names_allowed= 1;
lex->current_select= fake; lex->current_select= fake;
} }