mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
Bug#14654 : Cannot select from the same table twice within a UNION statement
Made the parser to support parenthesis around UNION branches. This is done by amending the rules of the parser so it generates the correct structure. Currently it supports arbitrary subquery/join/parenthesis operations in the EXISTS clause. In the IN/scalar subquery case it will allow adding nested parenthesis only if there is an UNION clause after the parenthesis. Otherwise it will just treat the multiple nested parenthesis as a scalar expression. It adds extra lex level for ((SELECT ...) UNION ...) to accommodate for the UNION clause.
This commit is contained in:
@@ -3346,3 +3346,28 @@ ORDER BY t1.t DESC LIMIT 1);
|
|||||||
i1 i2 t i1 i2 t
|
i1 i2 t i1 i2 t
|
||||||
24 1 2005-05-27 12:40:30 24 1 2006-06-20 12:29:40
|
24 1 2005-05-27 12:40:30 24 1 2006-06-20 12:29:40
|
||||||
DROP TABLE t1, t2;
|
DROP TABLE t1, t2;
|
||||||
|
CREATE TABLE t1 (i INT);
|
||||||
|
(SELECT i FROM t1) UNION (SELECT i FROM t1);
|
||||||
|
i
|
||||||
|
SELECT sql_no_cache * FROM t1 WHERE NOT EXISTS
|
||||||
|
(
|
||||||
|
(SELECT i FROM t1) UNION
|
||||||
|
(SELECT i FROM t1)
|
||||||
|
);
|
||||||
|
i
|
||||||
|
SELECT * FROM t1
|
||||||
|
WHERE NOT EXISTS (((SELECT i FROM t1) UNION (SELECT i FROM t1)));
|
||||||
|
i
|
||||||
|
explain select ((select t11.i from t1 t11) union (select t12.i from t1 t12))
|
||||||
|
from 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 t12.i from t1 t12))
|
||||||
|
from t1' at line 1
|
||||||
|
explain select * from t1 where not exists
|
||||||
|
((select t11.i from t1 t11) union (select t12.i from t1 t12));
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
|
||||||
|
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used
|
||||||
|
3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table
|
||||||
|
4 UNION t12 system NULL NULL NULL NULL 0 const row not found
|
||||||
|
NULL UNION RESULT <union2,4> ALL NULL NULL NULL NULL NULL
|
||||||
|
DROP TABLE t1;
|
||||||
|
@@ -2257,3 +2257,29 @@ SELECT * FROM t1,t2
|
|||||||
ORDER BY t1.t DESC LIMIT 1);
|
ORDER BY t1.t DESC LIMIT 1);
|
||||||
|
|
||||||
DROP TABLE t1, t2;
|
DROP TABLE t1, t2;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug#14654 : Cannot select from the same table twice within a UNION
|
||||||
|
# statement
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (i INT);
|
||||||
|
|
||||||
|
(SELECT i FROM t1) UNION (SELECT i FROM t1);
|
||||||
|
SELECT sql_no_cache * FROM t1 WHERE NOT EXISTS
|
||||||
|
(
|
||||||
|
(SELECT i FROM t1) UNION
|
||||||
|
(SELECT i FROM t1)
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT * FROM t1
|
||||||
|
WHERE NOT EXISTS (((SELECT i FROM t1) UNION (SELECT i FROM t1)));
|
||||||
|
|
||||||
|
#TODO:not supported
|
||||||
|
--error 1064
|
||||||
|
explain select ((select t11.i from t1 t11) union (select t12.i from t1 t12))
|
||||||
|
from t1;
|
||||||
|
#supported
|
||||||
|
explain select * from t1 where not exists
|
||||||
|
((select t11.i from t1 t11) union (select t12.i from t1 t12));
|
||||||
|
|
||||||
|
DROP TABLE t1;
|
||||||
|
146
sql/sql_yacc.yy
146
sql/sql_yacc.yy
@@ -725,8 +725,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||||||
predicate bit_expr bit_term bit_factor value_expr term factor
|
predicate bit_expr bit_term bit_factor value_expr term factor
|
||||||
table_wild simple_expr udf_expr
|
table_wild simple_expr udf_expr
|
||||||
expr_or_default set_expr_or_default interval_expr
|
expr_or_default set_expr_or_default interval_expr
|
||||||
param_marker singlerow_subselect singlerow_subselect_init
|
param_marker geometry_function
|
||||||
exists_subselect exists_subselect_init geometry_function
|
|
||||||
signed_literal now_or_signed_literal opt_escape
|
signed_literal now_or_signed_literal opt_escape
|
||||||
sp_opt_default
|
sp_opt_default
|
||||||
simple_ident_nospvar simple_ident_q
|
simple_ident_nospvar simple_ident_q
|
||||||
@@ -791,7 +790,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||||||
|
|
||||||
%type <variable> internal_variable_name
|
%type <variable> internal_variable_name
|
||||||
|
|
||||||
%type <select_lex> in_subselect in_subselect_init
|
%type <select_lex> subselect subselect_init
|
||||||
get_select_lex
|
get_select_lex
|
||||||
|
|
||||||
%type <boolfunc2creator> comp_op
|
%type <boolfunc2creator> comp_op
|
||||||
@@ -3914,12 +3913,14 @@ select_paren:
|
|||||||
yyerror(ER(ER_SYNTAX_ERROR));
|
yyerror(ER(ER_SYNTAX_ERROR));
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
if (sel->linkage == UNION_TYPE &&
|
if (sel->linkage == UNION_TYPE &&
|
||||||
!sel->master_unit()->first_select()->braces)
|
!sel->master_unit()->first_select()->braces &&
|
||||||
{
|
sel->master_unit()->first_select()->linkage ==
|
||||||
yyerror(ER(ER_SYNTAX_ERROR));
|
UNION_TYPE)
|
||||||
YYABORT;
|
{
|
||||||
}
|
yyerror(ER(ER_SYNTAX_ERROR));
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
/* select in braces, can't contain global parameters */
|
/* select in braces, can't contain global parameters */
|
||||||
if (sel->master_unit()->fake_select_lex)
|
if (sel->master_unit()->fake_select_lex)
|
||||||
sel->master_unit()->global_parameters=
|
sel->master_unit()->global_parameters=
|
||||||
@@ -4177,37 +4178,37 @@ bool_pri:
|
|||||||
| bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); }
|
| bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); }
|
||||||
| bool_pri comp_op predicate %prec EQ
|
| bool_pri comp_op predicate %prec EQ
|
||||||
{ $$= (*$2)(0)->create($1,$3); }
|
{ $$= (*$2)(0)->create($1,$3); }
|
||||||
| bool_pri comp_op all_or_any in_subselect %prec EQ
|
| bool_pri comp_op all_or_any '(' subselect ')' %prec EQ
|
||||||
{ $$= all_any_subquery_creator($1, $2, $3, $4); }
|
{ $$= all_any_subquery_creator($1, $2, $3, $5); }
|
||||||
| predicate ;
|
| predicate ;
|
||||||
|
|
||||||
predicate:
|
predicate:
|
||||||
bit_expr IN_SYM '(' expr_list ')'
|
bit_expr IN_SYM '(' subselect ')'
|
||||||
{
|
{ $$= new Item_in_subselect($1, $4); }
|
||||||
if ($4->elements == 1)
|
| bit_expr not IN_SYM '(' subselect ')'
|
||||||
$$= new Item_func_eq($1, $4->head());
|
{ $$= negate_expression(YYTHD, new Item_in_subselect($1, $5)); }
|
||||||
else
|
| bit_expr IN_SYM '(' expr ')'
|
||||||
{
|
|
||||||
$4->push_front($1);
|
|
||||||
$$= new Item_func_in(*$4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
| bit_expr not IN_SYM '(' expr_list ')'
|
|
||||||
{
|
{
|
||||||
if ($5->elements == 1)
|
$$= new Item_func_eq($1, $4);
|
||||||
$$= new Item_func_ne($1, $5->head());
|
}
|
||||||
else
|
| bit_expr IN_SYM '(' expr ',' expr_list ')'
|
||||||
{
|
{
|
||||||
$5->push_front($1);
|
$6->push_front($4);
|
||||||
Item_func_in *item = new Item_func_in(*$5);
|
$6->push_front($1);
|
||||||
|
$$= new Item_func_in(*$6);
|
||||||
|
}
|
||||||
|
| bit_expr not IN_SYM '(' expr ')'
|
||||||
|
{
|
||||||
|
$$= new Item_func_ne($1, $5);
|
||||||
|
}
|
||||||
|
| bit_expr not IN_SYM '(' expr ',' expr_list ')'
|
||||||
|
{
|
||||||
|
$7->push_front($5);
|
||||||
|
$7->push_front($1);
|
||||||
|
Item_func_in *item = new Item_func_in(*$7);
|
||||||
item->negate();
|
item->negate();
|
||||||
$$= item;
|
$$= item;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
| bit_expr IN_SYM in_subselect
|
|
||||||
{ $$= new Item_in_subselect($1, $3); }
|
|
||||||
| bit_expr not IN_SYM in_subselect
|
|
||||||
{ $$= negate_expression(YYTHD, new Item_in_subselect($1, $4)); }
|
|
||||||
| bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
|
| bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
|
||||||
{ $$= new Item_func_between($1,$3,$5); }
|
{ $$= new Item_func_between($1,$3,$5); }
|
||||||
| bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
|
| bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
|
||||||
@@ -4329,6 +4330,10 @@ simple_expr:
|
|||||||
| '-' simple_expr %prec NEG { $$= new Item_func_neg($2); }
|
| '-' simple_expr %prec NEG { $$= new Item_func_neg($2); }
|
||||||
| '~' simple_expr %prec NEG { $$= new Item_func_bit_neg($2); }
|
| '~' simple_expr %prec NEG { $$= new Item_func_bit_neg($2); }
|
||||||
| not2 simple_expr %prec NEG { $$= negate_expression(YYTHD, $2); }
|
| not2 simple_expr %prec NEG { $$= negate_expression(YYTHD, $2); }
|
||||||
|
| '(' subselect ')'
|
||||||
|
{
|
||||||
|
$$= new Item_singlerow_subselect($2);
|
||||||
|
}
|
||||||
| '(' expr ')' { $$= $2; }
|
| '(' expr ')' { $$= $2; }
|
||||||
| '(' expr ',' expr_list ')'
|
| '(' expr ',' expr_list ')'
|
||||||
{
|
{
|
||||||
@@ -4340,8 +4345,10 @@ simple_expr:
|
|||||||
$5->push_front($3);
|
$5->push_front($3);
|
||||||
$$= new Item_row(*$5);
|
$$= new Item_row(*$5);
|
||||||
}
|
}
|
||||||
| EXISTS exists_subselect { $$= $2; }
|
| EXISTS '(' subselect ')'
|
||||||
| singlerow_subselect { $$= $1; }
|
{
|
||||||
|
$$= new Item_exists_subselect($3);
|
||||||
|
}
|
||||||
| '{' ident expr '}' { $$= $3; }
|
| '{' ident expr '}' { $$= $3; }
|
||||||
| MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
|
| MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
|
||||||
{ $2->push_front($5);
|
{ $2->push_front($5);
|
||||||
@@ -8863,49 +8870,38 @@ union_option:
|
|||||||
| ALL { $$=0; }
|
| ALL { $$=0; }
|
||||||
;
|
;
|
||||||
|
|
||||||
singlerow_subselect:
|
subselect:
|
||||||
subselect_start singlerow_subselect_init
|
SELECT_SYM subselect_start subselect_init subselect_end
|
||||||
subselect_end
|
{
|
||||||
{
|
$$= $3;
|
||||||
$$= $2;
|
}
|
||||||
};
|
| '(' subselect_start subselect ')'
|
||||||
|
{
|
||||||
|
LEX *lex= Lex;
|
||||||
|
THD *thd= YYTHD;
|
||||||
|
/*
|
||||||
|
note that a local variable can't be used for
|
||||||
|
$3 as it's used in local variable construction
|
||||||
|
and some compilers can't guarnatee the order
|
||||||
|
in which the local variables are initialized.
|
||||||
|
*/
|
||||||
|
List_iterator<Item> it($3->item_list);
|
||||||
|
Item *item;
|
||||||
|
/*
|
||||||
|
we must fill the items list for the "derived table".
|
||||||
|
*/
|
||||||
|
while ((item= it++))
|
||||||
|
add_item_to_list(thd, item);
|
||||||
|
}
|
||||||
|
union_clause subselect_end { $$= $3; };
|
||||||
|
|
||||||
singlerow_subselect_init:
|
subselect_init:
|
||||||
select_init2
|
|
||||||
{
|
|
||||||
$$= new Item_singlerow_subselect(Lex->current_select->
|
|
||||||
master_unit()->first_select());
|
|
||||||
};
|
|
||||||
|
|
||||||
exists_subselect:
|
|
||||||
subselect_start exists_subselect_init
|
|
||||||
subselect_end
|
|
||||||
{
|
|
||||||
$$= $2;
|
|
||||||
};
|
|
||||||
|
|
||||||
exists_subselect_init:
|
|
||||||
select_init2
|
|
||||||
{
|
|
||||||
$$= new Item_exists_subselect(Lex->current_select->master_unit()->
|
|
||||||
first_select());
|
|
||||||
};
|
|
||||||
|
|
||||||
in_subselect:
|
|
||||||
subselect_start in_subselect_init
|
|
||||||
subselect_end
|
|
||||||
{
|
|
||||||
$$= $2;
|
|
||||||
};
|
|
||||||
|
|
||||||
in_subselect_init:
|
|
||||||
select_init2
|
select_init2
|
||||||
{
|
{
|
||||||
$$= Lex->current_select->master_unit()->first_select();
|
$$= Lex->current_select->master_unit()->first_select();
|
||||||
};
|
};
|
||||||
|
|
||||||
subselect_start:
|
subselect_start:
|
||||||
'(' SELECT_SYM
|
|
||||||
{
|
{
|
||||||
LEX *lex=Lex;
|
LEX *lex=Lex;
|
||||||
if (lex->sql_command == (int)SQLCOM_HA_READ ||
|
if (lex->sql_command == (int)SQLCOM_HA_READ ||
|
||||||
@@ -8914,12 +8910,18 @@ subselect_start:
|
|||||||
yyerror(ER(ER_SYNTAX_ERROR));
|
yyerror(ER(ER_SYNTAX_ERROR));
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
we are making a "derived table" for the parenthesis
|
||||||
|
as we need to have a lex level to fit the union
|
||||||
|
after the parenthesis, e.g.
|
||||||
|
(SELECT .. ) UNION ... becomes
|
||||||
|
SELECT * FROM ((SELECT ...) UNION ...)
|
||||||
|
*/
|
||||||
if (mysql_new_select(Lex, 1))
|
if (mysql_new_select(Lex, 1))
|
||||||
YYABORT;
|
YYABORT;
|
||||||
};
|
};
|
||||||
|
|
||||||
subselect_end:
|
subselect_end:
|
||||||
')'
|
|
||||||
{
|
{
|
||||||
LEX *lex=Lex;
|
LEX *lex=Lex;
|
||||||
lex->pop_context();
|
lex->pop_context();
|
||||||
|
Reference in New Issue
Block a user