From c0a59b46be5be341bd6ffc9fe188a236ced46522 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 6 May 2016 11:42:48 +0400 Subject: [PATCH] MDEV-10030 sql_yacc.yy: Split table_expression and remove PROCEDURE from create_select, select_paren_derived, select_derived2, query_specification This change refactors the "table_expression" rule in sql_yacc.yy. Queries with subselects and derived tables, as well as "CREATE TABLE ... SELECT" now return syntax error instead of "Incorrect usage of PROCEDURE and ...". --- mysql-test/r/func_analyse.result | 26 +++++++- mysql-test/r/parser.result | 4 +- mysql-test/r/subselect.result | 2 +- mysql-test/r/subselect_no_exists_to_in.result | 2 +- mysql-test/r/subselect_no_mat.result | 2 +- mysql-test/r/subselect_no_opts.result | 2 +- mysql-test/r/subselect_no_scache.result | 2 +- mysql-test/r/subselect_no_semijoin.result | 2 +- mysql-test/t/func_analyse.test | 28 +++++++- mysql-test/t/parser.test | 4 +- mysql-test/t/subselect.test | 2 +- sql/sql_select.cc | 7 ++ sql/sql_yacc.yy | 65 ++++++++++++------- 13 files changed, 110 insertions(+), 38 deletions(-) diff --git a/mysql-test/r/func_analyse.result b/mysql-test/r/func_analyse.result index 2c300559a32..bc8ec445e73 100644 --- a/mysql-test/r/func_analyse.result +++ b/mysql-test/r/func_analyse.result @@ -19,7 +19,7 @@ test.t1.empty_string 0 0 4 0 0.0000 NULL CHAR(0) NOT NULL test.t1.bool N Y 1 1 0 0 1.0000 NULL ENUM('N','Y') NOT NULL test.t1.d 2002-03-03 2002-03-05 10 10 0 0 10.0000 NULL ENUM('2002-03-03','2002-03-04','2002-03-05') NOT NULL create table t2 select * from t1 procedure analyse(); -ERROR HY000: Incorrect usage of PROCEDURE and non-SELECT +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 'procedure analyse()' at line 1 drop table t1; EXPLAIN SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(); ERROR HY000: Incorrect usage of PROCEDURE and subquery @@ -120,7 +120,7 @@ CREATE TABLE t1(a INT); INSERT INTO t1 VALUES (1),(2); # should not crash CREATE TABLE t2 SELECT 1 FROM t1, t1 t3 GROUP BY t3.a PROCEDURE ANALYSE(); -ERROR HY000: Incorrect usage of PROCEDURE and non-SELECT +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 'PROCEDURE ANALYSE()' at line 1 DROP TABLE t1; End of 5.0 tests # @@ -149,3 +149,25 @@ Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_ test.t2.f2 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL DROP TABLE t1, t2; End of 5.1 tests +# +# Start of 10.2 tests +# +(SELECT 1 FROM DUAL PROCEDURE ANALYSE()); +Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype +1 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL +((SELECT 1 FROM DUAL PROCEDURE ANALYSE())); +Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype +1 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL +SELECT * FROM t1 UNION SELECT * FROM t1 PROCEDURE analyse(); +ERROR HY000: Incorrect usage of PROCEDURE and subquery +# +# MDEV-10030 sql_yacc.yy: Split table_expression and remove PROCEDURE from create_select, select_paren_derived, select_derived2, query_specification +# +SELECT * FROM (SELECT * FROM t1 PROCEDURE ANALYSE()); +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 'PROCEDURE ANALYSE())' at line 1 +SELECT * FROM t1 NATURAL JOIN (SELECT * FROM t2 PROCEDURE ANALYSE()); +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 'PROCEDURE ANALYSE())' at line 1 +SELECT (SELECT 1 FROM t1 PROCEDURE ANALYSE()) FROM t2; +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 'PROCEDURE ANALYSE()) FROM t2' at line 1 +SELECT ((SELECT 1 FROM t1 PROCEDURE ANALYSE())) FROM t2; +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 'PROCEDURE ANALYSE())) FROM t2' at line 1 diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result index 01cc9d79aaf..18a8e13815c 100644 --- a/mysql-test/r/parser.result +++ b/mysql-test/r/parser.result @@ -686,7 +686,7 @@ FOR UPDATE) a; SELECT 1 FROM (SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 PROCEDURE ANALYSE() FOR UPDATE) a; -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE() FOR UPDATE) a' at line 3 SELECT 1 FROM t1 WHERE EXISTS(SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 FOR UPDATE); @@ -694,7 +694,7 @@ FOR UPDATE); SELECT 1 FROM t1 WHERE EXISTS(SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 PROCEDURE ANALYSE() FOR UPDATE); -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE() FOR UPDATE)' at line 3 SELECT 1 FROM t1 UNION SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index b306965034b..1b1198958b8 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -79,7 +79,7 @@ SELECT 1 FROM (SELECT 1 as a) b WHERE 1 IN (SELECT (SELECT a)); 1 1 select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE(1))' at line 1 SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 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 'SELECT 1))' at line 1 SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NULL; diff --git a/mysql-test/r/subselect_no_exists_to_in.result b/mysql-test/r/subselect_no_exists_to_in.result index 244f6057a2f..39d1f64410e 100644 --- a/mysql-test/r/subselect_no_exists_to_in.result +++ b/mysql-test/r/subselect_no_exists_to_in.result @@ -83,7 +83,7 @@ SELECT 1 FROM (SELECT 1 as a) b WHERE 1 IN (SELECT (SELECT a)); 1 1 select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE(1))' at line 1 SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 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 'SELECT 1))' at line 1 SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NULL; diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index e095ed16ff9..211d3206a7f 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -86,7 +86,7 @@ SELECT 1 FROM (SELECT 1 as a) b WHERE 1 IN (SELECT (SELECT a)); 1 1 select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE(1))' at line 1 SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 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 'SELECT 1))' at line 1 SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NULL; diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index f4714526613..6ae9f8aa066 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -82,7 +82,7 @@ SELECT 1 FROM (SELECT 1 as a) b WHERE 1 IN (SELECT (SELECT a)); 1 1 select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE(1))' at line 1 SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 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 'SELECT 1))' at line 1 SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NULL; diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index 5ead5de4cf9..9a3fcd1de3b 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -85,7 +85,7 @@ SELECT 1 FROM (SELECT 1 as a) b WHERE 1 IN (SELECT (SELECT a)); 1 1 select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE(1))' at line 1 SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 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 'SELECT 1))' at line 1 SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NULL; diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index c57c46b76ff..10cf05649e7 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -82,7 +82,7 @@ SELECT 1 FROM (SELECT 1 as a) b WHERE 1 IN (SELECT (SELECT a)); 1 1 select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); -ERROR HY000: Incorrect usage of PROCEDURE and subquery +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 'PROCEDURE ANALYSE(1))' at line 1 SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 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 'SELECT 1))' at line 1 SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NULL; diff --git a/mysql-test/t/func_analyse.test b/mysql-test/t/func_analyse.test index c77967a0cc9..6c30c0ca630 100644 --- a/mysql-test/t/func_analyse.test +++ b/mysql-test/t/func_analyse.test @@ -11,7 +11,7 @@ insert into t1 values (1,2,"","Y","2002-03-03"), (3,4,"","N","2002-03-04"), (5,6 select count(*) from t1 procedure analyse(); select * from t1 procedure analyse(); select * from t1 procedure analyse(2); ---error ER_WRONG_USAGE +--error ER_PARSE_ERROR create table t2 select * from t1 procedure analyse(); drop table t1; @@ -127,7 +127,7 @@ CREATE TABLE t1(a INT); INSERT INTO t1 VALUES (1),(2); --echo # should not crash ---error ER_WRONG_USAGE +--error ER_PARSE_ERROR CREATE TABLE t2 SELECT 1 FROM t1, t1 t3 GROUP BY t3.a PROCEDURE ANALYSE(); DROP TABLE t1; @@ -157,3 +157,27 @@ SELECT * FROM t2 LIMIT 1 PROCEDURE ANALYSE(); DROP TABLE t1, t2; --echo End of 5.1 tests + +--echo # +--echo # Start of 10.2 tests +--echo # +(SELECT 1 FROM DUAL PROCEDURE ANALYSE()); +((SELECT 1 FROM DUAL PROCEDURE ANALYSE())); + +# TODO: +--error ER_WRONG_USAGE +SELECT * FROM t1 UNION SELECT * FROM t1 PROCEDURE analyse(); + +--echo # +--echo # MDEV-10030 sql_yacc.yy: Split table_expression and remove PROCEDURE from create_select, select_paren_derived, select_derived2, query_specification +--echo # + +--error ER_PARSE_ERROR +SELECT * FROM (SELECT * FROM t1 PROCEDURE ANALYSE()); +--ERROR ER_PARSE_ERROR +SELECT * FROM t1 NATURAL JOIN (SELECT * FROM t2 PROCEDURE ANALYSE()); + +--error ER_PARSE_ERROR +SELECT (SELECT 1 FROM t1 PROCEDURE ANALYSE()) FROM t2; +--error ER_PARSE_ERROR +SELECT ((SELECT 1 FROM t1 PROCEDURE ANALYSE())) FROM t2; diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test index 0a19b03a4eb..86cc3c47c37 100644 --- a/mysql-test/t/parser.test +++ b/mysql-test/t/parser.test @@ -806,7 +806,7 @@ SELECT 1 FROM (SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 FOR UPDATE) a; ---error ER_WRONG_USAGE +--error ER_PARSE_ERROR SELECT 1 FROM (SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 PROCEDURE ANALYSE() FOR UPDATE) a; @@ -815,7 +815,7 @@ SELECT 1 FROM t1 WHERE EXISTS(SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 FOR UPDATE); ---error ER_WRONG_USAGE +--error ER_PARSE_ERROR SELECT 1 FROM t1 WHERE EXISTS(SELECT 1 FROM DUAL WHERE 1 GROUP BY 1 HAVING 1 ORDER BY 1 PROCEDURE ANALYSE() FOR UPDATE); diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 7a7c01e78bd..5313a4b8a39 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -51,7 +51,7 @@ SELECT * FROM (SELECT 1 as id) b WHERE id IN (SELECT * FROM (SELECT 1 as id) c O SELECT * FROM (SELECT 1) a WHERE 1 IN (SELECT 1,1); SELECT 1 IN (SELECT 1); SELECT 1 FROM (SELECT 1 as a) b WHERE 1 IN (SELECT (SELECT a)); --- error ER_WRONG_USAGE +-- error ER_PARSE_ERROR select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); -- error ER_PARSE_ERROR SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 1)); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a6bbfc806e1..6c4d2e1fc9c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -988,6 +988,12 @@ JOIN::prepare(TABLE_LIST *tables_init, } if (thd->lex->derived_tables) { + /* + Queries with derived tables and PROCEDURE are not allowed. + Many of such queries are disallowed grammatically, but there + are still some complex cases: + SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE() + */ my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", thd->lex->derived_tables & DERIVED_VIEW ? "view" : "subquery"); @@ -995,6 +1001,7 @@ JOIN::prepare(TABLE_LIST *tables_init, } if (thd->lex->sql_command != SQLCOM_SELECT) { + // EXPLAIN SELECT * FROM t1 PROCEDURE ANALYSE() my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "non-SELECT"); goto err; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5e1f5dc5058..04c1ba7e99a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1037,10 +1037,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 124 shift/reduce conflicts. + Currently there are 123 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 124 +%expect 123 /* Comments for TOKENS. @@ -5695,7 +5695,19 @@ create_select: { Select->parsing_place= NO_MATTER; } - table_expression + /* + TODO: + The following sequence repeats a few times: + opt_table_expression + opt_order_clause + opt_limit_clause + opt_select_lock_type + Perhaps they can be grouped into a dedicated rule. + */ + opt_table_expression + opt_order_clause + opt_limit_clause + opt_select_lock_type { /* The following work only with the local list, the global list @@ -8535,7 +8547,10 @@ select_paren_derived: Lex->current_select->set_braces(true); } SELECT_SYM select_part2_derived - table_expression + opt_table_expression + opt_order_clause + opt_limit_clause + opt_select_lock_type { if (setup_select_in_parentheses(Lex)) MYSQL_YYABORT; @@ -8566,24 +8581,20 @@ select_part2: | select_options_and_item_list into opt_select_lock_type | select_options_and_item_list opt_into - from_clause - opt_where_clause - opt_group_clause - opt_having_clause - opt_window_clause + table_expression opt_order_clause opt_limit_clause opt_procedure_clause opt_into opt_select_lock_type { - if ($2 && $11) + if ($2 && $7) { /* double "INTO" clause */ my_error(ER_WRONG_USAGE, MYF(0), "INTO", "INTO"); MYSQL_YYABORT; } - if ($10 && ($2 || $11)) + if ($6 && ($2 || $7)) { /* "INTO" with "PROCEDURE ANALYSE" */ my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "INTO"); @@ -8606,27 +8617,27 @@ select_options_and_item_list: } ; + +/** + , as in the SQL standard. +*/ table_expression: - opt_from_clause + from_clause opt_where_clause opt_group_clause opt_having_clause opt_window_clause - opt_order_clause - opt_limit_clause - opt_procedure_clause - opt_select_lock_type + ; + +opt_table_expression: + /* Empty */ + | table_expression ; from_clause: FROM table_reference_list ; -opt_from_clause: - /* empty */ - | from_clause - ; - table_reference_list: join_table_list { @@ -11208,7 +11219,10 @@ select_derived2: { Select->parsing_place= NO_MATTER; } - table_expression + opt_table_expression + opt_order_clause + opt_limit_clause + opt_select_lock_type ; get_select_lex: @@ -11946,9 +11960,11 @@ opt_procedure_clause: if (&lex->select_lex != lex->current_select) { + // SELECT * FROM t1 UNION SELECT * FROM t2 PROCEDURE ANALYSE(); my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery"); MYSQL_YYABORT; } + lex->proc_list.elements=0; lex->proc_list.first=0; lex->proc_list.next= &lex->proc_list.first; @@ -16359,7 +16375,10 @@ union_option: query_specification: SELECT_SYM select_init2_derived - table_expression + opt_table_expression + opt_order_clause + opt_limit_clause + opt_select_lock_type { $$= Lex->current_select->master_unit()->first_select(); }