Commit cbbe4971 authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-21887: federatedx crashes on SELECT ... INTO query in select_handler code

- Don't try to push down SELECTs that have a side effect

- In case the storage engine did support pushdown of SELECT with an INTO
  clause, write the rows we've got from it into select->join->result,
  and not thd->protocol.  This way, SELECT ... INTO ... FROM
  smart_engine_table will put the result into where instructed, and
  NOT send it to the client.
parent 23685378
...@@ -301,7 +301,27 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -301,7 +301,27 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 7 1 PRIMARY t3 ALL NULL NULL NULL NULL 7
1 PRIMARY <derived2> ref key0 key0 18 federated.t3.name 2 1 PRIMARY <derived2> ref key0 key0 18 federated.t3.name 2
2 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL 2 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL
DROP TABLE federated.t1, federated.t2, federated.t3; #
# MDEV-21887: federatedx crashes on SELECT ... INTO query in select_handler code
#
CREATE TABLE federated.t4 (
id int(20) NOT NULL,
name varchar(16) NOT NULL default ''
) engine=myisam;
insert into federated.t4 select * from federated.t1;
select * from federated.t4;
id name
1 zzz
3 xxx
4 xxx
5 yyy
7 yyy
select name into @var from federated.t1 where id=3 limit 1 ;
select @var;
@var
xxx
select name into outfile 'tmp.txt' from federated.t1;
DROP TABLE federated.t1, federated.t2, federated.t3, federated.t4;
connection slave; connection slave;
DROP TABLE federated.t1, federated.t2; DROP TABLE federated.t1, federated.t2;
connection default; connection default;
......
...@@ -147,8 +147,27 @@ FROM federated.t3, ...@@ -147,8 +147,27 @@ FROM federated.t3,
SELECT * FROM federated.t1 WHERE id >= 5) t SELECT * FROM federated.t1 WHERE id >= 5) t
WHERE federated.t3.name=t.name; WHERE federated.t3.name=t.name;
--echo #
--echo # MDEV-21887: federatedx crashes on SELECT ... INTO query in select_handler code
--echo #
DROP TABLE federated.t1, federated.t2, federated.t3; CREATE TABLE federated.t4 (
id int(20) NOT NULL,
name varchar(16) NOT NULL default ''
) engine=myisam;
insert into federated.t4 select * from federated.t1;
--sorted_result
select * from federated.t4;
select name into @var from federated.t1 where id=3 limit 1 ;
select @var;
select name into outfile 'tmp.txt' from federated.t1;
let $path=`select concat(@@datadir, 'test/tmp.txt')`;
remove_file $path;
DROP TABLE federated.t1, federated.t2, federated.t3, federated.t4;
connection slave; connection slave;
DROP TABLE federated.t1, federated.t2; DROP TABLE federated.t1, federated.t2;
......
...@@ -77,18 +77,17 @@ bool Pushdown_select::init() ...@@ -77,18 +77,17 @@ bool Pushdown_select::init()
bool Pushdown_select::send_result_set_metadata() bool Pushdown_select::send_result_set_metadata()
{ {
THD *thd= handler->thd;
Protocol *protocol= thd->protocol;
DBUG_ENTER("Pushdown_select::send_result_set_metadata"); DBUG_ENTER("Pushdown_select::send_result_set_metadata");
#ifdef WITH_WSREP #ifdef WITH_WSREP
THD *thd= handler->thd;
if (WSREP(thd) && thd->wsrep_retry_query) if (WSREP(thd) && thd->wsrep_retry_query)
{ {
WSREP_DEBUG("skipping select metadata"); WSREP_DEBUG("skipping select metadata");
DBUG_RETURN(false); DBUG_RETURN(false);
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
if (protocol->send_result_set_metadata(&result_columns, if (select->join->result->send_result_set_metadata(result_columns,
Protocol::SEND_NUM_ROWS | Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF)) Protocol::SEND_EOF))
DBUG_RETURN(true); DBUG_RETURN(true);
...@@ -99,21 +98,10 @@ bool Pushdown_select::send_result_set_metadata() ...@@ -99,21 +98,10 @@ bool Pushdown_select::send_result_set_metadata()
bool Pushdown_select::send_data() bool Pushdown_select::send_data()
{ {
THD *thd= handler->thd;
Protocol *protocol= thd->protocol;
DBUG_ENTER("Pushdown_select::send_data"); DBUG_ENTER("Pushdown_select::send_data");
protocol->prepare_for_resend(); if (select->join->result->send_data(result_columns))
if (protocol->send_result_set_row(&result_columns))
{
protocol->remove_last_row();
DBUG_RETURN(true); DBUG_RETURN(true);
}
thd->inc_sent_row_count(1);
if (thd->vio_ok())
DBUG_RETURN(protocol->write());
DBUG_RETURN(false); DBUG_RETURN(false);
} }
...@@ -121,16 +109,10 @@ bool Pushdown_select::send_data() ...@@ -121,16 +109,10 @@ bool Pushdown_select::send_data()
bool Pushdown_select::send_eof() bool Pushdown_select::send_eof()
{ {
THD *thd= handler->thd;
DBUG_ENTER("Pushdown_select::send_eof"); DBUG_ENTER("Pushdown_select::send_eof");
/* if (select->join->result->send_eof())
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
*/
if (thd->is_error())
DBUG_RETURN(true); DBUG_RETURN(true);
::my_eof(thd);
DBUG_RETURN(false); DBUG_RETURN(false);
} }
......
...@@ -182,6 +182,16 @@ create_federatedx_select_handler(THD* thd, SELECT_LEX *sel) ...@@ -182,6 +182,16 @@ create_federatedx_select_handler(THD* thd, SELECT_LEX *sel)
return 0; return 0;
} }
/*
Currently, ha_federatedx_select_handler::init_scan just takes the
thd->query and sends it to the backend.
This obviously won't work if the SELECT uses an "INTO @var" or
"INTO OUTFILE". It is also unlikely to work if the select has some
other kind of side effect.
*/
if (sel->uncacheable & UNCACHEABLE_SIDEEFFECT)
return NULL;
handler= new ha_federatedx_select_handler(thd, sel); handler= new ha_federatedx_select_handler(thd, sel);
return handler; return handler;
...@@ -285,9 +295,10 @@ int ha_federatedx_select_handler::end_scan() ...@@ -285,9 +295,10 @@ int ha_federatedx_select_handler::end_scan()
DBUG_RETURN(0); DBUG_RETURN(0);
} }
\
void ha_federatedx_select_handler::print_error(int, unsigned long) void ha_federatedx_select_handler::print_error(int error, myf error_flag)
{ {
select_handler::print_error(error, error_flag);
} }
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