Commit 55a149f2 authored by Satya B's avatar Satya B

merge to latest 5.0-bugteam

parents b1d74c9b 10350e20
...@@ -208,10 +208,17 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) ...@@ -208,10 +208,17 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys)
This segment will be reallocated after construction of the tables. This segment will be reallocated after construction of the tables.
*/ */
length=(uint) (elements*2+trees*(1 << myisam_quick_table_bits)); length=(uint) (elements*2+trees*(1 << myisam_quick_table_bits));
/*
To keep some algorithms simpler, we accept that they access
bytes beyond the end of the input data. This can affect up to
one byte less than the "word size" size used in this file,
which is BITS_SAVED / 8. To avoid accessing non-allocated
data, we add (BITS_SAVED / 8) - 1 bytes to the buffer size.
*/
if (!(share->decode_tables=(uint16*) if (!(share->decode_tables=(uint16*)
my_malloc((length + OFFSET_TABLE_SIZE) * sizeof(uint16) + my_malloc((length + OFFSET_TABLE_SIZE) * sizeof(uint16) +
(uint) (share->pack.header_length - sizeof(header)), (uint) (share->pack.header_length - sizeof(header) +
MYF(MY_WME | MY_ZEROFILL)))) (BITS_SAVED / 8) - 1), MYF(MY_WME | MY_ZEROFILL))))
goto err1; goto err1;
tmp_buff=share->decode_tables+length; tmp_buff=share->decode_tables+length;
disk_cache=(byte*) (tmp_buff+OFFSET_TABLE_SIZE); disk_cache=(byte*) (tmp_buff+OFFSET_TABLE_SIZE);
...@@ -1430,31 +1437,6 @@ static void fill_buffer(MI_BIT_BUFF *bit_buff) ...@@ -1430,31 +1437,6 @@ static void fill_buffer(MI_BIT_BUFF *bit_buff)
bit_buff->current_byte=0; bit_buff->current_byte=0;
return; return;
} }
else
{
uint len= 0;
uint i= 0;
/*
Check if the remaining buffer/record to read is less than the word size.
If so read byte by byte
Note: if this branch becomes a bottleneck it can be removed, assuming
that the second memory segment allocates 7 extra bytes (see
_mi_read_pack_info()).
*/
len= bit_buff->end - bit_buff->pos;
if (len < (BITS_SAVED / 8))
{
bit_buff->current_byte= 0;
for (i=0 ; i < len ; i++)
{
bit_buff->current_byte+= (((uint) ((uchar) bit_buff->pos[len - i - 1]))
<< (8 * i));
}
bit_buff->pos= bit_buff->end;
return;
}
}
#if BITS_SAVED == 64 #if BITS_SAVED == 64
bit_buff->current_byte= ((((uint) ((uchar) bit_buff->pos[7]))) + bit_buff->current_byte= ((((uint) ((uchar) bit_buff->pos[7]))) +
......
...@@ -133,3 +133,58 @@ ALTER TABLE t1 ADD COLUMN a INT; ...@@ -133,3 +133,58 @@ ALTER TABLE t1 ADD COLUMN a INT;
# 2.2.1. normal mode # 2.2.1. normal mode
# 2.2.2. PS mode # 2.2.2. PS mode
DROP TABLE t1; DROP TABLE t1;
create table t1 (a int);
create table t2 like t1;
# con1
lock tables t1 write;
# con2
flush tables with read lock;
# con5
# global read lock is taken
# con3
select * from t2 for update;
# waiting for release of read lock
# con4
# would hang and later cause a deadlock
flush tables t2;
# clean up
unlock tables;
unlock tables;
a
drop table t1,t2;
#
# Lightweight version:
# Ensure that the wait for a GRL is done before opening tables.
#
create table t1 (a int);
create table t2 like t1;
#
# UPDATE
#
# default
flush tables with read lock;
# con1
update t2 set a = 1;
# default
# statement is waiting for release of read lock
# con2
flush table t2;
# default
unlock tables;
# con1
#
# LOCK TABLES .. WRITE
#
# default
flush tables with read lock;
# con1
lock tables t2 write;
# default
# statement is waiting for release of read lock
# con2
flush table t2;
# default
unlock tables;
# con1
unlock tables;
drop table t1,t2;
...@@ -38,3 +38,25 @@ SELECT COUNT(*) FROM t1; ...@@ -38,3 +38,25 @@ SELECT COUNT(*) FROM t1;
COUNT(*) COUNT(*)
1024 1024
DROP TABLE t1; DROP TABLE t1;
#
# Bug #43973 - backup_myisam.test fails on 6.0-bugteam
#
CREATE DATABASE mysql_db1;
CREATE TABLE mysql_db1.t1 (c1 VARCHAR(5), c2 int);
CREATE INDEX i1 ON mysql_db1.t1 (c1, c2);
INSERT INTO mysql_db1.t1 VALUES ('A',1);
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
FLUSH TABLE mysql_db1.t1;
# Compress the table using MYISAMPACK tool
# Run MYISAMCHK tool on the compressed table
SELECT COUNT(*) FROM mysql_db1.t1 WHERE c2 < 5;
COUNT(*)
128
DROP TABLE mysql_db1.t1;
DROP DATABASE mysql_db1;
...@@ -683,6 +683,134 @@ DROP TABLE t1; ...@@ -683,6 +683,134 @@ DROP TABLE t1;
--disconnect locker --disconnect locker
--disconnect writer --disconnect writer
#
# Bug#43230: SELECT ... FOR UPDATE can hang with FLUSH TABLES WITH READ LOCK indefinitely
#
connect (con1,localhost,root,,);
connect (con2,localhost,root,,);
connect (con3,localhost,root,,);
connect (con4,localhost,root,,);
connect (con5,localhost,root,,);
create table t1 (a int);
create table t2 like t1;
connection con1;
--echo # con1
lock tables t1 write;
connection con2;
--echo # con2
send flush tables with read lock;
connection con5;
--echo # con5
let $show_statement= SHOW PROCESSLIST;
let $field= State;
let $condition= = 'Flushing tables';
--source include/wait_show_condition.inc
--echo # global read lock is taken
connection con3;
--echo # con3
send select * from t2 for update;
connection con5;
let $show_statement= SHOW PROCESSLIST;
let $field= State;
let $condition= = 'Waiting for release of readlock';
--source include/wait_show_condition.inc
--echo # waiting for release of read lock
connection con4;
--echo # con4
--echo # would hang and later cause a deadlock
flush tables t2;
connection con1;
--echo # clean up
unlock tables;
connection con2;
--reap
unlock tables;
connection con3;
--reap
connection default;
disconnect con5;
disconnect con4;
disconnect con3;
disconnect con2;
disconnect con1;
drop table t1,t2;
--echo #
--echo # Lightweight version:
--echo # Ensure that the wait for a GRL is done before opening tables.
--echo #
connect (con1,localhost,root,,);
connect (con2,localhost,root,,);
create table t1 (a int);
create table t2 like t1;
--echo #
--echo # UPDATE
--echo #
connection default;
--echo # default
flush tables with read lock;
connection con1;
--echo # con1
send update t2 set a = 1;
connection default;
--echo # default
let $show_statement= SHOW PROCESSLIST;
let $field= State;
let $condition= = 'Waiting for release of readlock';
--source include/wait_show_condition.inc
--echo # statement is waiting for release of read lock
connection con2;
--echo # con2
flush table t2;
connection default;
--echo # default
unlock tables;
connection con1;
--echo # con1
--reap
--echo #
--echo # LOCK TABLES .. WRITE
--echo #
connection default;
--echo # default
flush tables with read lock;
connection con1;
--echo # con1
send lock tables t2 write;
connection default;
--echo # default
let $show_statement= SHOW PROCESSLIST;
let $field= State;
let $condition= = 'Waiting for release of readlock';
--source include/wait_show_condition.inc
--echo # statement is waiting for release of read lock
connection con2;
--echo # con2
flush table t2;
connection default;
--echo # default
unlock tables;
connection con1;
--echo # con1
--reap
unlock tables;
connection default;
disconnect con2;
disconnect con1;
drop table t1,t2;
# End of 5.0 tests # End of 5.0 tests
# Wait till all disconnects are completed # Wait till all disconnects are completed
......
...@@ -50,3 +50,28 @@ FLUSH TABLE t1; ...@@ -50,3 +50,28 @@ FLUSH TABLE t1;
--exec $MYISAMPACK $MYSQLTEST_VARDIR/master-data/test/t1 --exec $MYISAMPACK $MYSQLTEST_VARDIR/master-data/test/t1
SELECT COUNT(*) FROM t1; SELECT COUNT(*) FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # Bug #43973 - backup_myisam.test fails on 6.0-bugteam
--echo #
CREATE DATABASE mysql_db1;
CREATE TABLE mysql_db1.t1 (c1 VARCHAR(5), c2 int);
CREATE INDEX i1 ON mysql_db1.t1 (c1, c2);
INSERT INTO mysql_db1.t1 VALUES ('A',1);
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
INSERT INTO mysql_db1.t1 SELECT * FROM mysql_db1.t1;
FLUSH TABLE mysql_db1.t1;
#
--echo # Compress the table using MYISAMPACK tool
--exec $MYISAMPACK -s $MYSQLTEST_VARDIR/master-data/mysql_db1/t1
--echo # Run MYISAMCHK tool on the compressed table
--exec $MYISAMCHK -srq $MYSQLTEST_VARDIR/master-data/mysql_db1/t1
SELECT COUNT(*) FROM mysql_db1.t1 WHERE c2 < 5;
#
DROP TABLE mysql_db1.t1;
DROP DATABASE mysql_db1;
...@@ -293,12 +293,7 @@ sub start_mysqlds() ...@@ -293,12 +293,7 @@ sub start_mysqlds()
@groups = &find_groups($groupids); @groups = &find_groups($groupids);
for ($i = 0; defined($groups[$i]); $i++) for ($i = 0; defined($groups[$i]); $i++)
{ {
# Defaults are made explicit parameters to server execution...
@options = defaults_for_group($groups[$i]); @options = defaults_for_group($groups[$i]);
# ...so server MUST NOT try to read again from some config file, especially
# as the "right" file may be unknown to the server if we are using
# --defaults-file=... params in here.
unshift(@options,"--no-defaults");
$mysqld_found= 1; # The default $mysqld_found= 1; # The default
$mysqld_found= 0 if (!length($mysqld)); $mysqld_found= 0 if (!length($mysqld));
......
...@@ -204,6 +204,7 @@ void lex_start(THD *thd) ...@@ -204,6 +204,7 @@ void lex_start(THD *thd)
lex->nest_level=0 ; lex->nest_level=0 ;
lex->allow_sum_func= 0; lex->allow_sum_func= 0;
lex->in_sum_func= NULL; lex->in_sum_func= NULL;
lex->protect_against_global_read_lock= FALSE;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -1176,6 +1176,22 @@ typedef struct st_lex : public Query_tables_list ...@@ -1176,6 +1176,22 @@ typedef struct st_lex : public Query_tables_list
bool escape_used; bool escape_used;
/*
Special case for SELECT .. FOR UPDATE and LOCK TABLES .. WRITE.
Protect from a impending GRL as otherwise the thread might deadlock
if it starts waiting for the GRL in mysql_lock_tables.
The protection is needed because there is a race between setting
the global read lock and waiting for all open tables to be closed.
The problem is a circular wait where a thread holding "old" open
tables will wait for the global read lock to be released while the
thread holding the global read lock will wait for all "old" open
tables to be closed -- the flush part of flush tables with read
lock.
*/
bool protect_against_global_read_lock;
st_lex(); st_lex();
virtual ~st_lex() virtual ~st_lex()
......
...@@ -2800,6 +2800,10 @@ mysql_execute_command(THD *thd) ...@@ -2800,6 +2800,10 @@ mysql_execute_command(THD *thd)
if (res) if (res)
goto error; goto error;
if (!thd->locked_tables && lex->protect_against_global_read_lock &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error;
if (!(res= open_and_lock_tables(thd, all_tables))) if (!(res= open_and_lock_tables(thd, all_tables)))
{ {
if (lex->describe) if (lex->describe)
...@@ -3660,6 +3664,9 @@ end_with_restore_list: ...@@ -3660,6 +3664,9 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (update_precheck(thd, all_tables)) if (update_precheck(thd, all_tables))
break; break;
if (!thd->locked_tables &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error;
DBUG_ASSERT(select_lex->offset_limit == 0); DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex); unit->set_limit(select_lex);
res= (up_result= mysql_update(thd, all_tables, res= (up_result= mysql_update(thd, all_tables,
...@@ -3686,6 +3693,15 @@ end_with_restore_list: ...@@ -3686,6 +3693,15 @@ end_with_restore_list:
else else
res= 0; res= 0;
/*
Protection might have already been risen if its a fall through
from the SQLCOM_UPDATE case above.
*/
if (!thd->locked_tables &&
lex->sql_command == SQLCOM_UPDATE_MULTI &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error;
res= mysql_multi_update_prepare(thd); res= mysql_multi_update_prepare(thd);
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
...@@ -3853,7 +3869,8 @@ end_with_restore_list: ...@@ -3853,7 +3869,8 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error; goto error;
} }
if (!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error;
res= mysql_truncate(thd, first_table, 0); res= mysql_truncate(thd, first_table, 0);
break; break;
case SQLCOM_DELETE: case SQLCOM_DELETE:
...@@ -4027,6 +4044,10 @@ end_with_restore_list: ...@@ -4027,6 +4044,10 @@ end_with_restore_list:
if (check_one_table_access(thd, privilege, all_tables)) if (check_one_table_access(thd, privilege, all_tables))
goto error; goto error;
if (!thd->locked_tables &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error;
res= mysql_load(thd, lex->exchange, first_table, lex->field_list, res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
lex->update_list, lex->value_list, lex->duplicates, lex->update_list, lex->value_list, lex->duplicates,
lex->ignore, (bool) lex->local_file); lex->ignore, (bool) lex->local_file);
...@@ -4082,6 +4103,9 @@ end_with_restore_list: ...@@ -4082,6 +4103,9 @@ end_with_restore_list:
goto error; goto error;
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
goto error; goto error;
if (lex->protect_against_global_read_lock &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error;
thd->in_lock_tables=1; thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK; thd->options|= OPTION_TABLE_LOCK;
......
...@@ -4522,6 +4522,7 @@ select_lock_type: ...@@ -4522,6 +4522,7 @@ select_lock_type:
LEX *lex=Lex; LEX *lex=Lex;
lex->current_select->set_lock_for_tables(TL_WRITE); lex->current_select->set_lock_for_tables(TL_WRITE);
lex->safe_to_cache_query=0; lex->safe_to_cache_query=0;
lex->protect_against_global_read_lock= TRUE;
} }
| LOCK_SYM IN_SYM SHARE_SYM MODE_SYM | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
{ {
...@@ -10058,8 +10059,12 @@ table_lock_list: ...@@ -10058,8 +10059,12 @@ table_lock_list:
table_lock: table_lock:
table_ident opt_table_alias lock_option table_ident opt_table_alias lock_option
{ {
if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3)) thr_lock_type lock_type= (thr_lock_type) $3;
if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type))
MYSQL_YYABORT; MYSQL_YYABORT;
/* If table is to be write locked, protect from a impending GRL. */
if (lock_type >= TL_WRITE_ALLOW_WRITE)
Lex->protect_against_global_read_lock= TRUE;
} }
; ;
......
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