summaryrefslogtreecommitdiff
path: root/sql/sql_yacc.yy
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_yacc.yy')
-rw-r--r--sql/sql_yacc.yy437
1 files changed, 293 insertions, 144 deletions
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 164dbe8bb88..5ba8e070246 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -829,10 +829,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%parse-param { THD *thd }
%lex-param { THD *thd }
/*
- Currently there are 46 shift/reduce conflicts.
+ Currently there are 37 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 46
+%expect 37
/*
Comments for TOKENS.
@@ -1644,6 +1644,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%left MYSQL_CONCAT_SYM
%left NEG '~' NOT2_SYM BINARY
%left COLLATE_SYM
+%left SUBQUERY_AS_EXPR
/*
Tokens that can change their meaning from identifier to something else
@@ -1734,7 +1735,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
ALTER TABLE t1 ADD SYSTEM VERSIONING;
*/
%left PREC_BELOW_CONTRACTION_TOKEN2
-%left TEXT_STRING '(' VALUE_SYM VERSIONING_SYM
+%left TEXT_STRING '(' ')' VALUE_SYM VERSIONING_SYM
+%left EMPTY_FROM_CLAUSE
+%right INTO
%type <lex_str>
DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
@@ -1997,16 +2000,18 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
query_specification
table_value_constructor
simple_table
+ query_simple
query_primary
- query_primary_parens
+ subquery
select_into_query_specification
-
%type <select_lex_unit>
- query_specification_start
- query_expression_body
query_expression
- query_expression_unit
+ query_expression_no_with_clause
+ query_expression_body_ext
+ query_expression_body_ext_parens
+ query_expression_body
+ query_specification_start
%type <boolfunc2creator> comp_op
@@ -2031,7 +2036,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <order_limit_lock>
query_expression_tail
+ opt_query_expression_tail
order_or_limit
+ order_limit_lock
opt_order_limit_lock
%type <select_order> opt_order_clause order_clause order_list
@@ -2184,7 +2191,7 @@ END_OF_INPUT
THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM
-%type <with_clause> opt_with_clause with_clause
+%type <with_clause> with_clause
%type <lex_str_ptr> query_name
@@ -5272,7 +5279,7 @@ create_select_query_expression:
if (Lex->parsed_insert_select($1->first_select()))
MYSQL_YYABORT;
}
- | LEFT_PAREN_WITH with_clause query_expression_body ')'
+ | LEFT_PAREN_WITH with_clause query_expression_no_with_clause ')'
{
SELECT_LEX *first_select= $3->first_select();
$3->set_with_clause($2);
@@ -6785,12 +6792,7 @@ parse_vcol_expr:
;
parenthesized_expr:
- query_expression
- {
- if (!($$= Lex->create_item_query_expression(thd, $1)))
- MYSQL_YYABORT;
- }
- | expr
+ expr
| expr ',' expr_list
{
$3->push_front($1, thd->mem_root);
@@ -6809,6 +6811,16 @@ virtual_column_func:
MYSQL_YYABORT;
$$= v;
}
+ | subquery
+ {
+ Item *item;
+ if (!(item= new (thd->mem_root) Item_singlerow_subselect(thd, $1)))
+ MYSQL_YYABORT;
+ Virtual_column_info *v= add_virtual_expression(thd, item);
+ if (unlikely(!v))
+ MYSQL_YYABORT;
+ $$= v;
+ }
;
expr_or_literal: column_default_non_parenthesized_expr | signed_literal ;
@@ -9175,8 +9187,9 @@ opt_ignore_leaves:
Select : retrieve data from table
*/
+
select:
- query_expression_body
+ query_expression_no_with_clause
{
if (Lex->push_select($1->fake_select_lex ?
$1->fake_select_lex :
@@ -9186,10 +9199,11 @@ select:
opt_procedure_or_into
{
Lex->pop_select();
+ $1->set_with_clause(NULL);
if (Lex->select_finalize($1, $3))
MYSQL_YYABORT;
}
- | with_clause query_expression_body
+ | with_clause query_expression_no_with_clause
{
if (Lex->push_select($2->fake_select_lex ?
$2->fake_select_lex :
@@ -9206,7 +9220,6 @@ select:
}
;
-
select_into:
select_into_query_specification
{
@@ -9215,14 +9228,15 @@ select_into:
}
opt_order_limit_lock
{
- st_select_lex_unit *unit;
- if (!(unit= Lex->parsed_body_select($1, $3)))
+ SELECT_LEX_UNIT *unit;
+ if (!(unit = Lex->create_unit($1)))
MYSQL_YYABORT;
+ if ($3)
+ unit= Lex->add_tail_to_query_expression_body(unit, $3);
if (Lex->select_finalize(unit))
MYSQL_YYABORT;
- }
- ;
-
+ }
+ ;
simple_table:
query_specification { $$= $1; }
@@ -9288,92 +9302,191 @@ select_into_query_specification:
}
;
-opt_from_clause:
- /* Empty */
- | from_clause
+/**
+
+ The following grammar for query expressions conformant to
+ the latest SQL Standard is supported:
+
+ <query expression> ::=
+ [ <with clause> ] <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+
+ <with clause> ::=
+ WITH [ RECURSIVE ] <with_list
+
+ <with list> ::=
+ <with list element> [ { <comma> <with list element> }... ]
+
+ <with list element> ::=
+ <query name> [ '(' <with column list> ')' ]
+ AS <table subquery>
+
+ <with column list> ::=
+ <column name list>
+
+ <query expression body> ::
+ <query term>
+ | <query expression body> UNION [ ALL | DISTINCT ] <query term>
+ | <query expression body> EXCEPT [ DISTINCT ] <query term>
+
+ <query term> ::=
+ <query primary>
+ | <query term> INTERSECT [ DISTINCT ] <query primary>
+
+ <query primary> ::=
+ <simple table>
+ | '(' <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ ')'
+
+ <simple table>
+ <query specification>
+ | <table value constructor>
+
+ <subquery>
+ '(' <query_expression> ')'
+
+*/
+
+/*
+ query_expression produces the same expressions as
+ <query expression>
+*/
+
+query_expression:
+ query_expression_no_with_clause
+ {
+ $1->set_with_clause(NULL);
+ $$= $1;
+ }
+ | with_clause
+ query_expression_no_with_clause
+ {
+ $2->set_with_clause($1);
+ $1->attach_to($2->first_select());
+ $$= $2;
+ }
;
+/*
+ query_expression_no_with_clause produces the same expressions as
+ <query expression> without [ <with clause> ]
+*/
-query_primary:
- simple_table
- { $$= $1; }
- | query_primary_parens
- { $$= $1; }
+query_expression_no_with_clause:
+ query_expression_body_ext { $$= $1; }
+ | query_expression_body_ext_parens { $$= $1; }
;
-query_primary_parens:
- '(' query_expression_unit
+/*
+ query_expression_body_ext produces the same expressions as
+ <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ | '('... <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ ')'...
+ Note: number of ')' must be equal to the number of '(' in the rule above
+*/
+
+query_expression_body_ext:
+ query_expression_body
{
- if (Lex->parsed_unit_in_brackets($2))
- MYSQL_YYABORT;
+ if ($1->first_select()->next_select())
+ {
+ if (Lex->parsed_multi_operand_query_expression_body($1))
+ MYSQL_YYABORT;
+ }
}
- query_expression_tail ')'
+ opt_query_expression_tail
{
- $$= Lex->parsed_unit_in_brackets_tail($2, $4);
+ if (!$3)
+ $$= $1;
+ else
+ $$= Lex->add_tail_to_query_expression_body($1, $3);
}
- | '(' query_primary
+ | query_expression_body_ext_parens
{
- Lex->push_select($2);
+ Lex->push_select(!$1->first_select()->next_select() ?
+ $1->first_select() : $1->fake_select_lex);
}
- query_expression_tail ')'
+ query_expression_tail
{
- if (!($$= Lex->parsed_select_in_brackets($2, $4)))
- YYABORT;
+ if (!($$= Lex->add_tail_to_query_expression_body_ext_parens($1, $3)))
+ MYSQL_YYABORT;
}
;
-query_expression_unit:
- query_primary
- unit_type_decl
- query_primary
- {
- if (!($$= Lex->parsed_select_expr_start($1, $3, $2.unit_type,
- $2.distinct)))
- YYABORT;
- }
- | query_expression_unit
- unit_type_decl
- query_primary
+query_expression_body_ext_parens:
+ '(' query_expression_body_ext_parens ')'
+ { $$= $2; }
+ | '(' query_expression_body_ext ')'
{
- if (!($$= Lex->parsed_select_expr_cont($1, $3, $2.unit_type,
- $2.distinct, FALSE)))
- YYABORT;
+ SELECT_LEX *sel= $2->first_select()->next_select() ?
+ $2->fake_select_lex : $2->first_select();
+ sel->braces= true;
+ $$= $2;
}
;
+/*
+ query_expression_body produces the same expressions as
+ <query expression body>
+*/
+
query_expression_body:
- query_primary
+ query_simple
{
Lex->push_select($1);
+ if (!($$= Lex->create_unit($1)))
+ MYSQL_YYABORT;
}
- query_expression_tail
+ | query_expression_body
+ unit_type_decl
{
- if (!($$= Lex->parsed_body_select($1, $3)))
- MYSQL_YYABORT;
+ if (!$1->first_select()->next_select())
+ {
+ Lex->pop_select();
+ }
}
- | query_expression_unit
+ query_primary
{
- if (Lex->parsed_body_unit($1))
+ if (!($$= Lex->add_primary_to_query_expression_body($1, $4,
+ $2.unit_type,
+ $2.distinct,
+ FALSE)))
MYSQL_YYABORT;
}
- query_expression_tail
+ | query_expression_body_ext_parens
+ unit_type_decl
+ query_primary
{
- if (!($$= Lex->parsed_body_unit_tail($1, $3)))
+ if (!($$= Lex->add_primary_to_query_expression_body_ext_parens(
+ $1, $3,
+ $2.unit_type,
+ $2.distinct)))
MYSQL_YYABORT;
}
;
-query_expression:
- opt_with_clause
- query_expression_body
- {
- if ($1)
- {
- $2->set_with_clause($1);
- $1->attach_to($2->first_select());
- }
- $$= $2;
- }
+/*
+ query_primary produces the same expressions as
+ <query primary>
+*/
+
+query_primary:
+ query_simple
+ { $$= $1; }
+ | query_expression_body_ext_parens
+ { $$= $1->first_select(); }
+ ;
+
+/*
+ query_simple produces the same expressions as
+ <simple table>
+*/
+
+query_simple:
+ simple_table { $$= $1;}
;
subselect:
@@ -9384,11 +9497,63 @@ subselect:
}
;
-
-/**
- <table expression>, as in the SQL standard.
+/*
+ subquery produces the same expressions as
+ <subquery>
+
+ Consider the production rule of the SQL Standard
+ subquery:
+ '(' query_expression')'
+
+ This rule is equivalent to the rule
+ subquery:
+ '(' query_expression_no_with_clause ')'
+ | '(' with_clause query_expression_no_with_clause ')'
+ that in its turn is equivalent to
+ subquery:
+ '(' query_expression_body_ext ')'
+ | query_expression_body_ext_parens
+ | '(' with_clause query_expression_no_with_clause ')'
+
+ The latter can be re-written into
+ subquery:
+ query_expression_body_ext_parens ')'
+ | '(' with_clause query_expression_no_with_clause ')'
+
+ The last rule allows us to resolve properly the shift/reduce conflict
+ when subquery is used in expressions such as in the following queries
+ select (select * from t1 limit 1) + t2.a from t2
+ select * from t1 where t1.a [not] in (select t2.a from t2)
+
+ In the rule below %prec SUBQUERY_AS_EXPR forces the parser to perform a shift
+ operation rather then a reduce operation when ')' is encountered and can be
+ considered as the last symbol a query expression.
*/
+subquery:
+ query_expression_body_ext_parens %prec SUBQUERY_AS_EXPR
+ {
+ if (!$1->fake_select_lex)
+ $1->first_select()->braces= false;
+ else
+ $1->fake_select_lex->braces= false;
+ if (!($$= Lex->parsed_subselect($1)))
+ YYABORT;
+ }
+ | '(' with_clause query_expression_no_with_clause ')'
+ {
+ $3->set_with_clause($2);
+ $2->attach_to($3->first_select());
+ if (!($$= Lex->parsed_subselect($3)))
+ YYABORT;
+ }
+ ;
+
+opt_from_clause:
+ /* empty */ %prec EMPTY_FROM_CLAUSE
+ | from_clause
+ ;
+
from_clause:
FROM table_reference_list
;
@@ -9552,6 +9717,7 @@ select_lock_type:
}
;
+
opt_select_lock_type:
/* empty */
{
@@ -9563,6 +9729,7 @@ opt_select_lock_type:
}
;
+
opt_lock_wait_timeout_new:
/* empty */
{
@@ -9849,15 +10016,15 @@ bool_pri:
;
predicate:
- bit_expr IN_SYM '(' subselect ')'
+ bit_expr IN_SYM subquery
{
- $$= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
+ $$= new (thd->mem_root) Item_in_subselect(thd, $1, $3);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | bit_expr not IN_SYM '(' subselect ')'
+ | bit_expr not IN_SYM subquery
{
- Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5);
+ Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= negate_expression(thd, item);
@@ -10378,6 +10545,11 @@ primary_expr:
column_default_non_parenthesized_expr
| explicit_cursor_attr
| '(' parenthesized_expr ')' { $$= $2; }
+ | subquery
+ {
+ if (!($$= Lex->create_item_query_expression(thd, $1->master_unit())))
+ MYSQL_YYABORT;
+ }
;
string_factor_expr:
@@ -12129,35 +12301,12 @@ table_primary_ident:
}
;
-
-/*
- 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>
-
- <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.
-*/
-
table_primary_derived:
- query_primary_parens opt_for_system_time_clause table_alias_clause
+ subquery
+ opt_for_system_time_clause table_alias_clause
{
- if (!($$= Lex->parsed_derived_select($1, $2, $3)))
- YYABORT;
- }
- | '('
- query_expression
- ')' opt_for_system_time_clause table_alias_clause
- {
- if (!($$= Lex->parsed_derived_unit($2, $4, $5)))
- YYABORT;
+ if (!($$= Lex->parsed_derived_table($1->master_unit(), $2, $3)))
+ MYSQL_YYABORT;
}
;
@@ -12293,7 +12442,6 @@ table_alias:
opt_table_alias_clause:
/* empty */ { $$=0; }
-
| table_alias_clause { $$= $1; }
;
@@ -12427,7 +12575,7 @@ opt_window_clause:
{}
| WINDOW_SYM
window_def_list
- {}
+ {}
;
window_def_list:
@@ -12756,10 +12904,8 @@ delete_limit_clause:
| LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; }
;
-opt_order_limit_lock:
- /* empty */
- { $$= NULL; }
- | order_or_limit
+order_limit_lock:
+ order_or_limit
{
$$= $1;
$$->lock.empty();
@@ -12779,32 +12925,45 @@ opt_order_limit_lock:
$$->lock= $1;
}
;
+
+opt_order_limit_lock:
+ /* empty */
+ {
+ Lex->pop_select();
+ $$= NULL;
+ }
+ | order_limit_lock { $$= $1; }
+ ;
+
query_expression_tail:
+ order_limit_lock
+ ;
+
+opt_query_expression_tail:
opt_order_limit_lock
;
opt_procedure_or_into:
- /* empty */
- {
- $$.empty();
- }
+ /* empty */
+ {
+ $$.empty();
+ }
| procedure_clause opt_select_lock_type
- {
- $$= $2;
- }
+ {
+ $$= $2;
+ }
| into opt_select_lock_type
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_WARN_DEPRECATED_SYNTAX,
- ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
- "<select expression> INTO <destination>;",
- "'SELECT <select list> INTO <destination>"
- " FROM...'");
- $$= $2;
- }
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX,
+ ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
+ "<select expression> INTO <destination>;",
+ "'SELECT <select list> INTO <destination>"
+ " FROM...'");
+ $$= $2;
+ }
;
-
order_or_limit:
order_clause opt_limit_clause
{
@@ -15199,16 +15358,6 @@ temporal_literal:
}
;
-
-opt_with_clause:
- /*empty */ { $$= 0; }
- | with_clause
- {
- $$= $1;
- }
- ;
-
-
with_clause:
WITH opt_recursive
{