#At file:///Users/mattiasj/clones/bzrroot/topush-60-bugteam/ based on revid:serge.kozlov@stripped
3069 Mattias Jonsson 2009-02-20 [merge]
merge from mysql-5.1-bugteam to mysql-6.0-bugteam
added:
mysql-test/include/wait_show_condition.inc
mysql-test/r/innodb_bug42419.result
mysql-test/t/innodb_bug42419.test
modified:
mysql-test/r/query_cache_debug.result
mysql-test/suite/parts/r/partition_auto_increment_ndb.result
mysql-test/t/innodb_mysql.test
mysql-test/t/partition.test
mysql-test/t/query_cache_debug.test
sql/item.cc
sql/sql_cache.cc
sql/sql_insert.cc
sql/sql_select.cc
storage/myisam/ha_myisam.cc
storage/myisam/mi_locking.c
storage/myisam/myisamdef.h
=== added file 'mysql-test/include/wait_show_condition.inc'
--- a/mysql-test/include/wait_show_condition.inc 1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/wait_show_condition.inc 2009-02-20 09:12:06 +0000
@@ -0,0 +1,78 @@
+# include/wait_show_condition.inc
+#
+# SUMMARY
+#
+# Waits until the show statement ($show_statement) has at least within one of
+# the rows of the result set for the field ($field) a value which fulfils
+# a condition ($condition), or the operation times out.
+#
+#
+# USAGE
+#
+# let $show_statement= SHOW PROCESSLIST;
+# let $field= State;
+# let $condition= = 'Updating';
+# --source include/wait_show_condition.inc
+#
+# OR
+#
+# let $wait_timeout= 60; # Override default of 30 seconds with 60.
+# let $show_statement= SHOW PROCESSLIST;
+# let $field= State;
+# let $condition= = 'Updating';
+# --source include/wait_show_condition.inc
+#
+# Please do not use this use routine if you can replace the SHOW statement
+# with a select. In such a case include/wait_condition.inc is recommended.
+#
+# Created: 2009-02-18 mleich
+#
+
+let $max_run_time= 30;
+if ($wait_timeout)
+{
+ let $max_run_time= $wait_timeout;
+}
+# Reset $wait_timeout so that its value won't be used on subsequent
+# calls, and default will be used instead.
+let $wait_timeout= 0;
+
+# The smallest timespan till UNIX_TIMESTAMP() gets incremented is ~0 seconds.
+# We add one second to avoid the case that somebody measures timespans on a
+# real clock with fractions of seconds, detects that n seconds are sufficient,
+# assigns n to this routine and suffers because he sometimes gets n - 1
+# seconds in reality.
+inc $max_run_time;
+
+let $found= 0;
+let $max_end_time= `SELECT UNIX_TIMESTAMP() + $max_run_time`;
+while (`SELECT UNIX_TIMESTAMP() <= $max_end_time AND $found = 0`)
+{
+ # Sleep a bit to avoid too heavy load.
+ real_sleep 0.2;
+ let $rowno= 1;
+ let $process_result= 1;
+ while (`SELECT $process_result = 1 AND $found = 0`)
+ {
+ let $field_value= query_get_value($show_statement, $field, $rowno);
+ if (`SELECT '$field_value' $condition`)
+ {
+ let $found= 1;
+ }
+ if (`SELECT '$field_value' = 'No such row'`)
+ {
+ # We are behind the last row of the result set.
+ let $process_result= 0;
+ }
+ inc $rowno;
+ }
+}
+if (!$found)
+{
+ echo # Timeout in include/wait_show_condition.inc for $wait_condition;
+ echo # show_statement : $show_statement;
+ echo # field : $field;
+ echo # condition : $condition;
+ echo # max_run_time : $max_run_time;
+}
+
=== added file 'mysql-test/r/innodb_bug42419.result'
--- a/mysql-test/r/innodb_bug42419.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/innodb_bug42419.result 2009-02-20 09:12:06 +0000
@@ -0,0 +1,17 @@
+CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b INT) ENGINE = InnoDB;
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+COMMIT;
+SET AUTOCOMMIT = 0;
+CREATE TEMPORARY TABLE t1_tmp ( b INT );
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 3;
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 2;
+SET AUTOCOMMIT = 0;
+CREATE TEMPORARY TABLE t2_tmp ( a int, new_a int );
+INSERT INTO t2_tmp VALUES (1,51),(2,52),(3,53);
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 1;
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 2;
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 1;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+Reap the server message for connection user2 UPDATE t1 ...
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 3;
+DROP TABLE t1;
=== modified file 'mysql-test/r/query_cache_debug.result'
--- a/mysql-test/r/query_cache_debug.result 2008-04-01 22:43:17 +0000
+++ b/mysql-test/r/query_cache_debug.result 2009-02-19 21:09:35 +0000
@@ -22,3 +22,52 @@ Qcache_queries_in_cache 0
set global query_cache_size= 0;
use test;
drop table t1;
+SET @old_concurrent_insert= @@GLOBAL.concurrent_insert;
+SET @old_query_cache_size= @@GLOBAL.query_cache_size;
+DROP TABLE IF EXISTS t1, t2;
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+SET GLOBAL concurrent_insert= 1;
+SET GLOBAL query_cache_size= 1024*512;
+SET GLOBAL query_cache_type= ON;
+# Switch to connection con1
+SET SESSION debug='+d,wait_after_query_cache_invalidate';
+# Send concurrent insert, will wait in the query cache table invalidate
+INSERT INTO t1 VALUES (4);
+# Switch to connection default
+# Wait for concurrent insert to reach the debug point
+# Switch to connection con2
+# Send SELECT that shouldn't be cached
+SELECT * FROM t1;
+a
+1
+2
+3
+# Switch to connection default
+# Notify the concurrent insert to proceed
+SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST
+WHERE STATE = 'wait_after_query_cache_invalidate' INTO @thread_id;
+KILL QUERY @thread_id;
+# Switch to connection con1
+# Gather insert result
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 0
+# Test that it's cacheable
+SELECT * FROM t1;
+a
+1
+2
+3
+4
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 1
+# Disconnect
+# Restore defaults
+RESET QUERY CACHE;
+DROP TABLE t1,t2;
+SET GLOBAL concurrent_insert= DEFAULT;
+SET GLOBAL query_cache_size= DEFAULT;
+SET GLOBAL query_cache_type= DEFAULT;
=== modified file 'mysql-test/suite/parts/r/partition_auto_increment_ndb.result'
--- a/mysql-test/suite/parts/r/partition_auto_increment_ndb.result 2009-02-11 12:11:20 +0000
+++ b/mysql-test/suite/parts/r/partition_auto_increment_ndb.result 2009-02-20 12:37:37 +0000
@@ -649,6 +649,7 @@ INSERT INTO t1 VALUES (3, NULL);
INSERT INTO t1 VALUES (3, NULL), (2, 0), (2, NULL);
INSERT INTO t1 VALUES (2, 2);
# ERROR (only OK if Blackhole/NDB) should give ER_DUP_KEY or ER_DUP_ENTRY
+# mysql_errno: 0
INSERT INTO t1 VALUES (2, 22), (2, NULL);
SELECT * FROM t1 ORDER BY c1,c2;
c1 c2
=== added file 'mysql-test/t/innodb_bug42419.test'
--- a/mysql-test/t/innodb_bug42419.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/innodb_bug42419.test 2009-02-20 09:12:06 +0000
@@ -0,0 +1,77 @@
+#
+# Testcase for InnoDB
+# Bug#42419 Server crash with "Pure virtual method called" on two concurrent connections
+#
+
+--source include/have_innodb.inc
+
+let $innodb_lock_wait_timeout= query_get_value(SHOW VARIABLES LIKE 'innodb_lock_wait_timeout%', Value, 1);
+if (`SELECT $innodb_lock_wait_timeout < 10`)
+{
+ --echo # innodb_lock_wait_timeout must be >= 10 seconds
+ --echo # so that this test can work all time fine on an overloaded testing box
+ SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
+ exit;
+}
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+# First session
+connection default;
+
+
+--enable_warnings
+CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b INT) ENGINE = InnoDB;
+
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+COMMIT;
+SET AUTOCOMMIT = 0;
+
+CREATE TEMPORARY TABLE t1_tmp ( b INT );
+
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 3;
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 2;
+
+# Second session
+connect (user2,localhost,root,,,$MASTER_MYPORT,$MASTER_MYSOCK);
+
+SET AUTOCOMMIT = 0;
+
+CREATE TEMPORARY TABLE t2_tmp ( a int, new_a int );
+INSERT INTO t2_tmp VALUES (1,51),(2,52),(3,53);
+
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 1;
+send
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 2;
+
+# The last update will wait for a lock held by the first session
+
+# First session
+connection default;
+
+# Poll till the UPDATE of the second session waits for lock
+let $show_statement= SHOW PROCESSLIST;
+let $field= State;
+let $condition= = 'Updating';
+--source include/wait_show_condition.inc
+
+# If the testing box is overloadeded and innodb_lock_wait_timeout is too small
+# we might get here ER_LOCK_WAIT_TIMEOUT.
+--error ER_LOCK_DEADLOCK
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 1;
+
+# Second session
+connection user2;
+--echo Reap the server message for connection user2 UPDATE t1 ...
+reap;
+
+# The server crashed when executing this UPDATE or the succeeding SQL command.
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 3;
+
+connection default;
+disconnect user2;
+DROP TABLE t1;
+
+# Wait till all disconnects are completed
+--source include/wait_until_count_sessions.inc
=== modified file 'mysql-test/t/innodb_mysql.test'
--- a/mysql-test/t/innodb_mysql.test 2009-02-12 18:27:05 +0000
+++ b/mysql-test/t/innodb_mysql.test 2009-02-20 12:37:37 +0000
@@ -91,6 +91,7 @@ INSERT INTO foo2 SELECT * FROM foo;
DROP TABLE foo, bar, foo2;
+
#
# Bug#41348: INSERT INTO tbl SELECT * FROM temp_tbl overwrites locking type of temp table
#
=== modified file 'mysql-test/t/partition.test'
--- a/mysql-test/t/partition.test 2009-02-13 16:30:54 +0000
+++ b/mysql-test/t/partition.test 2009-02-20 12:37:37 +0000
@@ -19,7 +19,6 @@ drop table if exists t1, t2;
#
# Bug#36001: Partitions: spelling and using some error messages
#
-
--error ER_FOREIGN_KEY_ON_PARTITIONED
CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a))
ENGINE=MyISAM
=== modified file 'mysql-test/t/query_cache_debug.test'
--- a/mysql-test/t/query_cache_debug.test 2008-10-20 09:16:47 +0000
+++ b/mysql-test/t/query_cache_debug.test 2009-02-20 12:37:37 +0000
@@ -45,3 +45,71 @@ set global query_cache_size= 0;
use test;
drop table t1;
+#
+# Bug#41098: Query Cache returns wrong result with concurrent insert
+#
+
+SET @old_concurrent_insert= @@GLOBAL.concurrent_insert;
+SET @old_query_cache_size= @@GLOBAL.query_cache_size;
+
+--disable_warnings
+DROP TABLE IF EXISTS t1, t2;
+--enable_warnings
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+
+SET GLOBAL concurrent_insert= 1;
+SET GLOBAL query_cache_size= 1024*512;
+SET GLOBAL query_cache_type= ON;
+
+connect(con1,localhost,root,,test,,);
+connect(con2,localhost,root,,test,,);
+
+connection con1;
+--echo # Switch to connection con1
+SET SESSION debug='+d,wait_after_query_cache_invalidate';
+--echo # Send concurrent insert, will wait in the query cache table invalidate
+--send INSERT INTO t1 VALUES (4)
+
+connection default;
+--echo # Switch to connection default
+--echo # Wait for concurrent insert to reach the debug point
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE STATE = "wait_after_query_cache_invalidate" AND
+ INFO = "INSERT INTO t1 VALUES (4)";
+--source include/wait_condition.inc
+
+connection con2;
+--echo # Switch to connection con2
+--echo # Send SELECT that shouldn't be cached
+SELECT * FROM t1;
+
+connection default;
+--echo # Switch to connection default
+--echo # Notify the concurrent insert to proceed
+SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE STATE = 'wait_after_query_cache_invalidate' INTO @thread_id;
+KILL QUERY @thread_id;
+
+connection con1;
+--echo # Switch to connection con1
+--echo # Gather insert result
+--reap
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+--echo # Test that it's cacheable
+SELECT * FROM t1;
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+
+--echo # Disconnect
+disconnect con1;
+disconnect con2;
+
+connection default;
+--echo # Restore defaults
+RESET QUERY CACHE;
+DROP TABLE t1,t2;
+SET GLOBAL concurrent_insert= DEFAULT;
+SET GLOBAL query_cache_size= DEFAULT;
+SET GLOBAL query_cache_type= DEFAULT;
=== modified file 'sql/item.cc'
--- a/sql/item.cc 2009-02-19 14:48:28 +0000
+++ b/sql/item.cc 2009-02-20 12:37:37 +0000
@@ -1765,7 +1765,6 @@ bool agg_item_set_converter(DTCollation
bool agg_item_charsets(DTCollation &coll, const char *fname,
Item **args, uint nargs, uint flags, int item_sep)
{
- Item **arg, *safe_args[2];
if (agg_item_collations(coll, fname, args, nargs, flags, item_sep))
return TRUE;
=== modified file 'sql/sql_cache.cc'
--- a/sql/sql_cache.cc 2009-02-13 16:30:54 +0000
+++ b/sql/sql_cache.cc 2009-02-20 12:37:37 +0000
@@ -1540,6 +1540,9 @@ void Query_cache::invalidate(THD *thd, T
invalidate_table(thd, tables_used);
}
+ DBUG_EXECUTE_IF("wait_after_query_cache_invalidate",
+ debug_wait_for_kill("wait_after_query_cache_invalidate"););
+
DBUG_VOID_RETURN;
}
=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc 2009-02-16 21:18:45 +0000
+++ b/sql/sql_insert.cc 2009-02-20 12:37:37 +0000
@@ -928,20 +928,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
}
DBUG_ASSERT(transactional_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
-
- if (thd->lock)
- {
- /*
- Invalidate the table in the query cache if something changed
- after unlocking when changes become fisible.
- TODO: this is workaround. right way will be move invalidating in
- the unlock procedure.
- */
- if (lock_type == TL_WRITE_CONCURRENT_INSERT && changed)
- {
- query_cache_invalidate3(thd, table_list, 1);
- }
- }
}
thd_proc_info(thd, "end");
/*
=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc 2009-02-16 21:18:45 +0000
+++ b/sql/sql_select.cc 2009-02-20 12:37:37 +0000
@@ -3995,11 +3995,12 @@ static uint get_tmp_table_rec_length(Lis
*/
static bool
-make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
+make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
DYNAMIC_ARRAY *keyuse_array)
{
int error;
TABLE *table;
+ TABLE_LIST *tables= tables_arg;
uint i,table_count,const_count,key;
table_map found_const_table_map, all_table_map, found_ref, refs;
key_map const_ref, eq_part;
@@ -4039,7 +4040,7 @@ make_join_statistics(JOIN *join, TABLE_L
if ((error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK)))
{
table->file->print_error(error, MYF(0));
- DBUG_RETURN(1);
+ goto error;
}
table->quick_keys.clear_all();
table->reginfo.join_tab=s;
@@ -4135,7 +4136,7 @@ make_join_statistics(JOIN *join, TABLE_L
{
join->tables=0; // Don't use join->table
my_message(ER_WRONG_OUTER_JOIN, ER(ER_WRONG_OUTER_JOIN), MYF(0));
- DBUG_RETURN(1);
+ goto error;
}
s->key_dependent= s->dependent;
}
@@ -4145,7 +4146,7 @@ make_join_statistics(JOIN *join, TABLE_L
if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables,
conds, join->cond_equal,
~outer_join, join->select_lex, &sargables))
- DBUG_RETURN(1);
+ goto error;
/* Read tables with 0 or 1 rows (system tables) */
join->const_table_map= 0;
@@ -4161,7 +4162,7 @@ make_join_statistics(JOIN *join, TABLE_L
if ((tmp=join_read_const_table(s, p_pos)))
{
if (tmp > 0)
- DBUG_RETURN(1); // Fatal error
+ goto error; // Fatal error
}
else
found_const_table_map|= s->table->map;
@@ -4233,7 +4234,7 @@ make_join_statistics(JOIN *join, TABLE_L
if ((tmp= join_read_const_table(s, join->positions+const_count-1)))
{
if (tmp > 0)
- DBUG_RETURN(1); // Fatal error
+ goto error; // Fatal error
}
else
found_const_table_map|= table->map;
@@ -4289,12 +4290,12 @@ make_join_statistics(JOIN *join, TABLE_L
set_position(join,const_count++,s,start_keyuse);
if (create_ref_for_key(join, s, start_keyuse,
found_const_table_map))
- DBUG_RETURN(1);
+ goto error;
if ((tmp=join_read_const_table(s,
join->positions+const_count-1)))
{
if (tmp > 0)
- DBUG_RETURN(1); // Fatal error
+ goto error; // Fatal error
}
else
found_const_table_map|= table->map;
@@ -4381,7 +4382,7 @@ make_join_statistics(JOIN *join, TABLE_L
*s->on_expr_ref ? *s->on_expr_ref : conds,
1, &error);
if (!select)
- DBUG_RETURN(1);
+ goto error;
records= get_quick_record_count(join->thd, select, s->table,
&s->const_keys, join->row_limit);
s->quick=select->quick;
@@ -4432,7 +4433,7 @@ make_join_statistics(JOIN *join, TABLE_L
if (join->const_tables != join->tables)
{
if (choose_plan(join, all_table_map & ~join->const_table_map))
- DBUG_RETURN(TRUE);
+ goto error;
}
else
{
@@ -4442,6 +4443,17 @@ make_join_statistics(JOIN *join, TABLE_L
}
/* Generate an execution plan from the found optimal join order. */
DBUG_RETURN(join->thd->killed || get_best_combination(join));
+
+error:
+ /*
+ Need to clean up join_tab from TABLEs in case of error.
+ They won't get cleaned up by JOIN::cleanup() because JOIN::join_tab
+ may not be assigned yet by this function (which is building join_tab).
+ Dangling TABLE::reginfo.join_tab may cause part_of_refkey to choke.
+ */
+ for (tables= tables_arg; tables; tables= tables->next_leaf)
+ tables->table->reginfo.join_tab= NULL;
+ DBUG_RETURN (1);
}
=== modified file 'storage/myisam/ha_myisam.cc'
--- a/storage/myisam/ha_myisam.cc 2009-02-11 12:11:20 +0000
+++ b/storage/myisam/ha_myisam.cc 2009-02-20 12:37:37 +0000
@@ -2162,6 +2162,15 @@ my_bool ha_myisam::register_query_cache_
}
}
+ /*
+ This query execution might have started after the query cache was flushed
+ by a concurrent INSERT. In this case, don't cache this statement as the
+ data file length difference might not be visible yet if the tables haven't
+ been unlocked by the concurrent insert thread.
+ */
+ if (file->state->uncacheable)
+ DBUG_RETURN(FALSE);
+
/* It is ok to try to cache current statement. */
DBUG_RETURN(TRUE);
}
=== modified file 'storage/myisam/mi_locking.c'
--- a/storage/myisam/mi_locking.c 2008-10-31 18:02:34 +0000
+++ b/storage/myisam/mi_locking.c 2009-02-20 12:37:37 +0000
@@ -287,6 +287,8 @@ void mi_get_status(void* param, my_bool
info->save_state=info->s->state.state;
info->state= &info->save_state;
info->append_insert_at_end= concurrent_insert;
+ if (concurrent_insert)
+ info->s->state.state.uncacheable= TRUE;
DBUG_VOID_RETURN;
}
=== modified file 'storage/myisam/myisamdef.h'
--- a/storage/myisam/myisamdef.h 2009-02-13 16:30:54 +0000
+++ b/storage/myisam/myisamdef.h 2009-02-20 12:37:37 +0000
@@ -43,6 +43,7 @@ typedef struct st_mi_status_info
my_off_t key_file_length;
my_off_t data_file_length;
ha_checksum checksum;
+ my_bool uncacheable; /* Active concurrent insert */
} MI_STATUS_INFO;
typedef struct st_mi_state_info
Attachment: [text/bzr-bundle] bzr/mattias.jonsson@sun.com-20090220123737-au6r3dcaette6t01.bundle
| Thread |
|---|
| • bzr commit into mysql-6.0-bugteam branch (mattias.jonsson:3069) | Mattias Jonsson | 20 Feb |