Commit dd121781 authored by unknown's avatar unknown

Merge bk-internal.mysql.com:/home/bk/mysql-5.1-runtime

into  damien-katzs-computer.local:/Users/dkatz/mysql-5.1-runtime

parents 219af872 4db46e26
...@@ -203,7 +203,7 @@ typedef struct st_net { ...@@ -203,7 +203,7 @@ typedef struct st_net {
unsigned char reading_or_writing; unsigned char reading_or_writing;
char save_char; char save_char;
my_bool no_send_ok; /* For SPs and other things that do multiple stmts */ my_bool no_send_ok; /* For SPs and other things that do multiple stmts */
my_bool no_send_eof; /* For SPs' first version read-only cursors */ my_bool unused; /* Please remove with the next incompatible ABI change */
my_bool compress; my_bool compress;
/* /*
Set if OK packet is already sent, and we do not need to send error Set if OK packet is already sent, and we do not need to send error
......
...@@ -498,3 +498,71 @@ handler t1_alias read a next; ...@@ -498,3 +498,71 @@ handler t1_alias read a next;
handler t1_alias READ a next where inexistent > 0; handler t1_alias READ a next where inexistent > 0;
handler t1_alias close; handler t1_alias close;
drop table t1; drop table t1;
#
# Bug#21587 FLUSH TABLES causes server crash when used with HANDLER statements
#
--disable_warnings
drop table if exists t1,t2;
--enable_warnings
create table t1 (c1 int);
create table t2 (c1 int);
insert into t1 values (1);
insert into t2 values (2);
--echo connection: default
handler t1 open;
handler t1 read first;
connect (flush,localhost,root,,);
connection flush;
--echo connection: flush
--send flush tables;
connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
--source include/wait_condition.inc
handler t2 open;
handler t2 read first;
handler t1 read next;
handler t1 close;
handler t2 close;
connection flush;
reap;
connection default;
drop table t1,t2;
disconnect flush;
#
# Bug#31409 RENAME TABLE causes server crash or deadlock when used with HANDLER statements
#
--disable_warnings
drop table if exists t1,t2;
--enable_warnings
create table t1 (c1 int);
--echo connection: default
handler t1 open;
handler t1 read first;
connect (flush,localhost,root,,);
connection flush;
--echo connection: flush
--send rename table t1 to t2;
connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info = "rename table t1 to t2";
--source include/wait_condition.inc
handler t2 open;
handler t2 read first;
--error ER_NO_SUCH_TABLE
handler t1 read next;
handler t1 close;
handler t2 close;
connection flush;
reap;
connection default;
drop table t2;
disconnect flush;
...@@ -535,3 +535,43 @@ handler t1_alias READ a next where inexistent > 0; ...@@ -535,3 +535,43 @@ handler t1_alias READ a next where inexistent > 0;
ERROR 42S22: Unknown column 'inexistent' in 'field list' ERROR 42S22: Unknown column 'inexistent' in 'field list'
handler t1_alias close; handler t1_alias close;
drop table t1; drop table t1;
drop table if exists t1,t2;
create table t1 (c1 int);
create table t2 (c1 int);
insert into t1 values (1);
insert into t2 values (2);
connection: default
handler t1 open;
handler t1 read first;
c1
1
connection: flush
flush tables;;
connection: default
handler t2 open;
handler t2 read first;
c1
2
handler t1 read next;
c1
1
handler t1 close;
handler t2 close;
drop table t1,t2;
drop table if exists t1,t2;
create table t1 (c1 int);
connection: default
handler t1 open;
handler t1 read first;
c1
connection: flush
rename table t1 to t2;;
connection: default
handler t2 open;
handler t2 read first;
c1
handler t1 read next;
ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
handler t2 close;
drop table t2;
...@@ -535,3 +535,43 @@ handler t1_alias READ a next where inexistent > 0; ...@@ -535,3 +535,43 @@ handler t1_alias READ a next where inexistent > 0;
ERROR 42S22: Unknown column 'inexistent' in 'field list' ERROR 42S22: Unknown column 'inexistent' in 'field list'
handler t1_alias close; handler t1_alias close;
drop table t1; drop table t1;
drop table if exists t1,t2;
create table t1 (c1 int);
create table t2 (c1 int);
insert into t1 values (1);
insert into t2 values (2);
connection: default
handler t1 open;
handler t1 read first;
c1
1
connection: flush
flush tables;;
connection: default
handler t2 open;
handler t2 read first;
c1
2
handler t1 read next;
c1
1
handler t1 close;
handler t2 close;
drop table t1,t2;
drop table if exists t1,t2;
create table t1 (c1 int);
connection: default
handler t1 open;
handler t1 read first;
c1
connection: flush
rename table t1 to t2;;
connection: default
handler t2 open;
handler t2 read first;
c1
handler t1 read next;
ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
handler t2 close;
drop table t2;
...@@ -270,6 +270,10 @@ use mysql; ...@@ -270,6 +270,10 @@ use mysql;
lock tables general_log read local, help_category read local; lock tables general_log read local, help_category read local;
ERROR HY000: You can't use locks with log tables. ERROR HY000: You can't use locks with log tables.
unlock tables; unlock tables;
drop table if exists mysql.renamed_general_log;
drop table if exists mysql.renamed_slow_log;
drop table if exists mysql.general_log_new;
drop table if exists mysql.slow_log_new;
use mysql; use mysql;
RENAME TABLE general_log TO renamed_general_log; RENAME TABLE general_log TO renamed_general_log;
ERROR HY000: Cannot rename 'general_log'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to 'general_log' ERROR HY000: Cannot rename 'general_log'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to 'general_log'
......
...@@ -142,7 +142,10 @@ declare c cursor for insert into test.t1 values ("foo", 42); ...@@ -142,7 +142,10 @@ declare c cursor for insert into test.t1 values ("foo", 42);
open c; open c;
close c; close c;
end| end|
ERROR 42000: Cursor statement must be a SELECT 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 'insert into test.t1 values ("foo", 42);
open c;
close c;
end' at line 3
create procedure p() create procedure p()
begin begin
declare x int; declare x int;
...@@ -1491,3 +1494,19 @@ ALTER DATABASE `#mysql50#upgrade-me` UPGRADE DATA DIRECTORY NAME; ...@@ -1491,3 +1494,19 @@ ALTER DATABASE `#mysql50#upgrade-me` UPGRADE DATA DIRECTORY NAME;
RETURN 0; RETURN 0;
END// END//
ERROR HY000: Can't drop or alter a DATABASE from within another stored routine ERROR HY000: Can't drop or alter a DATABASE from within another stored routine
DROP PROCEDURE IF EXISTS p1;
CREATE PROCEDURE p1()
BEGIN
DECLARE c char(100);
DECLARE cur1 CURSOR FOR SHOW TABLES;
OPEN cur1;
FETCH cur1 INTO c;
select c;
CLOSE cur1;
END|
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 'SHOW TABLES;
OPEN cur1;
FETCH cur1 INTO c;
select c;
CLOSE cur1;
END' at line 4
...@@ -953,7 +953,7 @@ BEGIN ...@@ -953,7 +953,7 @@ BEGIN
DECLARE col1, col2, col3, col4, col6 CHAR(255); DECLARE col1, col2, col3, col4, col6 CHAR(255);
DECLARE default_val VARCHAR(65532); DECLARE default_val VARCHAR(65532);
DECLARE done INT DEFAULT 0; DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR SHOW COLUMNS FROM bug23037; DECLARE cur1 CURSOR FOR SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, EXTRA FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='bug23037';
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
OPEN cur1; OPEN cur1;
FETCH cur1 INTO col1, col2, col3, col4, default_val, col6; FETCH cur1 INTO col1, col2, col3, col4, default_val, col6;
......
...@@ -295,6 +295,13 @@ unlock tables; ...@@ -295,6 +295,13 @@ unlock tables;
# Bug #21785 Server crashes after rename of the log table # Bug #21785 Server crashes after rename of the log table
# #
--disable_warnings
drop table if exists mysql.renamed_general_log;
drop table if exists mysql.renamed_slow_log;
drop table if exists mysql.general_log_new;
drop table if exists mysql.slow_log_new;
--enable_warnings
use mysql; use mysql;
# Should result in error # Should result in error
--error ER_CANT_RENAME_LOG_TABLE --error ER_CANT_RENAME_LOG_TABLE
...@@ -358,6 +365,55 @@ flush logs; ...@@ -358,6 +365,55 @@ flush logs;
drop table renamed_general_log, renamed_slow_log; drop table renamed_general_log, renamed_slow_log;
use test; use test;
#
# Bug#27858 (Failing to log to a log table doesn't log anything to error log)
#
# This test works as expected, it's a negative test.
# The message "[ERROR] Failed to write to mysql.general_log"
# is printed to master.err when writing to the table mysql.general_log
# failed.
# However, this message is picked up by mysql-test-run.pl,
# and reported as a test failure, which is a false negative.
# There is no current way to *selectively* filter out these expected error conditions
# (see mysql-test/lib/mtr_report.pl, mtr_report_stats()).
# Instead of filtering all occurences of "Failed to write to
# mysql.general_log", which could hide bugs when the error is not expected,
# this test case is commented instead.
# TODO: improve filtering of expected errors in master.err in
# mysql-test-run.pl (based on the test name ?), and uncomment this test.
# --disable_warnings
# drop table if exists mysql.bad_general_log;
# drop table if exists mysql.bad_slow_log;
# drop table if exists mysql.general_log_hide;
# drop table if exists mysql.slow_log_hide;
# --enable_warnings
#
# create table mysql.bad_general_log (a int) engine= CSV;
# create table mysql.bad_slow_log (a int) engine= CSV;
#
# # Rename does not perform checks on the table structure,
# # exploiting this to force a failure to log
# rename table mysql.general_log to mysql.general_log_hide, mysql.bad_general_log TO mysql.general_log;
# rename table mysql.slow_log to mysql.slow_log_hide, mysql.bad_slow_log TO mysql.slow_log;
#
# # The following message should be printed in master.log:
# # [ERROR] Failed to write to mysql.general_log
# # TODO: See how verifying this could be automated
#
# flush tables;
# select "logging this should fail";
#
# # Restore the log tables
#
# rename table mysql.general_log to mysql.bad_general_log, mysql.general_log_hide TO mysql.general_log;
# rename table mysql.slow_log to mysql.bad_slow_log, mysql.slow_log_hide TO mysql.slow_log;
#
# flush tables;
#
# drop table mysql.bad_general_log;
# drop table mysql.bad_slow_log;
# #
# Bug #21966 Strange warnings on repair of the log tables # Bug #21966 Strange warnings on repair of the log tables
# #
......
...@@ -196,7 +196,7 @@ select f(10)| ...@@ -196,7 +196,7 @@ select f(10)|
drop function f| drop function f|
--error 1322 --error ER_PARSE_ERROR
create procedure p() create procedure p()
begin begin
declare c cursor for insert into test.t1 values ("foo", 42); declare c cursor for insert into test.t1 values ("foo", 42);
...@@ -2178,6 +2178,27 @@ END// ...@@ -2178,6 +2178,27 @@ END//
delimiter ;// delimiter ;//
#
# Bug#29223 declare cursor c for SHOW .....
#
--disable_warnings
DROP PROCEDURE IF EXISTS p1;
--enable_warnings
--delimiter |
--error ER_PARSE_ERROR
CREATE PROCEDURE p1()
BEGIN
DECLARE c char(100);
DECLARE cur1 CURSOR FOR SHOW TABLES;
OPEN cur1;
FETCH cur1 INTO c;
select c;
CLOSE cur1;
END|
--delimiter ;
# #
# BUG#NNNN: New bug synopsis # BUG#NNNN: New bug synopsis
# #
......
...@@ -387,10 +387,15 @@ bool Log_to_csv_event_handler:: ...@@ -387,10 +387,15 @@ bool Log_to_csv_event_handler::
if (table->field[1]->store(user_host, user_host_len, client_cs) || if (table->field[1]->store(user_host, user_host_len, client_cs) ||
table->field[2]->store((longlong) thread_id, TRUE) || table->field[2]->store((longlong) thread_id, TRUE) ||
table->field[3]->store((longlong) server_id, TRUE) || table->field[3]->store((longlong) server_id, TRUE) ||
table->field[4]->store(command_type, command_type_len, client_cs) || table->field[4]->store(command_type, command_type_len, client_cs))
table->field[5]->store(sql_text, sql_text_len, client_cs))
goto err; goto err;
/*
A positive return value in store() means truncation.
Still logging a message in the log in this case.
*/
if (table->field[5]->store(sql_text, sql_text_len, client_cs) < 0)
goto err;
/* mark all fields as not null */ /* mark all fields as not null */
table->field[1]->set_notnull(); table->field[1]->set_notnull();
...@@ -407,19 +412,14 @@ bool Log_to_csv_event_handler:: ...@@ -407,19 +412,14 @@ bool Log_to_csv_event_handler::
/* log table entries are not replicated */ /* log table entries are not replicated */
if (table->file->ha_write_row(table->record[0])) if (table->file->ha_write_row(table->record[0]))
{ goto err;
struct tm start;
localtime_r(&event_time, &start);
sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.general_log",
start.tm_year % 100, start.tm_mon + 1,
start.tm_mday, start.tm_hour,
start.tm_min, start.tm_sec);
}
result= FALSE; result= FALSE;
err: err:
if (result)
sql_print_error("Failed to write to mysql.general_log");
if (need_rnd_end) if (need_rnd_end)
{ {
table->file->ha_rnd_end(); table->file->ha_rnd_end();
...@@ -595,25 +595,24 @@ bool Log_to_csv_event_handler:: ...@@ -595,25 +595,24 @@ bool Log_to_csv_event_handler::
goto err; goto err;
table->field[9]->set_notnull(); table->field[9]->set_notnull();
/* sql_text */ /*
if (table->field[10]->store(sql_text,sql_text_len, client_cs)) Column sql_text.
A positive return value in store() means truncation.
Still logging a message in the log in this case.
*/
if (table->field[10]->store(sql_text, sql_text_len, client_cs) < 0)
goto err; goto err;
/* log table entries are not replicated */ /* log table entries are not replicated */
if (table->file->ha_write_row(table->record[0])) if (table->file->ha_write_row(table->record[0]))
{ goto err;
struct tm start;
localtime_r(&current_time, &start);
sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.slow_log",
start.tm_year % 100, start.tm_mon + 1,
start.tm_mday, start.tm_hour,
start.tm_min, start.tm_sec);
}
result= FALSE; result= FALSE;
err: err:
if (result)
sql_print_error("Failed to write to mysql.slow_log");
if (need_rnd_end) if (need_rnd_end)
{ {
table->file->ha_rnd_end(); table->file->ha_rnd_end();
......
...@@ -123,7 +123,7 @@ my_bool my_net_init(NET *net, Vio* vio) ...@@ -123,7 +123,7 @@ my_bool my_net_init(NET *net, Vio* vio)
MYF(MY_WME)))) MYF(MY_WME))))
DBUG_RETURN(1); DBUG_RETURN(1);
net->buff_end=net->buff+net->max_packet; net->buff_end=net->buff+net->max_packet;
net->no_send_ok= net->no_send_eof= net->no_send_error= 0; net->no_send_ok= net->no_send_error= 0;
net->error=0; net->return_errno=0; net->return_status=0; net->error=0; net->return_errno=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0; net->pkt_nr=net->compress_pkt_nr=0;
net->write_pos=net->read_pos = net->buff; net->write_pos=net->read_pos = net->buff;
......
...@@ -346,7 +346,7 @@ send_eof(THD *thd) ...@@ -346,7 +346,7 @@ send_eof(THD *thd)
{ {
NET *net= &thd->net; NET *net= &thd->net;
DBUG_ENTER("send_eof"); DBUG_ENTER("send_eof");
if (net->vio != 0 && !net->no_send_eof) if (net->vio != 0)
{ {
write_eof_packet(thd, net); write_eof_packet(thd, net);
VOID(net_flush(net)); VOID(net_flush(net));
......
...@@ -2466,7 +2466,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -2466,7 +2466,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
DBUG_RETURN(0); DBUG_RETURN(0);
} }
/* close handler tables which are marked for flush */ /*
In order for the back off and re-start process to work properly,
handler tables having old versions (due to FLUSH TABLES or pending
name-lock) MUST be closed. This is specially important if a name-lock
is pending for any table of the handler_tables list, otherwise a
deadlock may occur.
*/
if (thd->handler_tables) if (thd->handler_tables)
mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE); mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
...@@ -2533,6 +2539,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -2533,6 +2539,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->db_stat == 0 signals wait_for_locked_table_names table->db_stat == 0 signals wait_for_locked_table_names
that the tables in question are not used any more. See that the tables in question are not used any more. See
table_is_used call for details. table_is_used call for details.
Notice that HANDLER tables were already taken care of by
the earlier call to mysql_ha_flush() in this same critical
section.
*/ */
close_old_data_files(thd,thd->open_tables,0,0); close_old_data_files(thd,thd->open_tables,0,0);
/* /*
......
...@@ -698,6 +698,7 @@ void THD::cleanup(void) ...@@ -698,6 +698,7 @@ void THD::cleanup(void)
pthread_mutex_lock(&LOCK_user_locks); pthread_mutex_lock(&LOCK_user_locks);
item_user_lock_release(ull); item_user_lock_release(ull);
pthread_mutex_unlock(&LOCK_user_locks); pthread_mutex_unlock(&LOCK_user_locks);
ull= NULL;
} }
cleanup_done=1; cleanup_done=1;
...@@ -1422,7 +1423,14 @@ bool select_to_file::send_eof() ...@@ -1422,7 +1423,14 @@ bool select_to_file::send_eof()
if (my_close(file,MYF(MY_WME))) if (my_close(file,MYF(MY_WME)))
error= 1; error= 1;
if (!error) if (!error)
{
/*
In order to remember the value of affected rows for ROW_COUNT()
function, SELECT INTO has to have an own SQLCOM.
TODO: split from SQLCOM_SELECT
*/
::send_ok(thd,row_count); ::send_ok(thd,row_count);
}
file= -1; file= -1;
return error; return error;
} }
...@@ -2337,6 +2345,11 @@ bool select_dumpvar::send_eof() ...@@ -2337,6 +2345,11 @@ bool select_dumpvar::send_eof()
if (! row_count) if (! row_count)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA)); ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
/*
In order to remember the value of affected rows for ROW_COUNT()
function, SELECT INTO has to have an own SQLCOM.
TODO: split from SQLCOM_SELECT
*/
::send_ok(thd,row_count); ::send_ok(thd,row_count);
return 0; return 0;
} }
......
...@@ -142,7 +142,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -142,7 +142,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
{ {
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0; thd->row_count_func= 0;
send_ok(thd); // No matching records send_ok(thd, (ha_rows) thd->row_count_func); // No matching records
DBUG_RETURN(0); DBUG_RETURN(0);
} }
#endif #endif
...@@ -159,7 +159,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -159,7 +159,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0; thd->row_count_func= 0;
send_ok(thd,0L); send_ok(thd, (ha_rows) thd->row_count_func);
/* /*
We don't need to call reset_auto_increment in this case, because We don't need to call reset_auto_increment in this case, because
mysql_truncate always gives a NULL conds argument, hence we never mysql_truncate always gives a NULL conds argument, hence we never
...@@ -386,7 +386,7 @@ cleanup: ...@@ -386,7 +386,7 @@ cleanup:
if (error < 0 || (thd->lex->ignore && !thd->is_fatal_error)) if (error < 0 || (thd->lex->ignore && !thd->is_fatal_error))
{ {
thd->row_count_func= deleted; thd->row_count_func= deleted;
send_ok(thd,deleted); send_ok(thd, (ha_rows) thd->row_count_func);
DBUG_PRINT("info",("%ld records deleted",(long) deleted)); DBUG_PRINT("info",("%ld records deleted",(long) deleted));
} }
DBUG_RETURN(error >= 0 || thd->net.report_error); DBUG_RETURN(error >= 0 || thd->net.report_error);
...@@ -889,7 +889,7 @@ bool multi_delete::send_eof() ...@@ -889,7 +889,7 @@ bool multi_delete::send_eof()
if (!local_error) if (!local_error)
{ {
thd->row_count_func= deleted; thd->row_count_func= deleted;
::send_ok(thd, deleted); ::send_ok(thd, (ha_rows) thd->row_count_func);
} }
return 0; return 0;
} }
......
...@@ -65,11 +65,6 @@ ...@@ -65,11 +65,6 @@
static enum enum_ha_read_modes rkey_to_rnext[]= static enum enum_ha_read_modes rkey_to_rnext[]=
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV }; { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
#define HANDLER_TABLES_HACK(thd) { \
TABLE *tmp=thd->open_tables; \
thd->open_tables=thd->handler_tables; \
thd->handler_tables=tmp; }
static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags); static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags);
...@@ -187,6 +182,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -187,6 +182,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
char *db, *name, *alias; char *db, *name, *alias;
uint dblen, namelen, aliaslen, counter; uint dblen, namelen, aliaslen, counter;
int error; int error;
TABLE *backup_open_tables;
DBUG_ENTER("mysql_ha_open"); DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias, tables->db, tables->table_name, tables->alias,
...@@ -215,17 +211,38 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -215,17 +211,38 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
} }
} }
/*
Save and reset the open_tables list so that open_tables() won't
be able to access (or know about) the previous list. And on return
from open_tables(), thd->open_tables will contain only the opened
table.
The thd->handler_tables list is kept as-is to avoid deadlocks if
open_table(), called by open_tables(), needs to back-off because
of a pending name-lock on the table being opened.
See open_table() back-off comments for more details.
*/
backup_open_tables= thd->open_tables;
thd->open_tables= NULL;
/* /*
open_tables() will set 'tables->table' if successful. open_tables() will set 'tables->table' if successful.
It must be NULL for a real open when calling open_tables(). It must be NULL for a real open when calling open_tables().
*/ */
DBUG_ASSERT(! tables->table); DBUG_ASSERT(! tables->table);
HANDLER_TABLES_HACK(thd);
/* for now HANDLER can be used only for real TABLES */ /* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE; tables->required_type= FRMTYPE_TABLE;
error= open_tables(thd, &tables, &counter, 0); error= open_tables(thd, &tables, &counter, 0);
HANDLER_TABLES_HACK(thd); /* restore the state and merge the opened table into handler_tables list */
if (thd->open_tables)
{
thd->open_tables->next= thd->handler_tables;
thd->handler_tables= thd->open_tables;
}
thd->open_tables= backup_open_tables;
if (error) if (error)
goto err; goto err;
...@@ -351,7 +368,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -351,7 +368,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
ha_rows select_limit_cnt, ha_rows offset_limit_cnt) ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
{ {
TABLE_LIST *hash_tables; TABLE_LIST *hash_tables;
TABLE *table; TABLE *table, *backup_open_tables;
MYSQL_LOCK *lock; MYSQL_LOCK *lock;
List<Item> list; List<Item> list;
Protocol *protocol= thd->protocol; Protocol *protocol= thd->protocol;
...@@ -361,7 +378,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -361,7 +378,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
uint num_rows; uint num_rows;
uchar *key; uchar *key;
uint key_len; uint key_len;
bool not_used; bool need_reopen;
DBUG_ENTER("mysql_ha_read"); DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'", DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias)); tables->db, tables->table_name, tables->alias));
...@@ -375,6 +392,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -375,6 +392,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
List_iterator<Item> it(list); List_iterator<Item> it(list);
it++; it++;
retry:
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
(uchar*) tables->alias, (uchar*) tables->alias,
strlen(tables->alias) + 1))) strlen(tables->alias) + 1)))
...@@ -428,9 +446,34 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -428,9 +446,34 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
} }
tables->table=table; tables->table=table;
HANDLER_TABLES_HACK(thd); /* save open_tables state */
lock= mysql_lock_tables(thd, &tables->table, 1, 0, &not_used); backup_open_tables= thd->open_tables;
HANDLER_TABLES_HACK(thd); /*
mysql_lock_tables() needs thd->open_tables to be set correctly to
be able to handle aborts properly. When the abort happens, it's
safe to not protect thd->handler_tables because it won't close any
tables.
*/
thd->open_tables= thd->handler_tables;
lock= mysql_lock_tables(thd, &tables->table, 1,
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
/* restore previous context */
thd->open_tables= backup_open_tables;
if (need_reopen)
{
mysql_ha_close_table(thd, tables);
hash_tables->table= NULL;
/*
The lock might have been aborted, we need to manually reset
thd->some_tables_deleted because handler's tables are closed
in a non-standard way. Otherwise we might loop indefinitely.
*/
thd->some_tables_deleted= 0;
goto retry;
}
if (!lock) if (!lock)
goto err0; // mysql_lock_tables() printed error message already goto err0; // mysql_lock_tables() printed error message already
......
...@@ -2540,25 +2540,13 @@ sp_decl: ...@@ -2540,25 +2540,13 @@ sp_decl:
sp_cursor_stmt: sp_cursor_stmt:
{ {
Lex->sphead->reset_lex(YYTHD); Lex->sphead->reset_lex(YYTHD);
/*
We use statement here just be able to get a better
error message. Using 'select' works too, but will then
result in a generic "syntax error" if a non-select
statement is given.
*/
} }
statement select
{ {
LEX *lex= Lex; LEX *lex= Lex;
if (lex->sql_command != SQLCOM_SELECT && DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
!(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND))
{
my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY),
MYF(0));
MYSQL_YYABORT;
}
if (lex->result) if (lex->result)
{ {
my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT), my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT),
......
...@@ -16827,7 +16827,7 @@ static void bug20023_change_user(MYSQL *con) ...@@ -16827,7 +16827,7 @@ static void bug20023_change_user(MYSQL *con)
opt_db ? opt_db : "test")); opt_db ? opt_db : "test"));
} }
static void bug20023_query_int_variable(MYSQL *con, static bool query_int_variable(MYSQL *con,
const char *var_name, const char *var_name,
int *var_value) int *var_value)
{ {
...@@ -16836,16 +16836,25 @@ static void bug20023_query_int_variable(MYSQL *con, ...@@ -16836,16 +16836,25 @@ static void bug20023_query_int_variable(MYSQL *con,
char query_buffer[MAX_TEST_QUERY_LENGTH]; char query_buffer[MAX_TEST_QUERY_LENGTH];
bool is_null;
my_snprintf(query_buffer, my_snprintf(query_buffer,
sizeof (query_buffer), sizeof (query_buffer),
"SELECT @@%s", "SELECT %s",
(const char *) var_name); (const char *) var_name);
DIE_IF(mysql_query(con, query_buffer)); DIE_IF(mysql_query(con, query_buffer));
DIE_UNLESS(rs= mysql_store_result(con)); DIE_UNLESS(rs= mysql_store_result(con));
DIE_UNLESS(row= mysql_fetch_row(rs)); DIE_UNLESS(row= mysql_fetch_row(rs));
is_null= row[0] == NULL;
if (!is_null)
*var_value= atoi(row[0]); *var_value= atoi(row[0]);
mysql_free_result(rs); mysql_free_result(rs);
return is_null;
} }
static void test_bug20023() static void test_bug20023()
...@@ -16879,12 +16888,12 @@ static void test_bug20023() ...@@ -16879,12 +16888,12 @@ static void test_bug20023()
Remember original SQL_BIG_SELECTS, MAX_JOIN_SIZE values. Remember original SQL_BIG_SELECTS, MAX_JOIN_SIZE values.
***********************************************************************/ ***********************************************************************/
bug20023_query_int_variable(&con, query_int_variable(&con,
"session.sql_big_selects", "@@session.sql_big_selects",
&sql_big_selects_orig); &sql_big_selects_orig);
bug20023_query_int_variable(&con, query_int_variable(&con,
"global.max_join_size", "@@global.max_join_size",
&max_join_size_orig); &max_join_size_orig);
/*********************************************************************** /***********************************************************************
...@@ -16897,8 +16906,8 @@ static void test_bug20023() ...@@ -16897,8 +16906,8 @@ static void test_bug20023()
/* Query SQL_BIG_SELECTS. */ /* Query SQL_BIG_SELECTS. */
bug20023_query_int_variable(&con, query_int_variable(&con,
"session.sql_big_selects", "@@session.sql_big_selects",
&sql_big_selects_2); &sql_big_selects_2);
/* Check that SQL_BIG_SELECTS is reset properly. */ /* Check that SQL_BIG_SELECTS is reset properly. */
...@@ -16921,8 +16930,8 @@ static void test_bug20023() ...@@ -16921,8 +16930,8 @@ static void test_bug20023()
/* Query SQL_BIG_SELECTS. */ /* Query SQL_BIG_SELECTS. */
bug20023_query_int_variable(&con, query_int_variable(&con,
"session.sql_big_selects", "@@session.sql_big_selects",
&sql_big_selects_3); &sql_big_selects_3);
/* Check that SQL_BIG_SELECTS is 0. */ /* Check that SQL_BIG_SELECTS is 0. */
...@@ -16945,8 +16954,8 @@ static void test_bug20023() ...@@ -16945,8 +16954,8 @@ static void test_bug20023()
/* Query SQL_BIG_SELECTS. */ /* Query SQL_BIG_SELECTS. */
bug20023_query_int_variable(&con, query_int_variable(&con,
"session.sql_big_selects", "@@session.sql_big_selects",
&sql_big_selects_4); &sql_big_selects_4);
/* Check that SQL_BIG_SELECTS is 1. */ /* Check that SQL_BIG_SELECTS is 1. */
...@@ -16974,8 +16983,8 @@ static void test_bug20023() ...@@ -16974,8 +16983,8 @@ static void test_bug20023()
/* Query SQL_BIG_SELECTS. */ /* Query SQL_BIG_SELECTS. */
bug20023_query_int_variable(&con, query_int_variable(&con,
"session.sql_big_selects", "@@session.sql_big_selects",
&sql_big_selects_5); &sql_big_selects_5);
/* Check that SQL_BIG_SELECTS is 1. */ /* Check that SQL_BIG_SELECTS is 1. */
...@@ -16989,6 +16998,98 @@ static void test_bug20023() ...@@ -16989,6 +16998,98 @@ static void test_bug20023()
mysql_close(&con); mysql_close(&con);
} }
static void bug31418_impl()
{
MYSQL con;
bool is_null;
int rc;
/* Create a new connection. */
DIE_UNLESS(mysql_init(&con));
DIE_UNLESS(mysql_real_connect(&con,
opt_host,
opt_user,
opt_password,
opt_db ? opt_db : "test",
opt_port,
opt_unix_socket,
CLIENT_FOUND_ROWS));
/***********************************************************************
Check that lock is free:
- IS_FREE_LOCK() should return 1;
- IS_USED_LOCK() should return NULL;
***********************************************************************/
is_null= query_int_variable(&con,
"IS_FREE_LOCK('bug31418')",
&rc);
DIE_UNLESS(!is_null && rc);
is_null= query_int_variable(&con,
"IS_USED_LOCK('bug31418')",
&rc);
DIE_UNLESS(is_null);
/***********************************************************************
Acquire lock and check the lock status (the lock must be in use):
- IS_FREE_LOCK() should return 0;
- IS_USED_LOCK() should return non-zero thread id;
***********************************************************************/
query_int_variable(&con, "GET_LOCK('bug31418', 1)", &rc);
DIE_UNLESS(rc);
is_null= query_int_variable(&con,
"IS_FREE_LOCK('bug31418')",
&rc);
DIE_UNLESS(!is_null && !rc);
is_null= query_int_variable(&con,
"IS_USED_LOCK('bug31418')",
&rc);
DIE_UNLESS(!is_null && rc);
/***********************************************************************
Issue COM_CHANGE_USER command and check the lock status
(the lock must be free):
- IS_FREE_LOCK() should return 1;
- IS_USED_LOCK() should return NULL;
**********************************************************************/
bug20023_change_user(&con);
is_null= query_int_variable(&con,
"IS_FREE_LOCK('bug31418')",
&rc);
DIE_UNLESS(!is_null && rc);
is_null= query_int_variable(&con,
"IS_USED_LOCK('bug31418')",
&rc);
DIE_UNLESS(is_null);
/***********************************************************************
That's it. Cleanup.
***********************************************************************/
mysql_close(&con);
}
static void test_bug31418()
{
/* Run test case for BUG#31418 for three different connections. */
bug31418_impl();
bug31418_impl();
bug31418_impl();
}
/* /*
Read and parse arguments and MySQL options from my.cnf Read and parse arguments and MySQL options from my.cnf
...@@ -17286,6 +17387,7 @@ static struct my_tests_st my_tests[]= { ...@@ -17286,6 +17387,7 @@ static struct my_tests_st my_tests[]= {
{ "test_change_user", test_change_user }, { "test_change_user", test_change_user },
{ "test_bug30472", test_bug30472 }, { "test_bug30472", test_bug30472 },
{ "test_bug20023", test_bug20023 }, { "test_bug20023", test_bug20023 },
{ "test_bug31418", test_bug31418 },
{ 0, 0 } { 0, 0 }
}; };
......
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