Below is the list of changes that have just been committed into a local
5.1 repository of mhansson. When mhansson does a push these changes
will be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2008-02-22 16:37:15+01:00, mhansson@stripped +3 -0
Bug#33204: INTO is allowed in subselect, causing inconsistent results
The parser rule for UNION in nested queries produced, by transitivity,
top-level selects on its right-hand side. This allowed users to use
SELECT INTO in this position, which violates the standard and has
undefined result.
Fixed by duplicating the intermediate rules into a subgrammar for
nested selects where INTO is forbidden.
mysql-test/r/subselect.result@stripped, 2008-02-22 16:37:12+01:00,
mhansson@stripped +204 -6
Bug#33204: Test case.
mysql-test/t/subselect.test@stripped, 2008-02-22 16:37:12+01:00,
mhansson@stripped +163 -6
Bug#33204: Test result
sql/sql_yacc.yy@stripped, 2008-02-22 16:37:12+01:00,
mhansson@stripped +216 -70
Bug#33204
The fix appears to introduce 6 new shift/reduce conflicts. The truth is
1 conflict has disappeared (SELECT_SYM token in <simple_expr>),
1 conflict appears in 3 new places ('(' token in <simple_expr>),
3 conflicts appear in 1 new place each (tokens SQL_BUFFER_RESULT,
SQL_CACHE_SYM and SQL_NO_CACHE_SYM since the rule with the conflict,
select_part2 has been copied) and
1 conflict appears in 1 new place (LIMIT token, since the rule producing it,
select_part2 has been copied).
When the original conflicts are resolved, these conflicts will disappear as well.
diff -Nrup a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
--- a/mysql-test/r/subselect.result 2008-01-20 11:50:25 +01:00
+++ b/mysql-test/r/subselect.result 2008-02-22 16:37:12 +01:00
@@ -3562,9 +3562,7 @@ SELECT sql_no_cache * FROM t1 WHERE NOT
(SELECT i FROM t1) UNION
(SELECT i FROM t1)
);
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION
-(SELECT i FROM t1)
-)' at line 3
+i
SELECT * FROM t1
WHERE NOT EXISTS (((SELECT i FROM t1) UNION (SELECT i FROM t1)));
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION (SELECT i FROM t1)))'
at line 2
@@ -3574,7 +3572,11 @@ ERROR 42000: You have an error in your S
from t1' at line 1
explain select * from t1 where not exists
((select t11.i from t1 t11) union (select t12.i from t1 t12));
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'union (select t12.i from t1
t12))' at line 2
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table
+3 UNION NULL NULL NULL NULL NULL NULL NULL no matching row in const table
+NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
DROP TABLE t1;
CREATE TABLE t1 (a VARCHAR(250), b INT auto_increment, PRIMARY KEY (b));
insert into t1 (a) values (FLOOR(rand() * 100));
@@ -4279,8 +4281,15 @@ Note 1003 select 2 AS `2` from `test`.`t
EXPLAIN EXTENDED
SELECT 2 FROM t1 WHERE EXISTS ((SELECT 1 FROM t2 WHERE t1.a=t2.a) UNION
(SELECT 1 FROM t2 WHERE t1.a = t2.a));
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION
-(SELECT 1 FROM t2 WHERE t1.a = t2.a))' at line 2
+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 Using where
+2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
+3 DEPENDENT UNION t2 ALL NULL NULL NULL NULL 2 100.00 Using where
+NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #1
+Note 1003 select 2 AS `2` from `test`.`t1` where exists((select 1 AS `1` from `test`.`t2`
where (`test`.`t1`.`a` = `test`.`t2`.`a`)) union (select 1 AS `1` from `test`.`t2` where
(`test`.`t1`.`a` = `test`.`t2`.`a`)))
DROP TABLE t1,t2;
create table t1(f11 int, f12 int);
create table t2(f21 int unsigned not null, f22 int, f23 varchar(10));
@@ -4369,3 +4378,192 @@ SELECT * FROM t1 WHERE _utf8'a' = ANY (S
s1
a
DROP TABLE t1;
+CREATE TABLE t1( a INT );
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2( a INT, b INT );
+SELECT *
+FROM (SELECT a INTO @var FROM t1 WHERE a = 2) t1a;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @var FROM t1 WHERE a =
2) t1a' at line 2
+SELECT * FROM (
+SELECT 1 a
+UNION
+SELECT a INTO @var FROM t1 WHERE a = 2
+) t1a;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @var FROM t1 WHERE a = 2
+) t1a' at line 4
+SELECT * FROM (SELECT a FROM t1 WHERE a = 2) t1a;
+a
+2
+SELECT * FROM (
+SELECT a FROM t1 WHERE a = 2
+UNION
+SELECT a FROM t1 WHERE a = 2
+) t1a;
+a
+2
+SELECT * FROM (
+SELECT 1 a
+UNION
+SELECT a FROM t1 WHERE a = 2
+UNION
+SELECT a FROM t1 WHERE a = 2
+) t1a;
+a
+1
+2
+SELECT * FROM ((SELECT 1 a) UNION SELECT 1 a);
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near ')' at line 1
+SELECT * FROM (SELECT 1 a UNION (SELECT 1 a));
+ERROR 42000: Every derived table must have its own alias
+SELECT * FROM (SELECT 1 UNION SELECT 1);
+ERROR 42000: Every derived table must have its own alias
+SELECT * FROM (SELECT 1 UNION SELECT 1) t1a;
+1
+1
+SELECT * FROM ((SELECT 1 a INTO @a)) t1a;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @a)) t1a' at line 1
+SELECT * FROM (SELECT 1 a UNION (SELECT 1 a INTO @a)) t1a;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @a)) t1a' at line 1
+SELECT * FROM (SELECT 1 a UNION ((SELECT 1 a INTO @a))) t1a;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @a))) t1a' at line 1
+SELECT * FROM (SELECT 1 a ORDER BY a) t1a;
+a
+1
+SELECT * FROM (SELECT 1 a UNION SELECT 1 a ORDER BY a) t1a;
+a
+1
+SELECT * FROM (SELECT 1 a UNION SELECT 1 a LIMIT 1) t1a;
+a
+1
+SELECT * FROM (SELECT 1 a UNION SELECT 1 a ORDER BY a LIMIT 1) t1a;
+a
+1
+SELECT * FROM t1 JOIN (SELECT 1 UNION SELECT 1) ON 1;
+ERROR 42000: Every derived table must have its own alias
+SELECT * FROM t1 JOIN ((SELECT 1 UNION SELECT 1)) ON 1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near ')) ON 1' at line 1
+SELECT * FROM t1 JOIN (t1 t1a UNION SELECT 1) ON 1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'ON 1' at line 1
+SELECT * FROM t1 JOIN ((t1 t1a UNION SELECT 1)) ON 1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near ') ON 1' at line 1
+SELECT * FROM t1 JOIN (t1 t1a) t1a ON 1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 't1a ON 1' at line 1
+SELECT * FROM t1 JOIN ((t1 t1a)) t1a ON 1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 't1a ON 1' at line 1
+SELECT * FROM t1 JOIN (t1 t1a) ON 1;
+a a
+1 1
+2 1
+1 2
+2 2
+SELECT * FROM t1 JOIN ((t1 t1a)) ON 1;
+a a
+1 1
+2 1
+1 2
+2 2
+SELECT * FROM (t1 t1a);
+a
+1
+2
+SELECT * FROM ((t1 t1a));
+a
+1
+2
+SELECT * FROM t1 JOIN (SELECT 1 t1a) ON 1;
+ERROR 42000: Every derived table must have its own alias
+SELECT * FROM t1 JOIN ((SELECT 1 t1a)) ON 1;
+ERROR 42000: Every derived table must have its own alias
+SELECT * FROM t1 JOIN (SELECT 1 a) a ON 1;
+a a
+1 1
+2 1
+SELECT * FROM t1 JOIN ((SELECT 1 a)) a ON 1;
+a a
+1 1
+2 1
+SELECT * FROM (t1 JOIN (SELECT 1) t1a1 ON 1) t1a2;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 't1a2' at line 1
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 UNION SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a = ANY ( SELECT 3 UNION SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a = ANY ( SELECT 1 UNION SELECT 1 INTO @a);
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @a)' at line 1
+SELECT * FROM t1 WHERE a = ( SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION SELECT 1 INTO @a);
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @a)' at line 1
+SELECT ( SELECT 1 INTO @v );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @v )' at line 1
+SELECT ( SELECT 1 UNION SELECT 1 INTO @v );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'INTO @v )' at line 1
+SELECT ( SELECT a FROM t1 WHERE a = 1 ), a FROM t1;
+( SELECT a FROM t1 WHERE a = 1 ) a
+1 1
+1 2
+SELECT ( SELECT a FROM t1 WHERE a = 1 UNION SELECT 1 ), a FROM t1;
+( SELECT a FROM t1 WHERE a = 1 UNION SELECT 1 ) a
+1 1
+1 2
+SELECT * FROM t2 WHERE (a, b) IN (SELECT a, b FROM t2);
+a b
+SELECT 1 UNION ( SELECT 1 UNION SELECT 1 );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 )' at line 1
+( SELECT 1 UNION SELECT 1 ) UNION SELECT 1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) UNION SELECT
1' at line 1
+SELECT ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) )' at line 1
+SELECT ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1' at line 1
+SELECT ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+( SELECT 1 UNION SELECT 1 UNION SELECT 1 )
+1
+SELECT ((SELECT 1 UNION SELECT 1 UNION SELECT 1));
+((SELECT 1 UNION SELECT 1 UNION SELECT 1))
+1
+SELECT * FROM ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) )' at line 1
+SELECT * FROM ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near ') UNION SELECT 1 )' at line 1
+SELECT * FROM ( SELECT 1 UNION SELECT 1 UNION SELECT 1 ) a;
+1
+1
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) )' at line 1
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) )' at line 1
+SELECT * FROM t1 WHERE a = ANY ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) )' at line 1
+SELECT * FROM t1 WHERE a IN ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) )' at line 1
+SELECT * FROM t1 WHERE a = ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 )' at line 1
+SELECT * FROM t1 WHERE a = ALL ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) UNION
SELECT 1 )' at line 1
+SELECT * FROM t1 WHERE a = ANY ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 ) UNION
SELECT 1 )' at line 1
+SELECT * FROM t1 WHERE a IN ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'UNION SELECT 1 )' at line 1
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a = ANY ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+a
+1
+SELECT * FROM t1 WHERE a IN ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+a
+1
+DROP TABLE t1, t2;
diff -Nrup a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
--- a/mysql-test/t/subselect.test 2008-01-20 11:50:25 +01:00
+++ b/mysql-test/t/subselect.test 2008-02-22 16:37:12 +01:00
@@ -2455,8 +2455,6 @@ DROP TABLE t1, t2;
CREATE TABLE t1 (i INT);
(SELECT i FROM t1) UNION (SELECT i FROM t1);
-#TODO:not supported
---error ER_PARSE_ERROR
SELECT sql_no_cache * FROM t1 WHERE NOT EXISTS
(
(SELECT i FROM t1) UNION
@@ -2473,8 +2471,6 @@ WHERE NOT EXISTS (((SELECT i FROM t1) UN
explain select ((select t11.i from t1 t11) union (select t12.i from t1 t12))
from t1;
-#TODO:not supported
---error ER_PARSE_ERROR
explain select * from t1 where not exists
((select t11.i from t1 t11) union (select t12.i from t1 t12));
@@ -3128,8 +3124,6 @@ EXPLAIN EXTENDED
SELECT 2 FROM t1 WHERE EXISTS ((SELECT 1 FROM t2 WHERE t1.a=t2.a));
-#TODO:not supported
---error ER_PARSE_ERROR
EXPLAIN EXTENDED
SELECT 2 FROM t1 WHERE EXISTS ((SELECT 1 FROM t2 WHERE t1.a=t2.a) UNION
(SELECT 1 FROM t2 WHERE t1.a = t2.a));
@@ -3249,3 +3243,166 @@ INSERT INTO t1 VALUES ('a');
SELECT * FROM t1 WHERE _utf8'a' = ANY (SELECT s1 FROM t1);
DROP TABLE t1;
+#
+# Bug#33204: INTO is allowed in subselect, causing inconsistent results
+#
+CREATE TABLE t1( a INT );
+INSERT INTO t1 VALUES (1),(2);
+
+CREATE TABLE t2( a INT, b INT );
+
+--error ER_PARSE_ERROR
+SELECT *
+FROM (SELECT a INTO @var FROM t1 WHERE a = 2) t1a;
+
+--error ER_PARSE_ERROR
+SELECT * FROM (
+ SELECT 1 a
+ UNION
+ SELECT a INTO @var FROM t1 WHERE a = 2
+) t1a;
+
+SELECT * FROM (SELECT a FROM t1 WHERE a = 2) t1a;
+
+SELECT * FROM (
+ SELECT a FROM t1 WHERE a = 2
+ UNION
+ SELECT a FROM t1 WHERE a = 2
+) t1a;
+
+SELECT * FROM (
+ SELECT 1 a
+ UNION
+ SELECT a FROM t1 WHERE a = 2
+ UNION
+ SELECT a FROM t1 WHERE a = 2
+) t1a;
+
+# This was not allowed previously. Possibly, it should be allowed on the future.
+# For now, the intent is to keep the fix as non-intrusive as possible.
+--error ER_PARSE_ERROR
+SELECT * FROM ((SELECT 1 a) UNION SELECT 1 a);
+--error ER_DERIVED_MUST_HAVE_ALIAS
+SELECT * FROM (SELECT 1 a UNION (SELECT 1 a));
+--error ER_DERIVED_MUST_HAVE_ALIAS
+SELECT * FROM (SELECT 1 UNION SELECT 1);
+SELECT * FROM (SELECT 1 UNION SELECT 1) t1a;
+--error ER_PARSE_ERROR
+SELECT * FROM ((SELECT 1 a INTO @a)) t1a;
+
+--error ER_PARSE_ERROR
+SELECT * FROM (SELECT 1 a UNION (SELECT 1 a INTO @a)) t1a;
+
+--error ER_PARSE_ERROR
+SELECT * FROM (SELECT 1 a UNION ((SELECT 1 a INTO @a))) t1a;
+
+SELECT * FROM (SELECT 1 a ORDER BY a) t1a;
+SELECT * FROM (SELECT 1 a UNION SELECT 1 a ORDER BY a) t1a;
+SELECT * FROM (SELECT 1 a UNION SELECT 1 a LIMIT 1) t1a;
+SELECT * FROM (SELECT 1 a UNION SELECT 1 a ORDER BY a LIMIT 1) t1a;
+
+# Test of rule
+# table_factor: '(' get_select_lex query_expression_body ')' opt_table_alias
+# UNION should not be allowed inside the parentheses, nor should
+# aliases after.
+#
+--error ER_DERIVED_MUST_HAVE_ALIAS
+SELECT * FROM t1 JOIN (SELECT 1 UNION SELECT 1) ON 1;
+--error ER_PARSE_ERROR
+SELECT * FROM t1 JOIN ((SELECT 1 UNION SELECT 1)) ON 1;
+--error ER_PARSE_ERROR
+SELECT * FROM t1 JOIN (t1 t1a UNION SELECT 1) ON 1;
+--error ER_PARSE_ERROR
+SELECT * FROM t1 JOIN ((t1 t1a UNION SELECT 1)) ON 1;
+--error ER_PARSE_ERROR
+SELECT * FROM t1 JOIN (t1 t1a) t1a ON 1;
+--error ER_PARSE_ERROR
+SELECT * FROM t1 JOIN ((t1 t1a)) t1a ON 1;
+
+SELECT * FROM t1 JOIN (t1 t1a) ON 1;
+SELECT * FROM t1 JOIN ((t1 t1a)) ON 1;
+
+SELECT * FROM (t1 t1a);
+SELECT * FROM ((t1 t1a));
+
+--error ER_DERIVED_MUST_HAVE_ALIAS
+SELECT * FROM t1 JOIN (SELECT 1 t1a) ON 1;
+--error ER_DERIVED_MUST_HAVE_ALIAS
+SELECT * FROM t1 JOIN ((SELECT 1 t1a)) ON 1;
+
+SELECT * FROM t1 JOIN (SELECT 1 a) a ON 1;
+SELECT * FROM t1 JOIN ((SELECT 1 a)) a ON 1;
+
+# For the join, TABLE_LIST::select_lex == NULL
+# Check that we handle this.
+--error ER_PARSE_ERROR
+SELECT * FROM (t1 JOIN (SELECT 1) t1a1 ON 1) t1a2;
+
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 );
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 UNION SELECT 1 );
+SELECT * FROM t1 WHERE a = ANY ( SELECT 3 UNION SELECT 1 );
+
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ANY ( SELECT 1 UNION SELECT 1 INTO @a);
+
+SELECT * FROM t1 WHERE a = ( SELECT 1 );
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION SELECT 1 );
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION SELECT 1 INTO @a);
+
+--error ER_PARSE_ERROR
+SELECT ( SELECT 1 INTO @v );
+--error ER_PARSE_ERROR
+SELECT ( SELECT 1 UNION SELECT 1 INTO @v );
+
+# Make sure context is popped when we leave the nested select
+SELECT ( SELECT a FROM t1 WHERE a = 1 ), a FROM t1;
+SELECT ( SELECT a FROM t1 WHERE a = 1 UNION SELECT 1 ), a FROM t1;
+
+# Make sure we have feature F561 (see .yy file)
+SELECT * FROM t2 WHERE (a, b) IN (SELECT a, b FROM t2);
+
+# Make sure the parser does not allow nested UNIONs anywhere
+
+--error ER_PARSE_ERROR
+SELECT 1 UNION ( SELECT 1 UNION SELECT 1 );
+--error ER_PARSE_ERROR
+( SELECT 1 UNION SELECT 1 ) UNION SELECT 1;
+
+--error ER_PARSE_ERROR
+SELECT ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+--error ER_PARSE_ERROR
+SELECT ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1;
+SELECT ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+SELECT ((SELECT 1 UNION SELECT 1 UNION SELECT 1));
+
+--error ER_PARSE_ERROR
+SELECT * FROM ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+--error ER_PARSE_ERROR
+SELECT * FROM ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+SELECT * FROM ( SELECT 1 UNION SELECT 1 UNION SELECT 1 ) a;
+
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ANY ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a IN ( SELECT 1 UNION ( SELECT 1 UNION SELECT 1 ) );
+
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ALL ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a = ANY ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+--error ER_PARSE_ERROR
+SELECT * FROM t1 WHERE a IN ( ( SELECT 1 UNION SELECT 1 ) UNION SELECT 1 );
+
+SELECT * FROM t1 WHERE a = ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+SELECT * FROM t1 WHERE a = ALL ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+SELECT * FROM t1 WHERE a = ANY ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+SELECT * FROM t1 WHERE a IN ( SELECT 1 UNION SELECT 1 UNION SELECT 1 );
+
+DROP TABLE t1, t2;
diff -Nrup a/sql/sql_yacc.yy b/sql/sql_yacc.yy
--- a/sql/sql_yacc.yy 2008-02-01 09:26:36 +01:00
+++ b/sql/sql_yacc.yy 2008-02-22 16:37:12 +01:00
@@ -457,6 +457,83 @@ Item* handle_sql2003_note184_exception(T
DBUG_RETURN(result);
}
+/**
+ @brief Creates a new SELECT_LEX for a UNION branch.
+
+ Sets up and initializes a SELECT_LEX structure for a query once the parser
+ discovers a UNION token. The current SELECT_LEX is pushed on the stack and
+ the new SELECT_LEX becomes the current one..=
+
+ @lex The parser state.
+
+ @is_union_distinct True if the union preceding the new select statement
+ uses UNION DISTINCT.
+
+ @return <code>false</code> if successful, <code>true</code> if
an error was
+ reported. In the latter case parsing should stop.
+ */
+bool add_select_to_union_list(LEX *lex, bool is_union_distinct)
+{
+ if (lex->result)
+ {
+ /* Only the last SELECT can have INTO...... */
+ my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
+ return TRUE;
+ }
+ if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ return TRUE;
+ }
+ /* This counter shouldn't be incremented for UNION parts */
+ lex->nest_level--;
+ if (mysql_new_select(lex, 0))
+ return TRUE;
+ mysql_init_select(lex);
+ lex->current_select->linkage=UNION_TYPE;
+ if (is_union_distinct) /* UNION DISTINCT - remember position */
+ lex->current_select->master_unit()->union_distinct=
+ lex->current_select;
+ return FALSE;
+}
+
+/**
+ @brief Initializes a SELECT_LEX for a query within parentheses (aka
+ braces).
+
+ @return false if successful, true if an error was reported. In the latter
+ case parsing should stop.
+ */
+bool setup_select_in_parentheses(LEX *lex)
+{
+ SELECT_LEX * sel= lex->current_select;
+ if (sel->set_braces(1))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ return TRUE;
+ }
+ if (sel->linkage == UNION_TYPE &&
+ !sel->master_unit()->first_select()->braces &&
+ sel->master_unit()->first_select()->linkage ==
+ UNION_TYPE)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ return TRUE;
+ }
+ if (sel->linkage == UNION_TYPE &&
+ sel->olap != UNSPECIFIED_OLAP_TYPE &&
+ sel->master_unit()->fake_select_lex)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY");
+ 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;
+}
+
%}
%union {
int num;
@@ -511,7 +588,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
Currently there are 177 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 177
+%expect 183
/*
Comments for TOKENS.
@@ -1195,6 +1272,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
join_table_list join_table
table_factor table_ref
select_derived derived_table_list
+ select_derived_union
%type <date_time_type> date_time_type;
%type <interval> interval
@@ -1228,8 +1306,9 @@ bool my_yyoverflow(short **a, YYSTYPE **
%type <variable> internal_variable_name
-%type <select_lex> subselect take_first_select
- get_select_lex
+%type <select_lex> subselect
+ get_select_lex query_specification
+ query_expression_body
%type <boolfunc2creator> comp_op
@@ -6136,37 +6215,22 @@ select_init:
select_paren:
SELECT_SYM select_part2
{
- LEX *lex= Lex;
- SELECT_LEX * sel= lex->current_select;
- if (sel->set_braces(1))
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
+ if (setup_select_in_parentheses(Lex))
MYSQL_YYABORT;
- }
- if (sel->linkage == UNION_TYPE &&
- !sel->master_unit()->first_select()->braces &&
- sel->master_unit()->first_select()->linkage ==
- UNION_TYPE)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- if (sel->linkage == UNION_TYPE &&
- sel->olap != UNSPECIFIED_OLAP_TYPE &&
- sel->master_unit()->fake_select_lex)
- {
- my_error(ER_WRONG_USAGE, MYF(0),
- "CUBE/ROLLUP", "ORDER BY");
- 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;
}
| '(' select_paren ')'
;
+/* The equivalent of select_paren for nested queries. */
+select_paren_derived:
+ SELECT_SYM select_part2_derived
+ {
+ if (setup_select_in_parentheses(Lex))
+ MYSQL_YYABORT;
+ }
+ | '(' select_paren_derived ')'
+ ;
+
select_init2:
select_part2
{
@@ -6659,7 +6723,7 @@ simple_expr:
{ $$= negate_expression(YYTHD, $2); }
| '(' subselect ')'
{
- $$= new (YYTHD->mem_root) Item_singlerow_subselect($2);
+ $$= new (YYTHD->mem_root) Item_singlerow_subselect($2);
}
| '(' expr ')' { $$= $2; }
| '(' expr ',' expr_list ')'
@@ -6674,7 +6738,7 @@ simple_expr:
}
| EXISTS '(' subselect ')'
{
- $$= new (YYTHD->mem_root) Item_exists_subselect($3);
+ $$= new (YYTHD->mem_root) Item_exists_subselect($3);
}
| '{' ident expr '}' { $$= $3; }
| MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
@@ -7437,6 +7501,7 @@ when_list:
}
;
+/* Equivalent to <from clause> in the SQL:2003 standard. */
/* Warning - may return NULL in case of incomplete SELECT */
table_ref:
table_factor { $$=$1; }
@@ -7448,10 +7513,12 @@ table_ref:
}
;
+/* Equivalent to <from clause> in the SQL:2003 standard. */
join_table_list:
derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); }
;
+/* Equivalent to <table reference list> in the SQL:2003 standard. */
/* Warning - may return NULL in case of incomplete SELECT */
derived_table_list:
table_ref { $$=$1; }
@@ -7605,6 +7672,13 @@ normal_join:
| CROSS JOIN_SYM {}
;
+/*
+ This is a flattening of the rules <table factor> and <table primary>
+ in the SQL:2003 standard, since we don't have <sample clause>
+
+ I.e.
+ <table factor> ::= <table primary> [ <sample clause> ]
+*/
/* Warning - may return NULL in case of incomplete SELECT */
table_factor:
{
@@ -7661,12 +7735,29 @@ table_factor:
/* incomplete derived tables return NULL, we must be
nested in select_derived rule to be here. */
}
- | '(' get_select_lex select_derived union_opt ')' opt_table_alias
+ /*
+ Represents a flattening of the following rules from the SQL:2003
+ standard. This sub-rule corresponds to the sub-rule
+ <table primary> ::= ... | <derived table> [ AS ] <correlation
name>
+
+ The following rules have been flattened into query_expression_body
+ (since we have no <with clause>).
+
+ <derived table> ::= <table subquery>
+ <table subquery> ::= <subquery>
+ <subquery> ::= <left paren> <query expression> <right
paren>
+ <query expression> ::= [ <with clause> ] <query expression
body>
+
+ For the time being we use the non-standard rule
+ select_derived_union which is a compromise between the standard
+ and our parser. Possibly this rule could be replaced by our
+ query_expression_body.
+ */
+ | '(' get_select_lex select_derived_union ')' opt_table_alias
{
/* Use $2 instead of Lex->current_select as derived table will
alter value of Lex->current_select. */
-
- if (!($3 || $6) && $2->embedding &&
+ if (!($3 || $5) && $2->embedding &&
!$2->embedding->nested_join->join_list.elements)
{
/* we have a derived table ($3 == NULL) but no alias,
@@ -7685,14 +7776,15 @@ table_factor:
SELECT_LEX_UNIT *unit= sel->master_unit();
lex->current_select= sel= unit->outer_select();
if (!($$= sel->add_table_to_list(lex->thd,
- new Table_ident(unit), $6, 0,
+ new Table_ident(unit), $5, 0,
TL_READ)))
MYSQL_YYABORT;
sel->add_joined_table($$);
lex->pop_context();
}
- else if ($4 || $6)
+ else if ($3->select_lex &&
+ $3->select_lex->master_unit()->is_union() || $5)
{
/* simple nested joins cannot have aliases or unions */
my_parse_error(ER(ER_SYNTAX_ERROR));
@@ -7703,6 +7795,62 @@ table_factor:
}
;
+select_derived_union:
+ select_derived opt_order_clause opt_limit_clause
+ | select_derived_union
+ UNION_SYM
+ union_option
+ {
+ if (add_select_to_union_list(Lex, (bool)$3))
+ MYSQL_YYABORT;
+ }
+ query_specification
+ {
+ /*
+ Remove from the name resolution context stack the context of the
+ last select in the union.
+ */
+ Lex->pop_context();
+ }
+ opt_order_clause opt_limit_clause
+ ;
+
+/* The equivalent of select_init2 for nested queries. */
+select_init2_derived:
+ select_part2_derived
+ {
+ LEX *lex= Lex;
+ SELECT_LEX * sel= lex->current_select;
+ if (lex->current_select->set_braces(0))
+ {
+ 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;
+ }
+ }
+ ;
+
+/* The equivalent of select_part2 for nested queries. */
+select_part2_derived:
+ {
+ LEX *lex= Lex;
+ SELECT_LEX *sel= lex->current_select;
+ if (sel->linkage != UNION_TYPE)
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
+ }
+ select_options select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ opt_select_from select_lock_type
+ ;
+
/* handle contents of parentheses in join expression */
select_derived:
get_select_lex
@@ -11584,27 +11732,8 @@ union_clause:
union_list:
UNION_SYM union_option
{
- LEX *lex=Lex;
- if (lex->result)
- {
- /* Only the last SELECT can have INTO...... */
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
+ if (add_select_to_union_list(Lex, (bool)$2))
MYSQL_YYABORT;
- }
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- /* This counter shouldn't be incremented for UNION parts */
- Lex->nest_level--;
- if (mysql_new_select(lex, 0))
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->linkage=UNION_TYPE;
- if ($2) /* UNION DISTINCT - remember position */
- lex->current_select->master_unit()->union_distinct=
- lex->current_select;
}
select_init
{
@@ -11657,22 +11786,39 @@ union_option:
| ALL { $$=0; }
;
-take_first_select: /* empty */
- {
- $$= Lex->current_select->master_unit()->first_select();
- };
+query_specification:
+ SELECT_SYM select_init2_derived
+ {
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ | '(' select_paren_derived ')'
+ {
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ ;
+
+query_expression_body:
+ query_specification
+ | query_expression_body
+ UNION_SYM union_option
+ {
+ if (add_select_to_union_list(Lex, (bool)$3))
+ MYSQL_YYABORT;
+ }
+ query_specification
+ {
+ Lex->pop_context();
+ $$= $1;
+ }
+ ;
+/* Corresponds to <query expression> in the SQL:2203 standard. */
subselect:
- SELECT_SYM subselect_start select_init2 take_first_select
- subselect_end
- {
- $$= $4;
- }
- | '(' subselect_start select_paren take_first_select
- subselect_end ')'
- {
- $$= $4;
- };
+ subselect_start query_expression_body subselect_end
+ {
+ $$= $2;
+ }
+ ;
subselect_start:
{
| Thread |
|---|
| • bk commit into 5.1 tree (mhansson:1.2659) BUG#33204 | mhansson | 22 Feb |