MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Marc Alff Date:March 4 2008 3:19am
Subject:Re: bk commit into 5.1 tree (mhansson:1.2659) BUG#33204
View as plain text  
Hi Martin

See some comments inline below.

The patch is ok to push with the following changes:

1) Please add the test cases from Bug#30105 (SELECT [...] INTO subquery
Invalid return.),
since it's actually fixed.

2) The test cases covering a invalid SELECT ... INTO in a sub select
always use the
"INTO @var" syntax.

Please also add tests covering syntax errors when using:
- SELECT ... INTO OUTFILE
- SELECT ... INTO DUMPFILE
inside a sub select, since these should be also prevented now.

The fix itself is not trivial, but goes into the right direction by
separating the concepts
of a select statement versus a query expression, which is the root issue
causing some trouble in this area:
- see related bug#35020
- see related bug#35021

This patch will make fixing 35020 and 35021 much easier,
and should help maintenance in general.

To untangle this area of the grammar is not a trivial task,
thanks a lot for finding this fix.

Best regards,
Marc

mhansson@stripped wrote:
> 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-26 12:52:12+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.
>
>   mysql-test/r/subselect.result@stripped, 2008-02-26 12:52:10+01:00,
> mhansson@riffraff.(none) +219 -6
>     Bug#33204: Test result.
>
>   mysql-test/t/subselect.test@stripped, 2008-02-26 12:52:10+01:00,
> mhansson@riffraff.(none) +170 -6
>     Bug#33204: Test case.
>
>   sql/sql_yacc.yy@stripped, 2008-02-26 12:52:10+01:00, mhansson@riffraff.(none) +217
> -71
>     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>),
>   
Verified that there are no conflicts involving SELECT_SYM any more.

>     - 1 conflict appears in 3 new places ('(' token in <simple_expr>),
>   
Verified. These conflicts are another manifestation of an existing bug
(See Bug#25734),
which affects '(' expr ')' and '(' subselect ')' in general, in
different contexts.
After analysis, it safe to conclude that no regression is introduced here,
even with a growing number of S/R conflicts in the grammar.

>     - 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
>   
Verified. These conflicts are due to the fact that SQL_BUFFER_RESULT,
SQL_CACHE_SYM and SQL_NO_CACHE_SYM
are *not* reserved keywords.

I think however that there is more to it:

This grammar changes do separate:
- what is a SELECT *statement*, which can have an "INTO" clause
- what is a SELECT *query*, and can be nested as an expression, which
can not have an "INTO" clause.

I think that the global options like:
- HIGH_PRIORITY
- SQL_CALC_FOUND_ROWS (?)
- SQL_NO_CACHE_SYM
- SQL_CACHE_SYM
should probably appear only in select statements,
and should not be available as options in a sub select.

On the other hand, other options like "ALL", "DISTINCT", etc,
do make sense in a sub select, so there is a need to split the current
select_options rules
to clarify which options are available in which context.

This is an existing defect, similar but not identical to the present
issue affecting "INTO",
so I suggest to fix it separately (no change required for this patch).


>     - 1 conflict appears in 1 new place (LIMIT token, since the rule producing it,
>       select_part2 has been copied).
>   
Verified.

>        
>     When the original conflicts are resolved, these conflicts will disappear as
> well.
>
>   
Agreed.

The "%expect NNN" rule in sql_yacc.yy has been added to detect at first
glance potential issues introduced by a patch.
In this case, I agree with the analysis, and think it's safe to push
grammar change even with "more" conflicts,
since these are additional manifestations of existing conflicts only,
not really new issues.


> 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-26 12:52:10 +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
>   
Very good, resolving long standing issues in the grammar.

>  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
>   
Same

>  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
>   

Same
>  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,173 @@ 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;
>   
Ok

> +
> +--error ER_PARSE_ERROR
> +SELECT * FROM ( 
> +  SELECT 1 a 
> +  UNION 
> +  SELECT a INTO @var FROM t1 WHERE a = 2 
> +) t1a;
>   

Ok
> +
> +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);
>   
Ok, existing limitation, due to bug#25734


> +SELECT * FROM (SELECT 1 a UNION (SELECT 1 a)) alias;
> +SELECT * FROM (SELECT 1 UNION SELECT 1) t1a;
>   
Ok
> +--error ER_PARSE_ERROR
> +SELECT * FROM ((SELECT 1 a INTO @a)) t1a;
>   
Ok

> +
> +--error ER_PARSE_ERROR
> +SELECT * FROM (SELECT 1 a UNION (SELECT 1 a INTO @a)) t1a;
>   
Ok

> +
> +--error ER_PARSE_ERROR
> +SELECT * FROM (SELECT 1 a UNION ((SELECT 1 a INTO @a))) t1a;
>   

Ok
> +
> +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;
>   

Ok
> +
> +# 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.
> +# 
> +SELECT * FROM t1 JOIN  (SELECT 1 UNION SELECT 1) alias 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));
> +
> +SELECT * FROM t1 JOIN  (SELECT 1 t1a) alias ON 1;
> +SELECT * FROM t1 JOIN ((SELECT 1 t1a)) alias 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);
>   
Ok

> +
> +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 INTO @a);
>   
Ok

> +--error ER_PARSE_ERROR
> +SELECT * FROM t1 WHERE a = ( SELECT 1 UNION SELECT 1 INTO @a);
> +
>   
Ok
> +--error ER_PARSE_ERROR
> +SELECT ( SELECT 1 INTO @v );
>   
Ok

> +--error ER_PARSE_ERROR
> +SELECT ( SELECT 1 UNION SELECT 1 INTO @v );
>   

Ok
> +
> +# 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 );
> +
> +--error ER_PARSE_ERROR
> +SELECT * FROM t1 WHERE EXISTS ( SELECT 1 UNION SELECT 1 INTO @v );
> +--error ER_PARSE_ERROR
> +SELECT * FROM t1 WHERE a IN ( SELECT 1 UNION SELECT 1 INTO @v );
> +
> +--error ER_PARSE_ERROR
> +SELECT * FROM t1 WHERE EXISTS ( SELECT 1 INTO @v );
> +--error ER_PARSE_ERROR
> +SELECT * FROM t1 WHERE a IN ( SELECT 1 INTO @v );
> +
> +
> +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-26 12:52:10 +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;
> +}
>   
New explicit helper (moved existing code), good.

> +
> +/**
> +   @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;
> +}
>   
New explicit helper (moved existing code), good.

> +
>  %}
>  %union {
>    int  num;
> @@ -508,10 +585,10 @@ bool my_yyoverflow(short **a, YYSTYPE **
>  
>  %pure_parser                                    /* We have threads */
>  /*
> -  Currently there are 177 shift/reduce conflicts.
> +  Currently there are 183 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.
> +          */
>   
Thanks for the comments and pointers.

> +        | '(' 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
>   
See previous comment:
using select_options here makes all options available in a sub select.

Existing bug, no change required: this will be fixed with bug#35020

> +          {
> +            Select->parsing_place= NO_MATTER;
> +          }
> +          opt_select_from select_lock_type
>   
And using select_lock_type here makes "FOR UPDATE" / "LOCK IN SHARED MODE"
available in sub selects also.

Existing bug, no change required: this will be fixed with bug#35021

> +        ;
> +
>  /* 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();
> -        };
>   
Removed "take_first_select", good.

> +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:2003 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#33204mhansson26 Feb
  • Re: bk commit into 5.1 tree (mhansson:1.2659) BUG#33204Marc Alff4 Mar