diff --git a/mysql-test/r/bug96677.result b/mysql-test/r/bug96677.result new file mode 100644 index 00000000000..f6073dc9e02 --- /dev/null +++ b/mysql-test/r/bug96677.result @@ -0,0 +1,36 @@ +CREATE TABLE t1(id INT); +INSERT INTO t1 VALUES (1); +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 INTO @id_var1 FOR UPDATE; +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 FOR UPDATE INTO @id_var2; +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 INTO @id_var3; +SELECT @id_var1, @id_var2, @id_var3; +@id_var1 @id_var2 @id_var3 +1 1 1 +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 INTO @id_var4 FOR UPDATE INTO @id_var5; +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 @id_var5' at line 1 +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 FOR UPDATE INTO @id_var2 FOR UPDATE; +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 'FOR UPDATE' at line 1 +SELECT 1 FROM t1 INTO @var5 UNION SELECT 2 FROM t1 WHERE id = 2; +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 2 FROM t1 WHERE id = 2' at line 1 +SELECT 1 FROM t1 WHERE id = 2 UNION SELECT 2 FROM t1 INTO @var5; +SELECT 1 FROM t1 WHERE id = 2 UNION SELECT 2 FROM t1 FOR UPDATE INTO @var6; +SELECT 1 FROM t1 WHERE id = 2 UNION SELECT 2 FROM t1 INTO @var7 FOR UPDATE; +SELECT @var5, @var6, @var7; +@var5 @var6 @var7 +2 2 2 +WITH +qt1 AS (SELECT id FROM t1), +qt2 AS (SELECT id AS id2 FROM t1) +SELECT id FROM qt1 JOIN qt2 +WHERE qt1.id = qt2.id2 FOR UPDATE INTO @var8; +WITH +qt1 AS (SELECT id FROM t1), +qt2 AS (SELECT id AS id2 FROM t1) +SELECT id FROM qt1 JOIN qt2 +WHERE qt1.id = qt2.id2 INTO @var9 FOR UPDATE; +WITH +qt1 AS (SELECT id FROM t1), +qt2 AS (SELECT id AS id2 FROM t1) +SELECT id FROM qt1 JOIN qt2 +WHERE qt1.id = qt2.id2 INTO @var10; +DROP TABLE t1; diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result index be86c771a1c..33527093b63 100644 --- a/mysql-test/r/parser.result +++ b/mysql-test/r/parser.result @@ -770,15 +770,15 @@ FROM t1 WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 LIMIT 1 INTO @var17727401_2; 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 @var17727401_2' at line 3 SELECT (SELECT 1 FROM t1 INTO @var17727401); -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 @var17727401)' at line 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 '(SELECT 1 FROM t1 INTO @var17727401)' at line 1 SELECT 1 FROM (SELECT 1 FROM t1 INTO @var17727401) 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 @var17727401) a' at line 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 '(SELECT 1 FROM t1 INTO @var17727401) a' at line 1 SELECT EXISTS(SELECT 1 FROM t1 INTO @var17727401); -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 @var17727401)' at line 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 '(SELECT 1 FROM t1 INTO @var17727401)' at line 1 SELECT 1 FROM t1 INTO @var17727401 UNION SELECT 1 FROM t1 INTO 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 1 FROM t1 INTO t1' at line 1 (SELECT 1 FROM t1 INTO @var17727401) UNION (SELECT 1 FROM t1 INTO 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 1 FROM t1 INTO t1)' at line 1 +ERROR HY000: Incorrect usage of UNION and INTO SELECT 1 FROM t1 UNION SELECT 1 FROM t1 INTO @var17727401; Warnings: Warning 1329 No data - zero rows fetched, selected, or processed @@ -1504,7 +1504,7 @@ a a a SELECT * FROM (t1 INNER JOIN t2 ON (t1.a = t2.a)); a a SELECT 1 FROM (SELECT 1 FROM t1 INTO @v) 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 @v) a' at line 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 '(SELECT 1 FROM t1 INTO @v) a' at line 1 SELECT 1 FROM (t1); 1 1 @@ -1657,11 +1657,11 @@ SELECT 1 FROM t1 INTO @v; SELECT 1 FROM t1 INTO @v 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 FROM t1 INTO @v) 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 +ERROR HY000: Incorrect usage of UNION and INTO SELECT 1 FROM t1 INTO @v 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 FROM t1 INTO @v) 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 +ERROR HY000: Incorrect usage of UNION and INTO SELECT 1 INTO @v UNION SELECT 1; ERROR HY000: Incorrect usage of UNION and INTO (SELECT 1 INTO @v) UNION SELECT 1; diff --git a/mysql-test/t/bug96677.test b/mysql-test/t/bug96677.test new file mode 100644 index 00000000000..0476d8be6ee --- /dev/null +++ b/mysql-test/t/bug96677.test @@ -0,0 +1,46 @@ +CREATE TABLE t1(id INT); +INSERT INTO t1 VALUES (1); + +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 INTO @id_var1 FOR UPDATE; +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 FOR UPDATE INTO @id_var2; +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 INTO @id_var3; + +SELECT @id_var1, @id_var2, @id_var3; + +--error ER_PARSE_ERROR +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 INTO @id_var4 FOR UPDATE INTO @id_var5; + +--error ER_PARSE_ERROR +SELECT id AS foobar FROM t1 GROUP BY id LIMIT 1 FOR UPDATE INTO @id_var2 FOR UPDATE; + +# This fails with a different error message in 5.7... but fails +--error ER_PARSE_ERROR +SELECT 1 FROM t1 INTO @var5 UNION SELECT 2 FROM t1 WHERE id = 2; + +SELECT 1 FROM t1 WHERE id = 2 UNION SELECT 2 FROM t1 INTO @var5; + +SELECT 1 FROM t1 WHERE id = 2 UNION SELECT 2 FROM t1 FOR UPDATE INTO @var6; + +SELECT 1 FROM t1 WHERE id = 2 UNION SELECT 2 FROM t1 INTO @var7 FOR UPDATE; + +SELECT @var5, @var6, @var7; + +WITH + qt1 AS (SELECT id FROM t1), + qt2 AS (SELECT id AS id2 FROM t1) +SELECT id FROM qt1 JOIN qt2 +WHERE qt1.id = qt2.id2 FOR UPDATE INTO @var8; + +WITH + qt1 AS (SELECT id FROM t1), + qt2 AS (SELECT id AS id2 FROM t1) +SELECT id FROM qt1 JOIN qt2 +WHERE qt1.id = qt2.id2 INTO @var9 FOR UPDATE; + +WITH + qt1 AS (SELECT id FROM t1), + qt2 AS (SELECT id AS id2 FROM t1) +SELECT id FROM qt1 JOIN qt2 +WHERE qt1.id = qt2.id2 INTO @var10; + +DROP TABLE t1; diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test index 6d88eee7a8a..fe9870364f4 100644 --- a/mysql-test/t/parser.test +++ b/mysql-test/t/parser.test @@ -913,7 +913,7 @@ SELECT EXISTS(SELECT 1 FROM t1 INTO @var17727401); --error ER_PARSE_ERROR SELECT 1 FROM t1 INTO @var17727401 UNION SELECT 1 FROM t1 INTO t1; ---error ER_PARSE_ERROR +--error ER_WRONG_USAGE (SELECT 1 FROM t1 INTO @var17727401) UNION (SELECT 1 FROM t1 INTO t1); SELECT 1 FROM t1 UNION SELECT 1 FROM t1 INTO @var17727401; @@ -1650,11 +1650,11 @@ SELECT 1 FROM t1 INTO @v; --error ER_PARSE_ERROR SELECT 1 FROM t1 INTO @v UNION SELECT 1; ---error ER_PARSE_ERROR +--error ER_WRONG_USAGE (SELECT 1 FROM t1 INTO @v) UNION SELECT 1; --error ER_PARSE_ERROR SELECT 1 FROM t1 INTO @v UNION (SELECT 1); ---error ER_PARSE_ERROR +--error ER_WRONG_USAGE ((SELECT 1 FROM t1 INTO @v) UNION (SELECT 1)); --error ER_WRONG_USAGE diff --git a/sql/parse_tree_nodes.h b/sql/parse_tree_nodes.h index 76309e32164..114875b2bcd 100644 --- a/sql/parse_tree_nodes.h +++ b/sql/parse_tree_nodes.h @@ -917,6 +917,9 @@ class PT_query_expression_body : public Parse_tree_node { virtual bool is_union() const = 0; virtual void set_containing_qe(PT_query_expression *) {} virtual bool has_into_clause() const = 0; + + /* @returns false if couldn't set, and parse error should be reported */ + virtual bool set_into(PT_into_destination *) { return false; } }; class PT_internal_variable_name : public Parse_tree_node { @@ -1758,6 +1761,8 @@ class PT_window_list : public Parse_tree_node { class PT_query_primary : public Parse_tree_node { public: virtual bool has_into_clause() const = 0; + /* @returns false if couldn't set, and parse error should be reported */ + virtual bool set_into(PT_into_destination *) { return false; } virtual bool is_union() const = 0; }; @@ -1822,6 +1827,12 @@ class PT_query_specification : public PT_query_primary { virtual bool has_into_clause() const { return opt_into1 != NULL; } + bool set_into(PT_into_destination *into) override { + if (opt_into1 != NULL) return false; + opt_into1 = into; + return true; + } + virtual bool is_union() const { return false; } }; @@ -1874,6 +1885,8 @@ class PT_query_expression : public Parse_tree_node { bool has_into_clause() const { return m_body->has_into_clause(); } + bool set_into(PT_into_destination *into) { return m_body->set_into(into); } + /** Callback for deeper nested query expressions. It's mandatory for any derived class to call this member function during contextualize. @@ -2018,6 +2031,10 @@ class PT_query_expression_body_primary : public PT_query_expression_body { return m_query_primary->has_into_clause(); } + bool set_into(PT_into_destination *into) override { + return m_query_primary->set_into(into); + } + private: PT_query_primary *m_query_primary; }; @@ -2044,6 +2061,14 @@ class PT_union : public PT_query_expression_body { return m_lhs->has_into_clause() || m_rhs->has_into_clause(); } + bool set_into(PT_into_destination *into) override { + /* INTO is only supported at the right-hand side. + Also, when parsing an union expression, and encountering + an into statement, the parser is already working on the + RHS. */ + return m_rhs->set_into(into); + } + private: PT_query_expression *m_lhs; POS m_lhs_pos; @@ -2072,6 +2097,10 @@ class PT_nested_query_expression : public PT_query_primary { bool has_into_clause() const { return m_qe->has_into_clause(); } + bool set_into(PT_into_destination *into) override { + return m_qe->set_into(into); + } + private: PT_query_expression *m_qe; }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 37ca940f472..0c4fa36e4ce 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -444,7 +444,7 @@ void warn_about_deprecated_national(THD *thd) 1. We do not accept any reduce/reduce conflicts 2. We should not introduce new shift/reduce conflicts any more. */ -%expect 107 +%expect 109 /* MAINTAINER: @@ -9054,6 +9054,17 @@ query_expression: { $$= NEW_PTN PT_query_expression($1, $2, $3, $4); } + | query_expression_body + opt_order_clause + opt_limit_clause + into_clause + opt_locking_clause_list + { + if(!$1->set_into($4)) { + YYTHD->syntax_error_at(@4); + } + $$= NEW_PTN PT_query_expression($1, $2, $3, $5); + } | with_clause query_expression_body opt_order_clause @@ -9062,6 +9073,18 @@ query_expression: { $$= NEW_PTN PT_query_expression($1, $2, $3, $4, $5); } + | with_clause + query_expression_body + opt_order_clause + opt_limit_clause + into_clause + opt_locking_clause_list + { + if(!$2->set_into($5)) { + YYTHD->syntax_error_at(@5); + } + $$= NEW_PTN PT_query_expression($1, $2, $3, $4, $6); + } | query_expression_parens order_clause opt_limit_clause