List:Commits« Previous MessageNext Message »
From:mhansson Date:February 14 2008 10:59am
Subject:bk commit into 5.1 tree (mhansson:1.2659) BUG#33204
View as plain text  
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-14 11:58:20+01:00, mhansson@riffraff.(none) +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, extracting common code 
  into helper functions.

  mysql-test/r/subselect.result@stripped, 2008-02-14 11:58:18+01:00, mhansson@riffraff.(none) +93 -0
    Bug#27219: Test case

  mysql-test/t/subselect.test@stripped, 2008-02-14 11:58:18+01:00, mhansson@riffraff.(none) +89 -0
    Bug#27219: Test result

  sql/sql_yacc.yy@stripped, 2008-02-14 11:58:18+01:00, mhansson@riffraff.(none) +153 -52
    Bug#33204
    
    The fix appears to introduce 4 new shift/reduce conflicts. These 
    are existing conflicts being included by yacc from the rules 
    select_options(3) and opt_limit_clause(1), respectively.

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-14 11:58:18 +01:00
@@ -4369,3 +4369,96 @@ SELECT * FROM t1 WHERE _utf8'a' = ANY (S
 s1
 a
 DROP TABLE t1;
+CREATE TABLE t1( a INT );
+INSERT INTO t1 VALUES (1),(2);
+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 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 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
+DROP TABLE t1;
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-14 11:58:18 +01:00
@@ -3249,3 +3249,92 @@ 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);
+
+--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_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;
+
+--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;
+
+DROP TABLE t1;
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-14 11:58:18 +01:00
@@ -457,6 +457,78 @@ Item* handle_sql2003_note184_exception(T
   DBUG_RETURN(result);
 }
 
+/**
+   @brief Creates a new SELECT_LEX for a UNION branch.
+
+   @lex The parser state.
+
+   @is_union_distinct True if the union preceding the new select statement
+   uses UNION DISTINCT.
+
+   @return false if successful, true 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.
+
+   @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 +583,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 181
 
 /*
    Comments for TOKENS.
@@ -1195,6 +1267,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
         join_table_list  join_table
         table_factor table_ref
         select_derived derived_table_list
+        query_expression_body
 
 %type <date_time_type> date_time_type;
 %type <interval> interval
@@ -6136,37 +6209,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));
-              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));
+            if (setup_select_in_parentheses(Lex))
               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
           {
@@ -7661,12 +7719,11 @@ 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
+        | '(' get_select_lex query_expression_body ')' 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 +7742,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 +7761,68 @@ table_factor:
           }
         ;
 
+query_expression_body:
+          select_derived opt_order_clause opt_limit_clause
+          | query_expression_body
+            UNION_SYM
+            union_option
+            {
+              if(add_select_to_union_list(Lex, (bool)$3))
+                MYSQL_YYABORT;
+            }
+            select_init_derived
+            {
+              /*
+                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_init for nested queries. */
+select_init_derived:
+          SELECT_SYM select_init2_derived
+        | '(' select_paren_derived ')' 
+        ;
+
+/* 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 +11704,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");
-              MYSQL_YYABORT;
-            }
-            if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
-            {
-              my_parse_error(ER(ER_SYNTAX_ERROR));
+            if(add_select_to_union_list(Lex, (bool)$2))
               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
           {
Thread
bk commit into 5.1 tree (mhansson:1.2659) BUG#33204mhansson14 Feb