Bug #32858: Erro: "Incorrect usage of UNION and INTO" does not take

subselects into account
  
It is forbidden to use the SELECT INTO construction inside UNION statements
unless on the last SELECT of the union. The parser records whether it 
has seen INTO or not when parsing a UNION statement. But if the INTO was
legally used in an outer query, an error is thrown if UNION is seen in a
subquery. Fixed in 5.0 by remembering the nesting level of INTO tokens and 
mitigate the error unless it collides with the UNION.
parent e6b8470d
...@@ -153,6 +153,48 @@ ERROR 42S22: Unknown column 'a' in 'field list' ...@@ -153,6 +153,48 @@ ERROR 42S22: Unknown column 'a' in 'field list'
1 3 1 3
1 3 1 3
2 1 2 1
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
SELECT a INTO @v FROM (
SELECT a FROM t1
UNION
SELECT a FROM t1
) alias;
SELECT a INTO OUTFILE 'union.out.file' FROM (
SELECT a FROM t1
UNION
SELECT a FROM t1 WHERE 0
) alias;
SELECT a INTO DUMPFILE 'union.out.file2' FROM (
SELECT a FROM t1
UNION
SELECT a FROM t1 WHERE 0
) alias;
SELECT a FROM (
SELECT a FROM t1
UNION
SELECT a INTO @v FROM t1
) alias;
SELECT a FROM (
SELECT a FROM t1
UNION
SELECT a INTO OUTFILE 'union.out.file3' FROM t1
) alias;
SELECT a FROM (
SELECT a FROM t1
UNION
SELECT a INTO DUMPFILE 'union.out.file4' FROM t1
) alias;
SELECT a FROM t1 UNION SELECT a INTO @v FROM t1;
SELECT a FROM t1 UNION SELECT a INTO OUTFILE 'union.out.file5' FROM t1;
SELECT a FROM t1 UNION SELECT a INTO OUTFILE 'union.out.file6' FROM t1;
SELECT a INTO @v FROM t1 UNION SELECT a FROM t1;
ERROR HY000: Incorrect usage of UNION and INTO
SELECT a INTO OUTFILE 'union.out.file7' FROM t1 UNION SELECT a FROM t1;
ERROR HY000: Incorrect usage of UNION and INTO
SELECT a INTO DUMPFILE 'union.out.file8' FROM t1 UNION SELECT a FROM t1;
ERROR HY000: Incorrect usage of UNION and INTO
DROP TABLE t1;
CREATE TABLE t1 ( CREATE TABLE t1 (
`pseudo` char(35) NOT NULL default '', `pseudo` char(35) NOT NULL default '',
`pseudo1` char(35) NOT NULL default '', `pseudo1` char(35) NOT NULL default '',
......
...@@ -91,6 +91,65 @@ SELECT @a:=1 UNION SELECT @a:=@a+1; ...@@ -91,6 +91,65 @@ SELECT @a:=1 UNION SELECT @a:=@a+1;
(SELECT 1) UNION (SELECT 2) ORDER BY (SELECT a); (SELECT 1) UNION (SELECT 2) ORDER BY (SELECT a);
(SELECT 1,3) UNION (SELECT 2,1) ORDER BY (SELECT 2); (SELECT 1,3) UNION (SELECT 2,1) ORDER BY (SELECT 2);
#
# Bug#32858: Erro: "Incorrect usage of UNION and INTO" does not take subselects
# into account
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
SELECT a INTO @v FROM (
SELECT a FROM t1
UNION
SELECT a FROM t1
) alias;
SELECT a INTO OUTFILE 'union.out.file' FROM (
SELECT a FROM t1
UNION
SELECT a FROM t1 WHERE 0
) alias;
SELECT a INTO DUMPFILE 'union.out.file2' FROM (
SELECT a FROM t1
UNION
SELECT a FROM t1 WHERE 0
) alias;
#
# INTO will not be allowed in subqueries in version 5.1 and above.
#
SELECT a FROM (
SELECT a FROM t1
UNION
SELECT a INTO @v FROM t1
) alias;
SELECT a FROM (
SELECT a FROM t1
UNION
SELECT a INTO OUTFILE 'union.out.file3' FROM t1
) alias;
SELECT a FROM (
SELECT a FROM t1
UNION
SELECT a INTO DUMPFILE 'union.out.file4' FROM t1
) alias;
SELECT a FROM t1 UNION SELECT a INTO @v FROM t1;
SELECT a FROM t1 UNION SELECT a INTO OUTFILE 'union.out.file5' FROM t1;
SELECT a FROM t1 UNION SELECT a INTO OUTFILE 'union.out.file6' FROM t1;
--error ER_WRONG_USAGE
SELECT a INTO @v FROM t1 UNION SELECT a FROM t1;
--error ER_WRONG_USAGE
SELECT a INTO OUTFILE 'union.out.file7' FROM t1 UNION SELECT a FROM t1;
--error ER_WRONG_USAGE
SELECT a INTO DUMPFILE 'union.out.file8' FROM t1 UNION SELECT a FROM t1;
DROP TABLE t1;
# #
# Test bug reported by joc@presence-pc.com # Test bug reported by joc@presence-pc.com
# #
......
...@@ -1430,6 +1430,7 @@ void THD::rollback_item_tree_changes() ...@@ -1430,6 +1430,7 @@ void THD::rollback_item_tree_changes()
select_result::select_result() select_result::select_result()
{ {
thd=current_thd; thd=current_thd;
nest_level= -1;
} }
void select_result::send_error(uint errcode,const char *err) void select_result::send_error(uint errcode,const char *err)
......
...@@ -2193,6 +2193,7 @@ class select_result :public Sql_alloc { ...@@ -2193,6 +2193,7 @@ class select_result :public Sql_alloc {
protected: protected:
THD *thd; THD *thd;
SELECT_LEX_UNIT *unit; SELECT_LEX_UNIT *unit;
uint nest_level;
public: public:
select_result(); select_result();
virtual ~select_result() {}; virtual ~select_result() {};
...@@ -2229,6 +2230,12 @@ class select_result :public Sql_alloc { ...@@ -2229,6 +2230,12 @@ class select_result :public Sql_alloc {
*/ */
virtual void cleanup(); virtual void cleanup();
void set_thd(THD *thd_arg) { thd= thd_arg; } void set_thd(THD *thd_arg) { thd= thd_arg; }
/**
The nest level, if supported.
@return
-1 if nest level is undefined, otherwise a positive integer.
*/
int get_nest_level() { return nest_level; }
#ifdef EMBEDDED_LIBRARY #ifdef EMBEDDED_LIBRARY
virtual void begin_dataset() {} virtual void begin_dataset() {}
#else #else
...@@ -2322,6 +2329,14 @@ class select_export :public select_to_file { ...@@ -2322,6 +2329,14 @@ class select_export :public select_to_file {
bool fixed_row_size; bool fixed_row_size;
public: public:
select_export(sql_exchange *ex) :select_to_file(ex) {} select_export(sql_exchange *ex) :select_to_file(ex) {}
/**
Creates a select_export to represent INTO OUTFILE <filename> with a
defined level of subquery nesting.
*/
select_export(sql_exchange *ex, uint nest_level_arg) :select_to_file(ex)
{
nest_level= nest_level_arg;
}
~select_export(); ~select_export();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items); bool send_data(List<Item> &items);
...@@ -2331,6 +2346,15 @@ class select_export :public select_to_file { ...@@ -2331,6 +2346,15 @@ class select_export :public select_to_file {
class select_dump :public select_to_file { class select_dump :public select_to_file {
public: public:
select_dump(sql_exchange *ex) :select_to_file(ex) {} select_dump(sql_exchange *ex) :select_to_file(ex) {}
/**
Creates a select_export to represent INTO DUMPFILE <filename> with a
defined level of subquery nesting.
*/
select_dump(sql_exchange *ex, uint nest_level_arg) :
select_to_file(ex)
{
nest_level= nest_level_arg;
}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items); bool send_data(List<Item> &items);
}; };
...@@ -2763,6 +2787,16 @@ class select_dumpvar :public select_result_interceptor { ...@@ -2763,6 +2787,16 @@ class select_dumpvar :public select_result_interceptor {
public: public:
List<my_var> var_list; List<my_var> var_list;
select_dumpvar() { var_list.empty(); row_count= 0;} select_dumpvar() { var_list.empty(); row_count= 0;}
/**
Creates a select_dumpvar to represent INTO <variable> with a defined
level of subquery nesting.
*/
select_dumpvar(uint nest_level_arg)
{
var_list.empty();
row_count= 0;
nest_level= nest_level_arg;
}
~select_dumpvar() {} ~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items); bool send_data(List<Item> &items);
......
...@@ -8380,7 +8380,8 @@ procedure_item: ...@@ -8380,7 +8380,8 @@ procedure_item:
select_var_list_init: select_var_list_init:
{ {
LEX *lex=Lex; LEX *lex=Lex;
if (!lex->describe && (!(lex->result= new select_dumpvar()))) if (!lex->describe &&
(!(lex->result= new select_dumpvar(lex->nest_level))))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
select_var_list select_var_list
...@@ -8454,7 +8455,7 @@ into_destination: ...@@ -8454,7 +8455,7 @@ into_destination:
LEX *lex= Lex; LEX *lex= Lex;
lex->uncacheable(UNCACHEABLE_SIDEEFFECT); lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
if (!(lex->exchange= new sql_exchange($2.str, 0)) || if (!(lex->exchange= new sql_exchange($2.str, 0)) ||
!(lex->result= new select_export(lex->exchange))) !(lex->result= new select_export(lex->exchange, lex->nest_level)))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
opt_field_term opt_line_term opt_field_term opt_line_term
...@@ -8466,7 +8467,7 @@ into_destination: ...@@ -8466,7 +8467,7 @@ into_destination:
lex->uncacheable(UNCACHEABLE_SIDEEFFECT); lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
if (!(lex->exchange= new sql_exchange($2.str,1))) if (!(lex->exchange= new sql_exchange($2.str,1)))
MYSQL_YYABORT; MYSQL_YYABORT;
if (!(lex->result= new select_dump(lex->exchange))) if (!(lex->result= new select_dump(lex->exchange, lex->nest_level)))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
} }
...@@ -11804,9 +11805,15 @@ union_list: ...@@ -11804,9 +11805,15 @@ union_list:
UNION_SYM union_option UNION_SYM union_option
{ {
LEX *lex=Lex; LEX *lex=Lex;
if (lex->result) if (lex->result &&
(lex->result->get_nest_level() == -1 ||
lex->result->get_nest_level() == lex->nest_level))
{ {
/* Only the last SELECT can have INTO...... */ /*
Only the last SELECT can have INTO unless the INTO and UNION
are at different nest levels. In version 5.1 and above, INTO
will onle be allowed at top level.
*/
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
MYSQL_YYABORT; MYSQL_YYABORT;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment