From 52b7c6b11c81370f1306f679d1cdf20626e473aa Mon Sep 17 00:00:00 2001 From: Joerg Bruehe Date: Sat, 4 Oct 2008 18:32:23 +0200 Subject: [PATCH 01/15] Fix some bad merge that got the string "5.1-bugteam" into this 5.0 tree. .bzr-mysql/default.conf: 1) Thou shalt not have a team tree designation in this file. 2) Thou shalt not claim "5.1" in a "5.0" tree. --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index e613cefc614..f79c1cd6319 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-5.1-bugteam" +tree_name = "mysql-5.0" From f985e787758e3081e18543b5490724d30296aebf Mon Sep 17 00:00:00 2001 From: Kristofer Pettersson Date: Thu, 9 Oct 2008 09:26:42 +0200 Subject: [PATCH 02/15] Bug#24289 Status Variable "Questions" gets wrong values with Stored Routines When running Stored Routines the Status Variable "Questions" was wrongly incremented. According to the manual it should contain the "number of statements that clients have sent to the server" Introduced a new status variable 'questions' to replace the query_id variable which currently corresponds badly with the number of statements sent by the client. The new behavior is ment to be backward compatible with 4.0 and at the same time work with new features in a similar way. This is a backport from 6.0 mysql-test/r/status2.result: Added test case mysql-test/t/status2.test: Added test case sql/mysqld.cc: Introduced a new status variable 'questions' to replace the query_id variable which currently corresponds badly with the number of statements sent by the client. sql/sql_class.h: Introduced a new status variable 'questions' to replace the query_id variable which currently corresponds badly with the number of statements sent by the client. sql/sql_parse.cc: To be backward compatible with 4.0 and at the same time extend the interpretation of the Question variable, it should be increased on all COM-commands but COM_STATISTICS, COM_PING, COM_STMT_PREPARE, COM_STMT_CLOSE and COM_STMT_RESET. Since COM_QUERY can process multiple statements, there has to be an extra increase there as well. sql/sql_show.cc: Removed deprecated SHOW_QUESTION status code. sql/structs.h: Removed deprecated SHOW_QUESTION status code. --- mysql-test/r/status2.result | 64 +++++++++++++++++++++++++++++++++++++ mysql-test/t/status2.test | 64 +++++++++++++++++++++++++++++++++++++ sql/mysqld.cc | 4 ++- sql/sql_class.h | 13 ++++++-- sql/sql_parse.cc | 23 ++++++++++++- sql/sql_show.cc | 3 -- sql/structs.h | 2 +- 7 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 mysql-test/r/status2.result create mode 100644 mysql-test/t/status2.test diff --git a/mysql-test/r/status2.result b/mysql-test/r/status2.result new file mode 100644 index 00000000000..7f6eab693e5 --- /dev/null +++ b/mysql-test/r/status2.result @@ -0,0 +1,64 @@ +# +# Bug#24289 Status Variable "Questions" gets wrong values with Stored Routines +# +FLUSH STATUS; +DROP TABLE IF EXISTS t1,t2; +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; +CREATE FUNCTION f1() RETURNS INTEGER +BEGIN +DECLARE foo INTEGER; +DECLARE bar INTEGER; +SET foo=1; +SET bar=2; +RETURN foo; +END $$ +CREATE PROCEDURE p1() +BEGIN +SELECT 1; +END $$ +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +INSERT INTO t1 VALUES (1); +Assert Questions == 9 +SHOW STATUS LIKE 'Questions'; +Variable_name Value +Questions 9 +SELECT f1(); +f1() +1 +Assert Questions == 11 +SHOW STATUS LIKE 'Questions'; +Variable_name Value +Questions 11 +CALL p1(); +1 +1 +Assert Questions == 13 +SHOW STATUS LIKE 'Questions'; +Variable_name Value +Questions 13 +SELECT 1; +1 +1 +Assert Questions == 15 +SHOW STATUS LIKE 'Questions'; +Variable_name Value +Questions 15 +FLUSH STATUS; +SELECT 1; +1 +1 +Assert Questions == 16 +SHOW STATUS LIKE 'Questions'; +Variable_name Value +Questions 16 +Global status updated; Assert diff == 5 +FLUSH STATUS; +SELECT 5; +5 +5 +DROP TABLE t1,t2; +DROP PROCEDURE p1; +DROP FUNCTION f1; +End of 6.0 tests diff --git a/mysql-test/t/status2.test b/mysql-test/t/status2.test new file mode 100644 index 00000000000..b834cd0c6ad --- /dev/null +++ b/mysql-test/t/status2.test @@ -0,0 +1,64 @@ +--echo # +--echo # Bug#24289 Status Variable "Questions" gets wrong values with Stored Routines +--echo # +# The bogus connection below is needed to make the gobal statement count +# deterministic when the test is run for the first time. +connect (con1,localhost,root,,); +connection con1; +connection default; +disconnect con1; +FLUSH STATUS; +--disable_warnings +DROP TABLE IF EXISTS t1,t2; +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; +--enable_warnings +DELIMITER $$; +CREATE FUNCTION f1() RETURNS INTEGER +BEGIN + DECLARE foo INTEGER; + DECLARE bar INTEGER; + SET foo=1; + SET bar=2; + RETURN foo; +END $$ +CREATE PROCEDURE p1() +BEGIN + SELECT 1; +END $$ +DELIMITER ;$$ +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +INSERT INTO t1 VALUES (1); +--echo Assert Questions == 9 +SHOW STATUS LIKE 'Questions'; +SELECT f1(); +--echo Assert Questions == 11 +SHOW STATUS LIKE 'Questions'; +CALL p1(); +--echo Assert Questions == 13 +SHOW STATUS LIKE 'Questions'; +SELECT 1; +--echo Assert Questions == 15 +SHOW STATUS LIKE 'Questions'; +connect (con1,localhost,root,,); +connection con1; +FLUSH STATUS; +let $org_questions= `SHOW GLOBAL STATUS LIKE 'questions'`; +SELECT 1; +connection default; +disconnect con1; +--echo Assert Questions == 16 +SHOW STATUS LIKE 'Questions'; +--echo Global status updated; Assert diff == 5 +FLUSH STATUS; +let $new_questions= `SHOW GLOBAL STATUS LIKE 'questions'`; +--disable_log +let $diff= `SELECT SUBSTRING('$new_questions',10)-SUBSTRING('$org_questions',10)`; +--enable_log +eval SELECT $diff; +DROP TABLE t1,t2; +DROP PROCEDURE p1; +DROP FUNCTION f1; +--echo End of 6.0 tests + diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 757427f9fdf..c3e5449b22b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6573,7 +6573,9 @@ struct show_var_st status_vars[]= { {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_CONST}, {"Qcache_total_blocks", (char*) &query_cache.total_blocks, SHOW_LONG_CONST}, #endif /*HAVE_QUERY_CACHE*/ - {"Questions", (char*) 0, SHOW_QUESTION}, + {"Questions", (char*) offsetof(STATUS_VAR, questions), + SHOW_LONG_STATUS}, + {"Rpl_status", (char*) 0, SHOW_RPL_STATUS}, {"Select_full_join", (char*) offsetof(STATUS_VAR, select_full_join_count), SHOW_LONG_STATUS}, {"Select_full_range_join", (char*) offsetof(STATUS_VAR, select_full_range_join_count), SHOW_LONG_STATUS}, diff --git a/sql/sql_class.h b/sql/sql_class.h index a9700c9e91b..9fe0a7423de 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -664,10 +664,17 @@ typedef struct system_status_var ulong com_stmt_fetch; ulong com_stmt_reset; ulong com_stmt_close; + /* + Number of statements sent from the client + */ + ulong questions; /* - Status variables which it does not make sense to add to - global status variable counter + IMPORTANT! + SEE last_system_status_var DEFINITION BELOW. + + Below 'last_system_status_var' are all variables which doesn't make any + sense to add to the /global/ status variable counter. */ double last_query_cost; } STATUS_VAR; @@ -678,7 +685,7 @@ typedef struct system_status_var counter */ -#define last_system_status_var com_stmt_close +#define last_system_status_var questions void free_tmp_table(THD *thd, TABLE *entry); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 83b111b7c4c..da1a4a352ae 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1690,8 +1690,24 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_time(); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id= global_query_id; - if (command != COM_STATISTICS && command != COM_PING) + + switch( command ) { + /* Ignore these statements. */ + case COM_STATISTICS: + case COM_PING: + break; + /* Only increase id on these statements but don't count them. */ + case COM_STMT_PREPARE: + case COM_STMT_CLOSE: + case COM_STMT_RESET: next_query_id(); + break; + /* Increase id and count all other statements. */ + default: + statistic_increment(thd->status_var.questions, &LOCK_status); + next_query_id(); + } + thread_running++; /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -1896,6 +1912,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_length= length; thd->query= next_packet; + /* + Count each statement from the client. + */ + statistic_increment(thd->status_var.questions, &LOCK_status); + thd->query_id= next_query_id(); thd->set_time(); /* Reset the query start time. */ /* TODO: set thd->lex->sql_command to SQLCOM_END here */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c30e0a00d95..d6854e00228 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1520,9 +1520,6 @@ static bool show_status_array(THD *thd, const char *wild, nr= (long) (thd->query_start() - server_start_time); end= int10_to_str(nr, buff, 10); break; - case SHOW_QUESTION: - end= int10_to_str((long) thd->query_id, buff, 10); - break; #ifdef HAVE_REPLICATION case SHOW_RPL_STATUS: end= strmov(buff, rpl_status_type[(int)rpl_status]); diff --git a/sql/structs.h b/sql/structs.h index bc373fe4b52..ab8537612fa 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -170,7 +170,7 @@ enum SHOW_TYPE SHOW_UNDEF, SHOW_LONG, SHOW_LONGLONG, SHOW_INT, SHOW_CHAR, SHOW_CHAR_PTR, SHOW_DOUBLE_STATUS, - SHOW_BOOL, SHOW_MY_BOOL, SHOW_OPENTABLES, SHOW_STARTTIME, SHOW_QUESTION, + SHOW_BOOL, SHOW_MY_BOOL, SHOW_OPENTABLES, SHOW_STARTTIME, SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE, SHOW_SYS, SHOW_HA_ROWS, SHOW_VARS, #ifdef HAVE_OPENSSL From 8bfbcbd98172ac936ff13ef97f2662cd6bc9a2c7 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Fri, 10 Oct 2008 15:13:12 +0500 Subject: [PATCH 03/15] Bug #39283: Date returned as VARBINARY to client for queries with COALESCE and JOIN The server returned to a client the VARBINARY column type instead of the DATE type for a result of the COALESCE, IFNULL, IF, CASE, GREATEST or LEAST functions if that result was filesorted in an anonymous temporary table during the query execution. For example: SELECT COALESCE(t1.date1, t2.date2) AS result FROM t1 JOIN t2 ON t1.id = t2.id ORDER BY result; To create a column of various date/time types in a temporary table the create_tmp_field_from_item() function uses the Item::tmp_table_field_from_field_type() method call. However, fields of the MYSQL_TYPE_NEWDATE type were missed there, and the VARBINARY columns were created by default. Necessary condition has been added. mysql-test/r/metadata.result: Added test case for bug #39283. mysql-test/t/metadata.test: Added test case for bug #39283. sql/sql_select.cc: Bug #39283: Date returned as VARBINARY to client for queries with COALESCE and JOIN To create a column of various date/time types in a temporary table the create_tmp_field_from_item() function uses the Item::tmp_table_field_from_field_type() method call. However, fields of the MYSQL_TYPE_NEWDATE type were missed there, and the VARBINARY columns were created by default. Necessary condition has been added. --- mysql-test/r/metadata.result | 17 +++++++++++++++++ mysql-test/t/metadata.test | 17 +++++++++++++++++ sql/sql_select.cc | 1 + 3 files changed, 35 insertions(+) diff --git a/mysql-test/r/metadata.result b/mysql-test/r/metadata.result index a4b6fd2ce8f..5aad5874ffc 100644 --- a/mysql-test/r/metadata.result +++ b/mysql-test/r/metadata.result @@ -181,4 +181,21 @@ c1 c2 3 3 DROP VIEW v1,v2; DROP TABLE t1,t2; +CREATE TABLE t1 (i INT, d DATE); +INSERT INTO t1 VALUES (1, '2008-01-01'), (2, '2008-01-02'), (3, '2008-01-03'); +SELECT COALESCE(d, d), IFNULL(d, d), IF(i, d, d), +CASE i WHEN i THEN d ELSE d END, GREATEST(d, d), LEAST(d, d) +FROM t1 ORDER BY RAND(); +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +2008-01-01 2008-01-01 2008-01-01 2008-01-01 2008-01-01 2008-01-01 +2008-01-02 2008-01-02 2008-01-02 2008-01-02 2008-01-02 2008-01-02 +2008-01-03 2008-01-03 2008-01-03 2008-01-03 2008-01-03 2008-01-03 +COALESCE(d, d) IFNULL(d, d) IF(i, d, d) CASE i WHEN i THEN d ELSE d END GREATEST(d, d) LEAST(d, d) +def CASE i WHEN i THEN d ELSE d END CASE i WHEN i THEN d ELSE d END 10 10 10 Y 128 0 63 +def COALESCE(d, d) COALESCE(d, d) 10 10 10 Y 128 0 63 +def GREATEST(d, d) GREATEST(d, d) 10 10 10 Y 128 0 63 +def IF(i, d, d) IF(i, d, d) 10 10 10 Y 128 0 63 +def IFNULL(d, d) IFNULL(d, d) 10 10 10 Y 128 0 63 +def LEAST(d, d) LEAST(d, d) 10 10 10 Y 128 0 63 +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/metadata.test b/mysql-test/t/metadata.test index 65c062399b7..a10767579fb 100644 --- a/mysql-test/t/metadata.test +++ b/mysql-test/t/metadata.test @@ -112,4 +112,21 @@ SELECT v1.c1, v2.c2 FROM v1 JOIN v2 ON c1=c2 GROUP BY v1.c1 ORDER BY v2.c2; DROP VIEW v1,v2; DROP TABLE t1,t2; +# +# Bug #39283: Date returned as VARBINARY to client for queries +# with COALESCE and JOIN +# + +CREATE TABLE t1 (i INT, d DATE); +INSERT INTO t1 VALUES (1, '2008-01-01'), (2, '2008-01-02'), (3, '2008-01-03'); + +--enable_metadata +--sorted_result +SELECT COALESCE(d, d), IFNULL(d, d), IF(i, d, d), + CASE i WHEN i THEN d ELSE d END, GREATEST(d, d), LEAST(d, d) + FROM t1 ORDER BY RAND(); # force filesort +--disable_metadata + +DROP TABLE t1; + --echo End of 5.0 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6928effc1a5..7ff069f0996 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8964,6 +8964,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, */ if ((type= item->field_type()) == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE || + type == MYSQL_TYPE_NEWDATE || type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_GEOMETRY) new_field= item->tmp_table_field_from_field_type(table); /* From 097b60bd06e8c9dd741df30389849a17971d5052 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Fri, 10 Oct 2008 15:27:58 +0500 Subject: [PATCH 04/15] Bug #37894: Assertion in init_read_record_seq in handler.h line 1444 Select with a "NULL NOT IN" condition containing complex subselect from the same table as in the outer select failed with an assertion. The failure was caused by a concatenation of circumstances: 1) an inner select was optimized by make_join_statistics to use the QUICK_RANGE_SELECT access method (that implies an index scan of the table); 2) a subselect was independent (constant) from the outer select; 3) a condition was pushed down into inner select. During the evaluation of a constant IN expression an optimizer temporary changed the access method from index scan to table scan, but an engine handler was already initialized for index access by make_join_statistics. That caused an assertion. Unnecessary index initialization has been removed from the QUICK_RANGE_SELECT::init method (QUICK_RANGE_SELECT::reset reinvokes this initialization). mysql-test/r/subselect3.result: Added test case for bug #37894. mysql-test/t/subselect3.test: Added test case for bug #37894. sql/opt_range.cc: Bug #37894: Assertion in init_read_record_seq in handler.h line 1444 Unnecessary index initialization has been removed from the QUICK_RANGE_SELECT::init method (QUICK_RANGE_SELECT::reset reinvokes this initialization). --- mysql-test/r/subselect3.result | 16 ++++++++++++++++ mysql-test/t/subselect3.test | 22 ++++++++++++++++++++++ sql/opt_range.cc | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index 5221fa09744..97479418bec 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -779,4 +779,20 @@ SELECT 1 FROM t1 WHERE t1.a NOT IN (SELECT 1 FROM t1, t2 WHERE 0); 1 1 DROP TABLE t1, t2; +CREATE TABLE t1 ( +pk INT PRIMARY KEY, +int_key INT, +varchar_key VARCHAR(5) UNIQUE, +varchar_nokey VARCHAR(5) +); +INSERT INTO t1 VALUES (9, 7,NULL,NULL), (10,8,'p' ,'p'); +SELECT varchar_nokey +FROM t1 +WHERE NULL NOT IN ( +SELECT INNR.pk FROM t1 AS INNR2 +LEFT JOIN t1 AS INNR ON ( INNR2.int_key = INNR.int_key ) +WHERE INNR.varchar_key > 'n{' +); +varchar_nokey +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/subselect3.test b/mysql-test/t/subselect3.test index d7bb1f7186a..7e9aa1554c0 100644 --- a/mysql-test/t/subselect3.test +++ b/mysql-test/t/subselect3.test @@ -618,4 +618,26 @@ SELECT 1 FROM t1 WHERE t1.a NOT IN (SELECT 1 FROM t1, t2 WHERE 0); DROP TABLE t1, t2; +# +# Bug #37894: Assertion in init_read_record_seq in handler.h line 1444 +# + +CREATE TABLE t1 ( + pk INT PRIMARY KEY, + int_key INT, + varchar_key VARCHAR(5) UNIQUE, + varchar_nokey VARCHAR(5) +); +INSERT INTO t1 VALUES (9, 7,NULL,NULL), (10,8,'p' ,'p'); + +SELECT varchar_nokey +FROM t1 +WHERE NULL NOT IN ( + SELECT INNR.pk FROM t1 AS INNR2 + LEFT JOIN t1 AS INNR ON ( INNR2.int_key = INNR.int_key ) + WHERE INNR.varchar_key > 'n{' +); + +DROP TABLE t1; + --echo End of 5.0 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 2ae86b876d2..204ebdb6f33 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1015,7 +1015,7 @@ int QUICK_RANGE_SELECT::init() if (file->inited != handler::NONE) file->ha_index_or_rnd_end(); - DBUG_RETURN(error= file->ha_index_init(index)); + DBUG_RETURN(FALSE); } From 12173de37bf7fd777f865f8745d8786508ee5094 Mon Sep 17 00:00:00 2001 From: Kent Boortz Date: Mon, 13 Oct 2008 14:23:39 +0200 Subject: [PATCH 05/15] The header "config.h" needs to be included "early" to control other headers. This time the inclusion of before "config.h" enabled legacy large file support, seek64() and similar, on AIX breaking the compile of "gzio.c" --- zlib/gzio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zlib/gzio.c b/zlib/gzio.c index 7e90f4928fc..ed4e77ca7e9 100644 --- a/zlib/gzio.c +++ b/zlib/gzio.c @@ -7,6 +7,11 @@ /* @(#) $Id$ */ +/* Need to be included "early" to control other headers */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include #include "zutil.h" From 017307f2163a068520374b3083895e246b7a4a60 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 14 Oct 2008 11:04:36 -0300 Subject: [PATCH 06/15] Bug#38823: Invalid memory access when a SP statement does wildcard expansion The problem is that field names constructed due to wild-card expansion done inside a stored procedure could point to freed memory if the expansion was performed after the first call to the stored procedure. The problem was solved by patch for Bug#38691. The solution was to allocate the database, table and field names in the in the statement memory instead of table memory. mysql-test/r/sp.result: Add test case result for Bug#38823 mysql-test/t/sp.test: Add test case for Bug#38823 sql/item.cc: Remark that this also impacts wildcard expansion inside SPs. --- mysql-test/r/sp.result | 13 +++++++++++++ mysql-test/t/sp.test | 22 ++++++++++++++++++++++ sql/item.cc | 3 ++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index e788d30a14b..dea51bb4c2c 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6672,6 +6672,19 @@ select substr(`str`, `pos`+ 1 ) into `str`; end $ call `p2`('s s s s s s'); drop procedure `p2`; +drop table if exists t1; +drop procedure if exists p1; +create procedure p1() begin select * from t1; end$ +call p1$ +ERROR 42S02: Table 'test.t1' doesn't exist +create table t1 (a integer)$ +call p1$ +a +alter table t1 add b integer; +call p1$ +a +drop table t1; +drop procedure p1; # ------------------------------------------------------------------ # -- End of 5.0 tests # ------------------------------------------------------------------ diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 21ca2528e4f..6d7a4b96167 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -7836,6 +7836,28 @@ delimiter ;$ call `p2`('s s s s s s'); drop procedure `p2`; +# +# Bug#38823: Invalid memory access when a SP statement does wildcard expansion +# + +--disable_warnings +drop table if exists t1; +drop procedure if exists p1; +--enable_warnings + +delimiter $; +create procedure p1() begin select * from t1; end$ +--error ER_NO_SUCH_TABLE +call p1$ +create table t1 (a integer)$ +call p1$ +alter table t1 add b integer; +call p1$ +delimiter ;$ + +drop table t1; +drop procedure p1; + --echo # ------------------------------------------------------------------ --echo # -- End of 5.0 tests --echo # ------------------------------------------------------------------ diff --git a/sql/item.cc b/sql/item.cc index e49de88cea7..182f4abdfe6 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1759,7 +1759,8 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, be allocated in the statement memory, not in table memory (the table structure can go away and pop up again between subsequent executions of a prepared statement or after the close_tables_for_reopen() call - in mysql_multi_update_prepare()). + in mysql_multi_update_prepare() or due to wildcard expansion in stored + procedures). */ { if (db_name) From 747351836c524cb15dfb0c5ed735921ab98acb6b Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 15 Oct 2008 16:55:52 +0300 Subject: [PATCH 07/15] Bug #38693: leaked memory with blobs! If delayed insert fails to upgrade the lock it was not freeing the temporary memory storage used to keep newly constructed blob values in memory. Fixed by iterating over the remaining rows in the delayed insert rowset and freeing the blob storage for each row. No test suite because it involves concurrent delayed inserts on a table and cannot easily be made deterministic. Added a correct valgrind suppression for Fedora 9. mysql-test/valgrind.supp: Added a vagrind suppression for Fedora 9 sql/sql_insert.cc: Bug #38693: free the blobs temp storage on error. --- mysql-test/valgrind.supp | 8 ++++++++ sql/sql_insert.cc | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index e71f4541cfd..d7ac6bc6c88 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -319,6 +319,14 @@ fun:_dl_map_object } +{ + libc pthread_exit 8 + Memcheck:Leak + fun:malloc + fun:local_strdup + fun:_dl_map_object + fun:dl_open_worker +} # diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3f43c902faa..4eddcd17df1 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2585,6 +2585,11 @@ bool Delayed_insert::handle_inserts(void) /* Remove all not used rows */ while ((row=rows.get())) { + if (table->s->blob_fields) + { + memcpy(table->record[0],row->record,table->s->reclength); + free_delayed_insert_blobs(table); + } delete row; thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); stacked_inserts--; From 3ad228d7fba6fa2e5b98569f798583b8f8b90db9 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Wed, 15 Oct 2008 18:34:51 -0300 Subject: [PATCH 08/15] Bug#37075: offset of limit clause might be truncated on 32-bits server w/o big tables The problem is that the offset argument of the limit clause might be truncated on a 32-bits server built without big tables support. The truncation was happening because the original 64-bits long argument was being cast to a 32-bits (ha_rows) offset counter. The solution is to check if the conversing resulted in value truncation and if so, the offset is set to the maximum possible value that can fit on the type. mysql-test/r/limit.result: Add test case result for Bug#37075 mysql-test/t/limit.test: Add test case for Bug#37075 sql/sql_lex.cc: Check for truncation of the offset value. If value was truncated, set to the maximum possible value. --- mysql-test/r/limit.result | 3 +++ mysql-test/t/limit.test | 8 ++++++++ sql/sql_lex.cc | 22 ++++++++++++++++++---- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/limit.result b/mysql-test/r/limit.result index 2acf74162a4..caed588acdb 100644 --- a/mysql-test/r/limit.result +++ b/mysql-test/r/limit.result @@ -111,3 +111,6 @@ set @a=-14632475938453979136; execute s using @a, @a; ERROR HY000: Incorrect arguments to EXECUTE End of 5.0 tests +select 1 as a limit 4294967296,10; +a +End of 5.1 tests diff --git a/mysql-test/t/limit.test b/mysql-test/t/limit.test index 9cccca1adc3..5847b90367a 100644 --- a/mysql-test/t/limit.test +++ b/mysql-test/t/limit.test @@ -95,3 +95,11 @@ set @a=-14632475938453979136; execute s using @a, @a; --echo End of 5.0 tests + +# +# Bug#37075: offset of limit clause might be truncated to 0 on 32-bits server w/o big tables +# + +select 1 as a limit 4294967296,10; + +--echo End of 5.1 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ba9c0e93134..71aa80b8170 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2041,12 +2041,26 @@ st_lex::copy_db_to(char **p_db, uint *p_db_length) const void st_select_lex_unit::set_limit(SELECT_LEX *sl) { ha_rows select_limit_val; + ulonglong val; DBUG_ASSERT(! thd->stmt_arena->is_stmt_prepare()); - select_limit_val= (ha_rows)(sl->select_limit ? sl->select_limit->val_uint() : - HA_POS_ERROR); - offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() : - ULL(0)); + val= sl->select_limit ? sl->select_limit->val_uint() : HA_POS_ERROR; + select_limit_val= (ha_rows)val; +#ifndef BIG_TABLES + /* + Check for overflow : ha_rows can be smaller then ulonglong if + BIG_TABLES is off. + */ + if (val != (ulonglong)select_limit_val) + select_limit_val= HA_POS_ERROR; +#endif + val= sl->offset_limit ? sl->offset_limit->val_uint() : ULL(0); + offset_limit_cnt= (ha_rows)val; +#ifndef BIG_TABLES + /* Check for truncation. */ + if (val != (ulonglong)offset_limit_cnt) + offset_limit_cnt= HA_POS_ERROR; +#endif select_limit_cnt= select_limit_val + offset_limit_cnt; if (select_limit_cnt < select_limit_val) select_limit_cnt= HA_POS_ERROR; // no limit From ca6e05765c7a404377dd84924a816e654b20618d Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 16 Oct 2008 14:16:27 +0300 Subject: [PATCH 09/15] Bug #39958: Test "windows" lacks a cleanup Added the missing DROP TABLE mysql-test/r/windows.result: Bug #39958: added the missing DROP TABLE mysql-test/t/windows.test: Bug #39958: added the missing DROP TABLE --- mysql-test/r/windows.result | 1 + mysql-test/t/windows.test | 1 + 2 files changed, 2 insertions(+) mode change 100644 => 100755 mysql-test/t/windows.test diff --git a/mysql-test/r/windows.result b/mysql-test/r/windows.result index b5f9a48d805..5a54db8bb84 100644 --- a/mysql-test/r/windows.result +++ b/mysql-test/r/windows.result @@ -18,4 +18,5 @@ EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2)); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used +DROP TABLE t1; End of 5.0 tests. diff --git a/mysql-test/t/windows.test b/mysql-test/t/windows.test old mode 100644 new mode 100755 index 6976ee98750..6e6a7ec93a3 --- a/mysql-test/t/windows.test +++ b/mysql-test/t/windows.test @@ -33,5 +33,6 @@ drop table t1; # CREATE TABLE t1 (a int, b int); INSERT INTO t1 VALUES (1,1); EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2)); +DROP TABLE t1; --echo End of 5.0 tests. From b591793496f4eb4e9953383c09ce4033bd85850f Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Thu, 16 Oct 2008 21:37:17 +0500 Subject: [PATCH 10/15] Bug #39844: Query Crash Mysql Server 5.0.67 Server crashed during a sort order optimization of a dependent subquery: SELECT (SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) FROM t3; Bitmap of tables, that the reference to outer table column uses, in addition to the regular table bit has the OUTER_REF_TABLE_BIT bit set. The only_eq_ref_tables function traverses this map bit by bit simultaneously with join->map2table list. Obviously join->map2table never contains an entry for the OUTER_REF_TABLE_BIT pseudo-table, so the server crashed there. The only_eq_ref_tables function has been modified to traverse regular table bits only like the update_depend_map function (resetting of the OUTER_REF_TABLE_BIT there is enough, but resetting of the whole set of PSEUDO_TABLE_BITS is used there for sure). mysql-test/r/order_by.result: Added test case for bug #39844. mysql-test/t/order_by.test: Added test case for bug #39844. sql/sql_select.cc: Bug #39844: Query Crash Mysql Server 5.0.67 The only_eq_ref_tables function has been modified to traverse regular table bits only like the update_depend_map function (resetting of the OUTER_REF_TABLE_BIT there is enough, but resetting of the whole set of PSEUDO_TABLE_BITS is used there for sure). --- mysql-test/r/order_by.result | 16 ++++++++++++++++ mysql-test/t/order_by.test | 18 ++++++++++++++++++ sql/sql_select.cc | 1 + 3 files changed, 35 insertions(+) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index 9f6a1b3932c..f64bbc79cbd 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -1076,3 +1076,19 @@ set session max_sort_length= 2180; select * from t1 order by b; ERROR HY001: Out of sort memory; increase server sort buffer size drop table t1; +# +# Bug #39844: Query Crash Mysql Server 5.0.67 +# +CREATE TABLE t1 (a INT PRIMARY KEY); +CREATE TABLE t2 (a INT PRIMARY KEY, b INT); +CREATE TABLE t3 (c INT); +INSERT INTO t1 (a) VALUES (1), (2); +INSERT INTO t2 (a,b) VALUES (1,2), (2,3); +INSERT INTO t3 (c) VALUES (1), (2); +SELECT +(SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) +FROM t3; +(SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) +2 +NULL +DROP TABLE t1, t2, t3; diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 9a55c27df99..6d7ee1c1ca7 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -738,3 +738,21 @@ set session max_sort_length= 2180; select * from t1 order by b; drop table t1; + +--echo # +--echo # Bug #39844: Query Crash Mysql Server 5.0.67 +--echo # + +CREATE TABLE t1 (a INT PRIMARY KEY); +CREATE TABLE t2 (a INT PRIMARY KEY, b INT); +CREATE TABLE t3 (c INT); + +INSERT INTO t1 (a) VALUES (1), (2); +INSERT INTO t2 (a,b) VALUES (1,2), (2,3); +INSERT INTO t3 (c) VALUES (1), (2); + +SELECT + (SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) + FROM t3; + +DROP TABLE t1, t2, t3; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7ff069f0996..9723dd8c4e4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6574,6 +6574,7 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables) { if (specialflag & SPECIAL_SAFE_MODE) return 0; // skip this optimize /* purecov: inspected */ + tables&= ~PSEUDO_TABLE_BITS; for (JOIN_TAB **tab=join->map2table ; tables ; tab++, tables>>=1) { if (tables & 1 && !eq_ref_table(join, order, *tab)) From 9ad8d644392bc9ca23e0212e18c704f7a6520837 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 21 Oct 2008 15:45:43 -0200 Subject: [PATCH 11/15] Bug#28323: Server crashed in xid cache operations The problem was that the server did not robustly handle a unilateral roll back issued by the Resource Manager (RM) due to a resource deadlock within the transaction branch. By not acknowledging the roll back, the server (TM) would eventually corrupt the XA transaction state and crash. The solution is to mark the transaction as rollback-only if the RM indicates that it rolled back its branch of the transaction. mysql-test/r/xa.result: Add test case result for Bug#28323 mysql-test/t/xa.test: Add test case for Bug#28323 sql/handler.cc: Reset XID only at the end of the global transaction. sql/share/errmsg.txt: Add new error codes. sql/sql_class.h: Remember the error reported by the Resource Manager. sql/sql_parse.cc: Rollback the transaction if the Resource Manager reported a error and rolled back its branch of the transaction. --- mysql-test/r/xa.result | 19 ++++++++++ mysql-test/t/xa.test | 44 ++++++++++++++++++++++++ sql/handler.cc | 7 +++- sql/share/errmsg.txt | 4 +++ sql/sql_class.h | 4 ++- sql/sql_parse.cc | 78 +++++++++++++++++++++++++++++++++++++----- 6 files changed, 145 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/xa.result b/mysql-test/r/xa.result index 5fb03d2378e..25d09f59247 100644 --- a/mysql-test/r/xa.result +++ b/mysql-test/r/xa.result @@ -55,3 +55,22 @@ select * from t1; a 20 drop table t1; +drop table if exists t1; +create table t1(a int, b int, c varchar(20), primary key(a)) engine = innodb; +insert into t1 values(1, 1, 'a'); +insert into t1 values(2, 2, 'b'); +xa start 'a','b'; +update t1 set c = 'aa' where a = 1; +xa start 'a','c'; +update t1 set c = 'bb' where a = 2; +update t1 set c = 'bb' where a = 2; +update t1 set c = 'aa' where a = 1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +select count(*) from t1; +count(*) +2 +xa end 'a','c'; +ERROR XA102: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected +xa rollback 'a','c'; +xa start 'a','c'; +End of 5.0 tests diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index 0d564727fe3..8f408fb1eda 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -74,3 +74,47 @@ xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'; select * from t1; drop table t1; +disconnect con1; + +# +# Bug#28323: Server crashed in xid cache operations +# + +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1(a int, b int, c varchar(20), primary key(a)) engine = innodb; +insert into t1 values(1, 1, 'a'); +insert into t1 values(2, 2, 'b'); + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); + +--connection con1 +xa start 'a','b'; +update t1 set c = 'aa' where a = 1; +--connection con2 +xa start 'a','c'; +update t1 set c = 'bb' where a = 2; +--connection con1 +--send update t1 set c = 'bb' where a = 2 +--connection con2 +--sleep 1 +--error ER_LOCK_DEADLOCK +update t1 set c = 'aa' where a = 1; +select count(*) from t1; +--error ER_XA_RBDEADLOCK +xa end 'a','c'; +xa rollback 'a','c'; +--disconnect con2 + +connect (con3,localhost,root,,); +--connection con3 +xa start 'a','c'; + +--disconnect con1 +--disconnect con3 +--connection default + +--echo End of 5.0 tests diff --git a/sql/handler.cc b/sql/handler.cc index 0de772e366b..67ec5f3e759 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -817,7 +817,12 @@ int ha_rollback_trans(THD *thd, bool all) trans->nht=0; trans->no_2pc=0; if (is_real_trans) - thd->transaction.xid_state.xid.null(); + { + if (thd->transaction_rollback_request) + thd->transaction.xid_state.rm_error= thd->net.last_errno; + else + thd->transaction.xid_state.xid.null(); + } if (all) { thd->variables.tx_isolation=thd->session_tx_isolation; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 0916ad56cef..c688ba88b7b 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5645,3 +5645,7 @@ ER_LOAD_DATA_INVALID_COLUMN eng "Invalid column reference (%-.64s) in LOAD DATA" ER_LOG_PURGE_NO_FILE eng "Being purged log %s was not found" +ER_XA_RBTIMEOUT XA106 + eng "XA_RBTIMEOUT: Transaction branch was rolled back: took too long" +ER_XA_RBDEADLOCK XA102 + eng "XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected" diff --git a/sql/sql_class.h b/sql/sql_class.h index 9fe0a7423de..c8d42d44df7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -927,7 +927,7 @@ struct st_savepoint { uint length, nht; }; -enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED}; +enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; extern const char *xa_state_names[]; typedef struct st_xid_state { @@ -935,6 +935,8 @@ typedef struct st_xid_state { XID xid; // transaction identifier enum xa_states xa_state; // used by external XA only bool in_thd; + /* Error reported by the Resource Manager (RM) to the Transaction Manager. */ + uint rm_error; } XID_STATE; extern pthread_mutex_t LOCK_xid_cache; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 005fdcac7f3..91c5cacc4d0 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -90,9 +90,57 @@ const char *command_name[]={ }; const char *xa_state_names[]={ - "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" + "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY" }; +/** + Mark a XA transaction as rollback-only if the RM unilaterally + rolled back the transaction branch. + + @note If a rollback was requested by the RM, this function sets + the appropriate rollback error code and transits the state + to XA_ROLLBACK_ONLY. + + @return TRUE if transaction was rolled back or if the transaction + state is XA_ROLLBACK_ONLY. FALSE otherwise. +*/ +static bool xa_trans_rolled_back(XID_STATE *xid_state) +{ + if (xid_state->rm_error) + { + switch (xid_state->rm_error) { + case ER_LOCK_WAIT_TIMEOUT: + my_error(ER_XA_RBTIMEOUT, MYF(0)); + break; + case ER_LOCK_DEADLOCK: + my_error(ER_XA_RBDEADLOCK, MYF(0)); + break; + default: + my_error(ER_XA_RBROLLBACK, MYF(0)); + } + xid_state->xa_state= XA_ROLLBACK_ONLY; + } + + return (xid_state->xa_state == XA_ROLLBACK_ONLY); +} + +/** + Rollback work done on behalf of at ransaction branch. +*/ +static bool xa_trans_rollback(THD *thd) +{ + bool status= test(ha_rollback(thd)); + + thd->options&= ~(ulong) OPTION_BEGIN; + thd->transaction.all.modified_non_trans_table= FALSE; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + thd->transaction.xid_state.rm_error= 0; + + return status; +} + #ifndef EMBEDDED_LIBRARY static bool do_command(THD *thd); #endif // EMBEDDED_LIBRARY @@ -5070,6 +5118,7 @@ create_sp_error: } DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); thd->transaction.xid_state.xa_state=XA_ACTIVE; + thd->transaction.xid_state.rm_error= 0; thd->transaction.xid_state.xid.set(thd->lex->xid); xid_cache_insert(&thd->transaction.xid_state); thd->transaction.all.modified_non_trans_table= FALSE; @@ -5095,6 +5144,8 @@ create_sp_error: my_error(ER_XAER_NOTA, MYF(0)); break; } + if (xa_trans_rolled_back(&thd->transaction.xid_state)) + break; thd->transaction.xid_state.xa_state=XA_IDLE; send_ok(thd); break; @@ -5126,6 +5177,12 @@ create_sp_error: XID_STATE *xs=xid_cache_search(thd->lex->xid); if (!xs || xs->in_thd) my_error(ER_XAER_NOTA, MYF(0)); + else if (xa_trans_rolled_back(xs)) + { + ha_commit_or_rollback_by_xid(thd->lex->xid, 0); + xid_cache_delete(xs); + break; + } else { ha_commit_or_rollback_by_xid(thd->lex->xid, 1); @@ -5134,6 +5191,11 @@ create_sp_error: } break; } + if (xa_trans_rolled_back(&thd->transaction.xid_state)) + { + xa_trans_rollback(thd); + break; + } if (thd->transaction.xid_state.xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE) { @@ -5180,28 +5242,26 @@ create_sp_error: my_error(ER_XAER_NOTA, MYF(0)); else { + bool ok= !xa_trans_rolled_back(xs); ha_commit_or_rollback_by_xid(thd->lex->xid, 0); xid_cache_delete(xs); - send_ok(thd); + if (ok) + send_ok(thd); } break; } if (thd->transaction.xid_state.xa_state != XA_IDLE && - thd->transaction.xid_state.xa_state != XA_PREPARED) + thd->transaction.xid_state.xa_state != XA_PREPARED && + thd->transaction.xid_state.xa_state != XA_ROLLBACK_ONLY) { my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[thd->transaction.xid_state.xa_state]); break; } - if (ha_rollback(thd)) + if (xa_trans_rollback(thd)) my_error(ER_XAER_RMERR, MYF(0)); else send_ok(thd); - thd->options&= ~(ulong) OPTION_BEGIN; - thd->transaction.all.modified_non_trans_table= FALSE; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state=XA_NOTR; break; case SQLCOM_XA_RECOVER: res= mysql_xa_recover(thd); From 41dd7b6b92ce8e3c2ccb5aa9ebeef12be746793e Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 21 Oct 2008 16:07:31 -0200 Subject: [PATCH 12/15] Bug#28323: Server crashed in xid cache operations The problem was that the server did not robustly handle a unilateral roll back issued by the Resource Manager (RM) due to a resource deadlock within the transaction branch. By not acknowledging the roll back, the server (TM) would eventually corrupt the XA transaction state and crash. The solution is to mark the transaction as rollback-only if the RM indicates that it rolled back its branch of the transaction. mysql-test/r/xa.result: Add test case result for Bug#28323 mysql-test/t/xa.test: Add test case for Bug#28323 sql/handler.cc: Reset XID only at the end of the global transaction. sql/share/errmsg.txt: Add new error codes. sql/sql_class.h: Remember the error reported by the Resource Manager. sql/sql_parse.cc: Rollback the transaction if the Resource Manager reported a error and rolled back its branch of the transaction. --- mysql-test/r/xa.result | 19 ++++++++++ mysql-test/t/xa.test | 44 ++++++++++++++++++++++++ sql/handler.cc | 7 +++- sql/share/errmsg.txt | 4 +++ sql/sql_class.h | 4 ++- sql/sql_parse.cc | 78 +++++++++++++++++++++++++++++++++++++----- 6 files changed, 145 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/xa.result b/mysql-test/r/xa.result index 5fb03d2378e..25d09f59247 100644 --- a/mysql-test/r/xa.result +++ b/mysql-test/r/xa.result @@ -55,3 +55,22 @@ select * from t1; a 20 drop table t1; +drop table if exists t1; +create table t1(a int, b int, c varchar(20), primary key(a)) engine = innodb; +insert into t1 values(1, 1, 'a'); +insert into t1 values(2, 2, 'b'); +xa start 'a','b'; +update t1 set c = 'aa' where a = 1; +xa start 'a','c'; +update t1 set c = 'bb' where a = 2; +update t1 set c = 'bb' where a = 2; +update t1 set c = 'aa' where a = 1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +select count(*) from t1; +count(*) +2 +xa end 'a','c'; +ERROR XA102: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected +xa rollback 'a','c'; +xa start 'a','c'; +End of 5.0 tests diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index 0d564727fe3..8f408fb1eda 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -74,3 +74,47 @@ xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'; select * from t1; drop table t1; +disconnect con1; + +# +# Bug#28323: Server crashed in xid cache operations +# + +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1(a int, b int, c varchar(20), primary key(a)) engine = innodb; +insert into t1 values(1, 1, 'a'); +insert into t1 values(2, 2, 'b'); + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); + +--connection con1 +xa start 'a','b'; +update t1 set c = 'aa' where a = 1; +--connection con2 +xa start 'a','c'; +update t1 set c = 'bb' where a = 2; +--connection con1 +--send update t1 set c = 'bb' where a = 2 +--connection con2 +--sleep 1 +--error ER_LOCK_DEADLOCK +update t1 set c = 'aa' where a = 1; +select count(*) from t1; +--error ER_XA_RBDEADLOCK +xa end 'a','c'; +xa rollback 'a','c'; +--disconnect con2 + +connect (con3,localhost,root,,); +--connection con3 +xa start 'a','c'; + +--disconnect con1 +--disconnect con3 +--connection default + +--echo End of 5.0 tests diff --git a/sql/handler.cc b/sql/handler.cc index 0de772e366b..67ec5f3e759 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -817,7 +817,12 @@ int ha_rollback_trans(THD *thd, bool all) trans->nht=0; trans->no_2pc=0; if (is_real_trans) - thd->transaction.xid_state.xid.null(); + { + if (thd->transaction_rollback_request) + thd->transaction.xid_state.rm_error= thd->net.last_errno; + else + thd->transaction.xid_state.xid.null(); + } if (all) { thd->variables.tx_isolation=thd->session_tx_isolation; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 0916ad56cef..c688ba88b7b 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5645,3 +5645,7 @@ ER_LOAD_DATA_INVALID_COLUMN eng "Invalid column reference (%-.64s) in LOAD DATA" ER_LOG_PURGE_NO_FILE eng "Being purged log %s was not found" +ER_XA_RBTIMEOUT XA106 + eng "XA_RBTIMEOUT: Transaction branch was rolled back: took too long" +ER_XA_RBDEADLOCK XA102 + eng "XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected" diff --git a/sql/sql_class.h b/sql/sql_class.h index a9700c9e91b..44dff38979e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -920,7 +920,7 @@ struct st_savepoint { uint length, nht; }; -enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED}; +enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; extern const char *xa_state_names[]; typedef struct st_xid_state { @@ -928,6 +928,8 @@ typedef struct st_xid_state { XID xid; // transaction identifier enum xa_states xa_state; // used by external XA only bool in_thd; + /* Error reported by the Resource Manager (RM) to the Transaction Manager. */ + uint rm_error; } XID_STATE; extern pthread_mutex_t LOCK_xid_cache; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 713aca9de47..345f2d0420f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -90,9 +90,57 @@ const char *command_name[]={ }; const char *xa_state_names[]={ - "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" + "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY" }; +/** + Mark a XA transaction as rollback-only if the RM unilaterally + rolled back the transaction branch. + + @note If a rollback was requested by the RM, this function sets + the appropriate rollback error code and transits the state + to XA_ROLLBACK_ONLY. + + @return TRUE if transaction was rolled back or if the transaction + state is XA_ROLLBACK_ONLY. FALSE otherwise. +*/ +static bool xa_trans_rolled_back(XID_STATE *xid_state) +{ + if (xid_state->rm_error) + { + switch (xid_state->rm_error) { + case ER_LOCK_WAIT_TIMEOUT: + my_error(ER_XA_RBTIMEOUT, MYF(0)); + break; + case ER_LOCK_DEADLOCK: + my_error(ER_XA_RBDEADLOCK, MYF(0)); + break; + default: + my_error(ER_XA_RBROLLBACK, MYF(0)); + } + xid_state->xa_state= XA_ROLLBACK_ONLY; + } + + return (xid_state->xa_state == XA_ROLLBACK_ONLY); +} + +/** + Rollback work done on behalf of at ransaction branch. +*/ +static bool xa_trans_rollback(THD *thd) +{ + bool status= test(ha_rollback(thd)); + + thd->options&= ~(ulong) OPTION_BEGIN; + thd->transaction.all.modified_non_trans_table= FALSE; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + thd->transaction.xid_state.rm_error= 0; + + return status; +} + #ifndef EMBEDDED_LIBRARY static bool do_command(THD *thd); #endif // EMBEDDED_LIBRARY @@ -5049,6 +5097,7 @@ create_sp_error: } DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); thd->transaction.xid_state.xa_state=XA_ACTIVE; + thd->transaction.xid_state.rm_error= 0; thd->transaction.xid_state.xid.set(thd->lex->xid); xid_cache_insert(&thd->transaction.xid_state); thd->transaction.all.modified_non_trans_table= FALSE; @@ -5074,6 +5123,8 @@ create_sp_error: my_error(ER_XAER_NOTA, MYF(0)); break; } + if (xa_trans_rolled_back(&thd->transaction.xid_state)) + break; thd->transaction.xid_state.xa_state=XA_IDLE; send_ok(thd); break; @@ -5105,6 +5156,12 @@ create_sp_error: XID_STATE *xs=xid_cache_search(thd->lex->xid); if (!xs || xs->in_thd) my_error(ER_XAER_NOTA, MYF(0)); + else if (xa_trans_rolled_back(xs)) + { + ha_commit_or_rollback_by_xid(thd->lex->xid, 0); + xid_cache_delete(xs); + break; + } else { ha_commit_or_rollback_by_xid(thd->lex->xid, 1); @@ -5113,6 +5170,11 @@ create_sp_error: } break; } + if (xa_trans_rolled_back(&thd->transaction.xid_state)) + { + xa_trans_rollback(thd); + break; + } if (thd->transaction.xid_state.xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE) { @@ -5159,28 +5221,26 @@ create_sp_error: my_error(ER_XAER_NOTA, MYF(0)); else { + bool ok= !xa_trans_rolled_back(xs); ha_commit_or_rollback_by_xid(thd->lex->xid, 0); xid_cache_delete(xs); - send_ok(thd); + if (ok) + send_ok(thd); } break; } if (thd->transaction.xid_state.xa_state != XA_IDLE && - thd->transaction.xid_state.xa_state != XA_PREPARED) + thd->transaction.xid_state.xa_state != XA_PREPARED && + thd->transaction.xid_state.xa_state != XA_ROLLBACK_ONLY) { my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[thd->transaction.xid_state.xa_state]); break; } - if (ha_rollback(thd)) + if (xa_trans_rollback(thd)) my_error(ER_XAER_RMERR, MYF(0)); else send_ok(thd); - thd->options&= ~(ulong) OPTION_BEGIN; - thd->transaction.all.modified_non_trans_table= FALSE; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state=XA_NOTR; break; case SQLCOM_XA_RECOVER: res= mysql_xa_recover(thd); From 07b7261a702c4721817881476732d8e514afced0 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 21 Oct 2008 19:02:26 -0200 Subject: [PATCH 13/15] Post-merge fix: drop table at the end of test. mysql-test/r/xa.result: Update test case result. mysql-test/t/xa.test: Drop table used for test. --- mysql-test/r/xa.result | 1 + mysql-test/t/xa.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/r/xa.result b/mysql-test/r/xa.result index 25d09f59247..592cf07522b 100644 --- a/mysql-test/r/xa.result +++ b/mysql-test/r/xa.result @@ -73,4 +73,5 @@ xa end 'a','c'; ERROR XA102: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected xa rollback 'a','c'; xa start 'a','c'; +drop table t1; End of 5.0 tests diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index 8f408fb1eda..591d7ac2c4d 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -116,5 +116,6 @@ xa start 'a','c'; --disconnect con1 --disconnect con3 --connection default +drop table t1; --echo End of 5.0 tests From 49ff055736b3b613005e5b17915d08fe48b7b532 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 23 Oct 2008 15:28:53 +0200 Subject: [PATCH 14/15] Bug#40280: Message compiler(mc.exe) needed to compile MySQL on windows. Visual Studio 2008 Express edition does not include message compiler mc.exe It is not possible to build MySQL server if only VC2008 Express is installed, because we use mc.exe to generate event log messages. This patch removes the mc.exe dependency. Generated files message.h, message.rc and MSG00001.bin are checked into source code repository. Instructions on how to add or change messages are added to messages.mc --- sql/CMakeLists.txt | 15 +++---------- sql/MSG00001.bin | Bin 0 -> 184 bytes sql/Makefile.am | 3 ++- sql/message.h | 55 +++++++++++++++++++++++++++++++++++++++++++++ sql/message.mc | 8 +++++++ sql/message.rc | 2 ++ 6 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 sql/MSG00001.bin create mode 100644 sql/message.h create mode 100644 sql/message.rc diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 9ea9874c1b1..860afb42a27 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -28,10 +28,8 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/bdb/build_win32 ${CMAKE_SOURCE_DIR}/bdb/dbinc) -SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/message.rc - ${CMAKE_SOURCE_DIR}/sql/message.h - ${CMAKE_SOURCE_DIR}/sql/sql_yacc.h - ${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc +SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/sql_yacc.h + ${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc ${CMAKE_SOURCE_DIR}/include/mysql_version.h ${CMAKE_SOURCE_DIR}/sql/lex_hash.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h @@ -120,13 +118,6 @@ ADD_CUSTOM_COMMAND( DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ) -# Windows message file -ADD_CUSTOM_COMMAND( - SOURCE ${PROJECT_SOURCE_DIR}/sql/message.mc - OUTPUT message.rc message.h - COMMAND mc ARGS ${PROJECT_SOURCE_DIR}/sql/message.mc - DEPENDS ${PROJECT_SOURCE_DIR}/sql/message.mc) - # Gen_lex_hash # About "mysqlclient_notls", see note in "client/CMakeLists.txt" ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc) @@ -141,7 +132,7 @@ ADD_DEPENDENCIES(mysqld${MYSQLD_EXE_SUFFIX} gen_lex_hash) # Remove the auto-generated files as part of 'Clean Solution' SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES - "lex_hash.h;message.rc;message.h;sql_yacc.h;sql_yacc.cc") + "lex_hash.h;sql_yacc.h;sql_yacc.cc") ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def) ADD_DEPENDENCIES(udf_example strings) diff --git a/sql/MSG00001.bin b/sql/MSG00001.bin new file mode 100644 index 0000000000000000000000000000000000000000..89f547694f5fd13c7773d3b80d0119aa4bccb713 GIT binary patch literal 184 zcmW-bK?=e^5Ckh%LGTH=c$0`%uY!2@1;MN+xJlF%@kc(xr&$@Aou1yA?%fLL;5VIx z{g;X*8}}?n=)&RHmh<2X9tRo+MFou-+K$S|^=+ Date: Fri, 24 Oct 2008 11:37:40 +0200 Subject: [PATCH 15/15] Raise version number after cloning 5.0.72 --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 85021ba8dde..493f46251a9 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.72) +AM_INIT_AUTOMAKE(mysql, 5.0.74) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 @@ -23,7 +23,7 @@ NDB_SHARED_LIB_VERSION=$NDB_SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=72 +NDB_VERSION_BUILD=74 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ?