From da2cc0cdf6ea9b1e689f41307ec118cf05579fc7 Mon Sep 17 00:00:00 2001 From: Tatjana Azundris Nuernberg Date: Fri, 17 Feb 2012 19:02:17 +0000 Subject: BUG#13431369 - MAIN.VARIABLES-NOTEMBEDDED CRASHES THE SERVER SPORADICALLY ON WINDOWS On shutdown(), Windows can drop traffic still queued for sending even if that wasn't specifically requested. As a result, fatal errors (those after signaling which the server will drop the connection) were sometimes only seen as "connection lost" on the client side, because the server-side shutdown() erraneously discarded the correct error message before sending it. If on Windows, we now use the Windows API to access the (non-broken) equivalent of shutdown(). Backport from trunk --- include/violite.h | 3 +++ sql/mysqld.cc | 16 ++++++++-------- vio/viosocket.c | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/include/violite.h b/include/violite.h index 32354938e13..5facd0911b7 100644 --- a/include/violite.h +++ b/include/violite.h @@ -61,6 +61,9 @@ int vio_close_pipe(Vio * vio); #define HANDLE void * #endif /* __WIN__ */ +/* backport from 5.6 where it is part of PSI, not vio_*() */ +int mysql_socket_shutdown(my_socket mysql_socket, int how); + void vio_delete(Vio* vio); int vio_close(Vio* vio); void vio_reset(Vio* vio, enum enum_vio_type type, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c47d39721b9..a37479dc4b4 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -932,7 +932,7 @@ static void close_connections(void) { if (ip_sock != INVALID_SOCKET) { - (void) shutdown(ip_sock, SHUT_RDWR); + (void) mysql_socket_shutdown(ip_sock, SHUT_RDWR); (void) closesocket(ip_sock); ip_sock= INVALID_SOCKET; } @@ -964,7 +964,7 @@ static void close_connections(void) #ifdef HAVE_SYS_UN_H if (unix_sock != INVALID_SOCKET) { - (void) shutdown(unix_sock, SHUT_RDWR); + (void) mysql_socket_shutdown(unix_sock, SHUT_RDWR); (void) closesocket(unix_sock); (void) unlink(mysqld_unix_port); unix_sock= INVALID_SOCKET; @@ -1069,7 +1069,7 @@ static void close_server_sock() { ip_sock=INVALID_SOCKET; DBUG_PRINT("info",("calling shutdown on TCP/IP socket")); - VOID(shutdown(tmp_sock, SHUT_RDWR)); + VOID(mysql_socket_shutdown(tmp_sock, SHUT_RDWR)); #if defined(__NETWARE__) /* The following code is disabled for normal systems as it causes MySQL @@ -1084,7 +1084,7 @@ static void close_server_sock() { unix_sock=INVALID_SOCKET; DBUG_PRINT("info",("calling shutdown on unix socket")); - VOID(shutdown(tmp_sock, SHUT_RDWR)); + VOID(mysql_socket_shutdown(tmp_sock, SHUT_RDWR)); #if defined(__NETWARE__) /* The following code is disabled for normal systems as it may cause MySQL @@ -5091,7 +5091,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) if (req.sink) ((void (*)(int))req.sink)(req.fd); - (void) shutdown(new_sock, SHUT_RDWR); + (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); (void) closesocket(new_sock); continue; } @@ -5106,7 +5106,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) if (getsockname(new_sock,&dummy, &dummyLen) < 0) { sql_perror("Error on new connection socket"); - (void) shutdown(new_sock, SHUT_RDWR); + (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); (void) closesocket(new_sock); continue; } @@ -5118,7 +5118,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) if (!(thd= new THD)) { - (void) shutdown(new_sock, SHUT_RDWR); + (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); VOID(closesocket(new_sock)); continue; } @@ -5137,7 +5137,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) vio_delete(vio_tmp); else { - (void) shutdown(new_sock, SHUT_RDWR); + (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); (void) closesocket(new_sock); } delete thd; diff --git a/vio/viosocket.c b/vio/viosocket.c index 024aacc5db2..9633a01f689 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -23,8 +23,15 @@ the file descriptior. */ +#ifdef __WIN__ + #include + #include + #pragma comment(lib, "ws2_32.lib") +#endif #include "vio_priv.h" + + int vio_errno(Vio *vio __attribute__((unused))) { return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ @@ -260,6 +267,37 @@ vio_was_interrupted(Vio *vio __attribute__((unused))) } +int +mysql_socket_shutdown(my_socket mysql_socket, int how) +{ + int result; + +#ifdef __WIN__ + static LPFN_DISCONNECTEX DisconnectEx = NULL; + if (DisconnectEx == NULL) + { + DWORD dwBytesReturned; + GUID guidDisconnectEx = WSAID_DISCONNECTEX; + WSAIoctl(mysql_socket, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidDisconnectEx, sizeof(GUID), + &DisconnectEx, sizeof(DisconnectEx), + &dwBytesReturned, NULL, NULL); + } +#endif + + /* Non instrumented code */ +#ifdef __WIN__ + if (DisconnectEx) + result= (DisconnectEx(mysql_socket, (LPOVERLAPPED) NULL, + (DWORD) 0, (DWORD) 0) == TRUE) ? 0 : -1; + else +#endif + result= shutdown(mysql_socket, how); + + return result; +} + + int vio_close(Vio * vio) { int r=0; @@ -272,7 +310,7 @@ int vio_close(Vio * vio) vio->type == VIO_TYPE_SSL); DBUG_ASSERT(vio->sd >= 0); - if (shutdown(vio->sd, SHUT_RDWR)) + if (mysql_socket_shutdown(vio->sd, SHUT_RDWR)) r= -1; if (closesocket(vio->sd)) r= -1; -- cgit v1.2.1 From c16b64c5b92da60eba39787a82239e95817c9fbb Mon Sep 17 00:00:00 2001 From: Sunanda Menon Date: Mon, 20 Feb 2012 06:19:12 +0100 Subject: Raise version number after cloning 5.0.96 --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 908171ca857..0f1968747c0 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.96) +AM_INIT_AUTOMAKE(mysql, 5.0.97) 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=96 +NDB_VERSION_BUILD=97 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? -- cgit v1.2.1 From b8a675b7585ba6896c2209e5ac3ab997112e4f29 Mon Sep 17 00:00:00 2001 From: Hery Ramilison Date: Mon, 20 Feb 2012 16:54:54 +0100 Subject: cloning 5.5.22 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index aa87ee35b40..0f96ceb238d 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=22 +MYSQL_VERSION_PATCH=24 MYSQL_VERSION_EXTRA= -- cgit v1.2.1 From 74cf0d02c1006ae351068e4bc1b9fa06e0a2bdf9 Mon Sep 17 00:00:00 2001 From: Karen Langford Date: Mon, 20 Feb 2012 17:03:24 +0100 Subject: Raise version number after cloning 5.1.62 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index fb6155aa0be..ccc2ac26570 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.1.62], [], [mysql]) +AC_INIT([MySQL Server], [5.1.63], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM -- cgit v1.2.1 From 067f83e642f472ccae31b593fead20d7588f681a Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 22 Feb 2012 11:17:50 +0100 Subject: Bug#13519724 63793: CRASH IN DTCOLLATION::SET(DTCOLLATION &SET) Backport of fix for: Bug#53236 Segfault in DTCollation::set(DTCollation&) --- mysql-test/r/cast.result | 9 +++++++++ mysql-test/t/cast.test | 12 ++++++++++++ sql/sql_select.cc | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 44d57055e7f..d7a7503707b 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -468,4 +468,13 @@ NULL Warnings: Warning 1301 Result of cast_as_char() was larger than max_allowed_packet (2048) - truncated SET @@GLOBAL.max_allowed_packet=default; +# +# Bug#13519724 63793: CRASH IN DTCOLLATION::SET(DTCOLLATION &SET) +# +CREATE TABLE t1 (a VARCHAR(50)); +SELECT a FROM t1 +WHERE CAST(a as BINARY)=x'62736D697468' +AND CAST(a AS BINARY)=x'65736D697468'; +a +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test index 426d7c7fdf2..4474736200c 100644 --- a/mysql-test/t/cast.test +++ b/mysql-test/t/cast.test @@ -297,4 +297,16 @@ connection default; disconnect newconn; SET @@GLOBAL.max_allowed_packet=default; +--echo # +--echo # Bug#13519724 63793: CRASH IN DTCOLLATION::SET(DTCOLLATION &SET) +--echo # + +CREATE TABLE t1 (a VARCHAR(50)); + +SELECT a FROM t1 +WHERE CAST(a as BINARY)=x'62736D697468' + AND CAST(a AS BINARY)=x'65736D697468'; + +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5f1efabfc97..0d8dc740431 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8546,10 +8546,10 @@ change_cond_ref_to_const(THD *thd, I_List *save_list, left_item->collation.collation == value->collation.collation)) { Item *tmp=value->clone_item(); - tmp->collation.set(right_item->collation); if (tmp) { + tmp->collation.set(right_item->collation); thd->change_item_tree(args + 1, tmp); func->update_used_tables(); if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC) @@ -8570,10 +8570,10 @@ change_cond_ref_to_const(THD *thd, I_List *save_list, right_item->collation.collation == value->collation.collation)) { Item *tmp= value->clone_item(); - tmp->collation.set(left_item->collation); if (tmp) { + tmp->collation.set(left_item->collation); thd->change_item_tree(args, tmp); value= tmp; func->update_used_tables(); -- cgit v1.2.1 From 02f44fe92ba250f1123321313c1c5202cc5c263a Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Fri, 24 Feb 2012 16:07:43 +0000 Subject: Bug#13693012: SLAVE CRASHING ON INSERT STATEMENT WITH MERGE TABLE PROBLEM: After WL 4144, when using MyISAM Merge tables, the routine open_and_lock_tables will append to the list of tables to lock, the base tables that make up the MERGE table. This has two side-effects in replication: 1. On the master side, we log additional table maps for the base tables, since they appear in the list of locked tables, even though we don't really use them at the slave. 2. On the slave side, when opening a MERGE table while applying a ROW event, additional tables are appended to the list of tables to lock. Side-effect #1 is not harmful. It's just that when using MyISAM Merge tables a few table maps more may be logged. Side-effect #2, is harmful, because the list rli->tables_to_lock is an extended structure from TABLE_LIST in which the extra fields are filled from the table maps that are processed. Since open_and_lock_tables appends tables to the list after all table map events have been processed we end up with entries without replication/table map data on them. Thus when trying to access that info for these extra tables, the server will crash. SOLUTION: We fix side-effect #2 by making sure that we access the replication part of the structure for those in the list that were accounted for when processing the correspondent table map events. All in all, we never go beyond rli->tables_to_lock_count. We also deploy an assertion when clearing rli->tables_to_lock, making sure that the base tables are not in the list anymore (were closed in close_thread_tables). --- mysql-test/suite/rpl/r/rpl_row_merge_engine.result | 16 +++++++ mysql-test/suite/rpl/t/rpl_row_merge_engine.test | 50 ++++++++++++++++++++++ sql/log_event.cc | 29 ++++++++++--- sql/log_event_old.cc | 12 +++--- sql/rpl_rli.cc | 20 +++++++++ 5 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_row_merge_engine.result create mode 100644 mysql-test/suite/rpl/t/rpl_row_merge_engine.test diff --git a/mysql-test/suite/rpl/r/rpl_row_merge_engine.result b/mysql-test/suite/rpl/r/rpl_row_merge_engine.result new file mode 100644 index 00000000000..c61167e84e0 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_row_merge_engine.result @@ -0,0 +1,16 @@ +include/master-slave.inc +[connection master] +CREATE TABLE t1 (a int) ENGINE=MyISAM; +CREATE TABLE t2 (a int) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (4), (5), (6); +CREATE TABLE IF NOT EXISTS t1_merge LIKE t1; +ALTER TABLE t1_merge ENGINE=MERGE UNION (t2, t1); +include/diff_tables.inc [master:test.t1, slave:test.t1] +include/diff_tables.inc [master:test.t2, slave:test.t2] +UPDATE t1_merge SET a=10 WHERE a=1; +DELETE FROM t1_merge WHERE a=10; +include/diff_tables.inc [master:test.t1, slave:test.t1] +include/diff_tables.inc [master:test.t2, slave:test.t2] +DROP TABLE t1_merge, t1, t2; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_row_merge_engine.test b/mysql-test/suite/rpl/t/rpl_row_merge_engine.test new file mode 100644 index 00000000000..5add8dc1cda --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_row_merge_engine.test @@ -0,0 +1,50 @@ +# +# BUG#47103 +# +# This test case checks whether the slave crashes or not when there is +# a merge table in use. +# +# Description +# =========== +# +# The test case creates two regular MyISAM tables on the master and +# one MERGE table. Then it populates the MyISAM tables, updates and +# deletes their contents through the merge table. Finally, the slave +# is synchronized with the master and (after the fix) it won't crash. +# +--source include/master-slave.inc +--source include/have_binlog_format_row.inc +--connection master + +CREATE TABLE t1 (a int) ENGINE=MyISAM; +CREATE TABLE t2 (a int) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (4), (5), (6); +CREATE TABLE IF NOT EXISTS t1_merge LIKE t1; +ALTER TABLE t1_merge ENGINE=MERGE UNION (t2, t1); + +--sync_slave_with_master + +--let diff_tables=master:test.t1, slave:test.t1 +--source include/diff_tables.inc + +--let diff_tables=master:test.t2, slave:test.t2 +--source include/diff_tables.inc + +--connection master +UPDATE t1_merge SET a=10 WHERE a=1; +DELETE FROM t1_merge WHERE a=10; + +--sync_slave_with_master +--connection master + +--let diff_tables=master:test.t1, slave:test.t1 +--source include/diff_tables.inc + +--let diff_tables=master:test.t2, slave:test.t2 +--source include/diff_tables.inc + +DROP TABLE t1_merge, t1, t2; +--sync_slave_with_master + +--source include/rpl_end.inc diff --git a/sql/log_event.cc b/sql/log_event.cc index 0003a621cbb..6ed7c7c64c7 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -7641,9 +7641,24 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) { DBUG_PRINT("debug", ("Checking compability of tables to lock - tables_to_lock: %p", rli->tables_to_lock)); + + /** + When using RBR and MyISAM MERGE tables the base tables that make + up the MERGE table can be appended to the list of tables to lock. + + Thus, we just check compatibility for those that tables that have + a correspondent table map event (ie, those that are actually going + to be accessed while applying the event). That's why the loop stops + at rli->tables_to_lock_count . + + NOTE: The base tables are added here are removed when + close_thread_tables is called. + */ RPL_TABLE_LIST *ptr= rli->tables_to_lock; - for ( ; ptr ; ptr= static_cast(ptr->next_global)) + for (uint i= 0 ; ptr && (i < rli->tables_to_lock_count); + ptr= static_cast(ptr->next_global), i++) { + DBUG_ASSERT(ptr->m_tabledef_valid); TABLE *conv_table; if (!ptr->m_tabledef.compatible_with(thd, const_cast(rli), ptr->table, &conv_table)) @@ -7681,10 +7696,10 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) Rows_log_event, we can invalidate the query cache for the associated table. */ - for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) - { + TABLE_LIST *ptr= rli->tables_to_lock; + for (uint i=0 ; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++) const_cast(rli)->m_table_map.set_table(ptr->table_id, ptr->table); - } + #ifdef HAVE_QUERY_CACHE query_cache.invalidate_locked_for_write(rli->tables_to_lock); #endif @@ -8466,9 +8481,9 @@ check_table_map(Relay_log_info const *rli, RPL_TABLE_LIST *table_list) res= FILTERED_OUT; else { - for(RPL_TABLE_LIST *ptr= static_cast(rli->tables_to_lock); - ptr; - ptr= static_cast(ptr->next_local)) + RPL_TABLE_LIST *ptr= static_cast(rli->tables_to_lock); + for(uint i=0 ; ptr && (i< rli->tables_to_lock_count); + ptr= static_cast(ptr->next_local), i++) { if (ptr->table_id == table_list->table_id) { diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 4aa8c0959b1..1e5ce3408ed 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -126,8 +126,10 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info { RPL_TABLE_LIST *ptr= rli->tables_to_lock; - for ( ; ptr ; ptr= static_cast(ptr->next_global)) + for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count); + ptr= static_cast(ptr->next_global), i++) { + DBUG_ASSERT(ptr->m_tabledef_valid); TABLE *conv_table; if (!ptr->m_tabledef.compatible_with(thd, const_cast(rli), ptr->table, &conv_table)) @@ -158,10 +160,9 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info Old_rows_log_event, we can invalidate the query cache for the associated table. */ - for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) - { + TABLE_LIST *ptr= rli->tables_to_lock; + for (uint i=0; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++) const_cast(rli)->m_table_map.set_table(ptr->table_id, ptr->table); - } #ifdef HAVE_QUERY_CACHE query_cache.invalidate_locked_for_write(rli->tables_to_lock); #endif @@ -1539,7 +1540,8 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *ptr= rli->tables_to_lock; - for ( ; ptr ; ptr= static_cast(ptr->next_global)) + for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count); + ptr= static_cast(ptr->next_global), i++) { TABLE *conv_table; if (ptr->m_tabledef.compatible_with(thd, const_cast(rli), diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 351ff5843dd..542ae58efae 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1264,6 +1264,23 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) void Relay_log_info::clear_tables_to_lock() { + DBUG_ENTER("Relay_log_info::clear_tables_to_lock()"); +#ifndef DBUG_OFF + /** + When replicating in RBR and MyISAM Merge tables are involved + open_and_lock_tables (called in do_apply_event) appends the + base tables to the list of tables_to_lock. Then these are + removed from the list in close_thread_tables (which is called + before we reach this point). + + This assertion just confirms that we get no surprises at this + point. + */ + uint i=0; + for (TABLE_LIST *ptr= tables_to_lock ; ptr ; ptr= ptr->next_global, i++) ; + DBUG_ASSERT(i == tables_to_lock_count); +#endif + while (tables_to_lock) { uchar* to_free= reinterpret_cast(tables_to_lock); @@ -1288,10 +1305,12 @@ void Relay_log_info::clear_tables_to_lock() my_free(to_free); } DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); + DBUG_VOID_RETURN; } void Relay_log_info::slave_close_thread_tables(THD *thd) { + DBUG_ENTER("Relay_log_info::slave_close_thread_tables(THD *thd)"); thd->stmt_da->can_overwrite_status= TRUE; thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); thd->stmt_da->can_overwrite_status= FALSE; @@ -1313,5 +1332,6 @@ void Relay_log_info::slave_close_thread_tables(THD *thd) thd->mdl_context.release_statement_locks(); clear_tables_to_lock(); + DBUG_VOID_RETURN; } #endif -- cgit v1.2.1 From 152bb4c17df21d65e7d5dd3b77b5af00c7e52802 Mon Sep 17 00:00:00 2001 From: Annamalai Gurusami Date: Mon, 27 Feb 2012 17:23:56 +0530 Subject: Bug#13635833: MULTIPLE CRASHES IN FOREIGN KEY CODE WITH CONCURRENT DDL/DML There are two threads. In one thread, dml operation is going on involving cascaded update operation. In another thread, alter table add foreign key constraint is happening. Under these circumstances, it is possible for the dml thread to access a dict_foreign_t object that has been freed by the ddl thread. The debug sync test case provides the sequence of operations. Without fix, the test case will crash the server (because of newly added assert). With fix, the alter table stmt will return an error message. rb:947 approved by Jimmy Yang --- include/my_base.h | 11 ++-- .../suite/innodb/r/innodb_bug13635833.result | 45 +++++++++++++++ mysql-test/suite/innodb/t/innodb_bug13635833.test | 64 ++++++++++++++++++++++ mysys/my_handler_errors.h | 9 +-- sql/handler.cc | 10 +++- sql/share/errmsg-utf8.txt | 3 + storage/innobase/dict/dict0dict.c | 10 +++- storage/innobase/handler/ha_innodb.cc | 7 ++- storage/innobase/include/db0err.h | 8 ++- storage/innobase/row/row0ins.c | 5 ++ storage/innobase/row/row0mysql.c | 31 ++++++++++- storage/innobase/ut/ut0ut.c | 8 ++- 12 files changed, 186 insertions(+), 25 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug13635833.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug13635833.test diff --git a/include/my_base.h b/include/my_base.h index d9f08a3c467..c10a325763e 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -1,5 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. - reserved +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,8 +10,9 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + /* This file includes constants used with all databases */ @@ -449,7 +449,8 @@ enum ha_base_keytype { #define HA_ERR_INDEX_COL_TOO_LONG 178 /* Index column length exceeds limit */ #define HA_ERR_INDEX_CORRUPT 179 /* Index corrupted */ #define HA_ERR_UNDO_REC_TOO_BIG 180 /* Undo log record too big */ -#define HA_ERR_LAST 180 /* Copy of last error nr */ +#define HA_ERR_TABLE_IN_FK_CHECK 181 /* Table being used in foreign key check */ +#define HA_ERR_LAST 181 /* Copy of last error nr */ /* Number of different errors */ #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) diff --git a/mysql-test/suite/innodb/r/innodb_bug13635833.result b/mysql-test/suite/innodb/r/innodb_bug13635833.result new file mode 100644 index 00000000000..6a505bc94f2 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug13635833.result @@ -0,0 +1,45 @@ +SET DEBUG_SYNC='reset'; +create table t1 (f1 integer, key k1 (f1)) engine=innodb; +create table t2 (f1 int, f2 int, key(f1), key(f2)) engine=innodb; +create table t3 (f2 int, key(f2)) engine=innodb; +insert into t1 values (10); +insert into t2 values (10, 20); +insert into t3 values (20); +alter table t2 add constraint c1 foreign key (f1) +references t1(f1) on update cascade; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f1` int(11) DEFAULT NULL, + KEY `k1` (`f1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `f1` int(11) DEFAULT NULL, + `f2` int(11) DEFAULT NULL, + KEY `f1` (`f1`), + KEY `f2` (`f2`), + CONSTRAINT `c1` FOREIGN KEY (`f1`) REFERENCES `t1` (`f1`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `f2` int(11) DEFAULT NULL, + KEY `f2` (`f2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SET DEBUG_SYNC='innodb_rename_table_ready SIGNAL update_can_proceed + WAIT_FOR dict_unfreeze'; +alter table t2 add constraint z1 foreign key (f2) +references t3(f2) on update cascade; +SET DEBUG_SYNC='innodb_row_update_for_mysql_begin + WAIT_FOR update_can_proceed'; +SET DEBUG_SYNC='innodb_dml_cascade_dict_unfreeze SIGNAL dict_unfreeze + WAIT_FOR foreign_free_cache'; +update ignore t1 set f1 = 20; +ERROR HY000: Error on rename of './test/t2' to '#sql2-temporary' (errno: 181) +SET DEBUG_SYNC='now SIGNAL foreign_free_cache'; +drop table t2; +drop table t1; +drop table t3; +SET DEBUG_SYNC='reset'; diff --git a/mysql-test/suite/innodb/t/innodb_bug13635833.test b/mysql-test/suite/innodb/t/innodb_bug13635833.test new file mode 100644 index 00000000000..47185b9d526 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug13635833.test @@ -0,0 +1,64 @@ +--source include/have_innodb.inc +--source include/have_debug_sync.inc +--source include/not_embedded.inc + +SET DEBUG_SYNC='reset'; + +# Save the initial number of concurrent sessions +--source include/count_sessions.inc + +create table t1 (f1 integer, key k1 (f1)) engine=innodb; +create table t2 (f1 int, f2 int, key(f1), key(f2)) engine=innodb; +create table t3 (f2 int, key(f2)) engine=innodb; + +insert into t1 values (10); +insert into t2 values (10, 20); +insert into t3 values (20); + +alter table t2 add constraint c1 foreign key (f1) + references t1(f1) on update cascade; + +show create table t1; +show create table t2; +show create table t3; + +SET DEBUG_SYNC='innodb_rename_table_ready SIGNAL update_can_proceed + WAIT_FOR dict_unfreeze'; + +--send +alter table t2 add constraint z1 foreign key (f2) + references t3(f2) on update cascade; + +connect (thr2,localhost,root,,); +connection thr2; + +SET DEBUG_SYNC='innodb_row_update_for_mysql_begin + WAIT_FOR update_can_proceed'; +SET DEBUG_SYNC='innodb_dml_cascade_dict_unfreeze SIGNAL dict_unfreeze + WAIT_FOR foreign_free_cache'; + +--send +update ignore t1 set f1 = 20; + +connection default; +--replace_regex /'[^']*test\/#sql2-[0-9a-f-]*'/'#sql2-temporary'/ +--error ER_ERROR_ON_RENAME +reap; + +SET DEBUG_SYNC='now SIGNAL foreign_free_cache'; + +connection thr2; +reap; +disconnect thr2; +--source include/wait_until_disconnected.inc + +connection default; + +drop table t2; +drop table t1; +drop table t3; + +# Wait till we reached the initial number of concurrent sessions +--source include/wait_until_count_sessions.inc + +SET DEBUG_SYNC='reset'; diff --git a/mysys/my_handler_errors.h b/mysys/my_handler_errors.h index 3533b633960..1c2f02f26cb 100644 --- a/mysys/my_handler_errors.h +++ b/mysys/my_handler_errors.h @@ -1,7 +1,7 @@ #ifndef MYSYS_MY_HANDLER_ERRORS_INCLUDED #define MYSYS_MY_HANDLER_ERRORS_INCLUDED -/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,8 +13,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ /* Errors a handler can give you @@ -83,7 +83,8 @@ static const char *handler_error_messages[]= "Too many active concurrent transactions", "Index column length exceeds limit", "Index corrupted", - "Undo record too big" + "Undo record too big", + "Table is being used in foreign key check" }; extern void my_handler_error_register(void); diff --git a/sql/handler.cc b/sql/handler.cc index b6df46ed48d..8ce20c19d2d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -10,8 +10,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ /** @file handler.cc @@ -359,6 +359,7 @@ int ha_init_errors(void) SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER_DEFAULT(ER_TOO_MANY_CONCURRENT_TRXS)); SETMSG(HA_ERR_INDEX_COL_TOO_LONG, ER_DEFAULT(ER_INDEX_COLUMN_TOO_LONG)); SETMSG(HA_ERR_INDEX_CORRUPT, ER_DEFAULT(ER_INDEX_CORRUPT)); + SETMSG(HA_ERR_TABLE_IN_FK_CHECK, ER_DEFAULT(ER_TABLE_IN_FK_CHECK)); /* Register the error messages for use with my_error(). */ return my_error_register(get_handler_errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -2878,6 +2879,9 @@ void handler::print_error(int error, myf errflag) case HA_ERR_UNDO_REC_TOO_BIG: textno= ER_UNDO_RECORD_TOO_BIG; break; + case HA_ERR_TABLE_IN_FK_CHECK: + textno= ER_TABLE_IN_FK_CHECK; + break; default: { /* The error was "unknown" to this function. diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index d164f75d7d8..597bb12a137 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6494,3 +6494,6 @@ ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC eng "CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave." +ER_TABLE_IN_FK_CHECK + eng "Table is being used in foreign key check." + diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index 9dc3cef229e..c6b0fa0d623 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -55,6 +55,8 @@ UNIV_INTERN dict_index_t* dict_ind_compact; #include "m_ctype.h" /* my_isspace() */ #include "ha_prototypes.h" /* innobase_strcasecmp(), innobase_casedn_str()*/ #include "row0upd.h" +#include "m_string.h" +#include "my_sys.h" #include @@ -2329,6 +2331,8 @@ dict_foreign_free( /*==============*/ dict_foreign_t* foreign) /*!< in, own: foreign key struct */ { + ut_a(foreign->foreign_table->n_foreign_key_checks_running == 0); + mem_heap_free(foreign->heap); } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 204e7f7a1e3..fc4a564795e 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2000, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2000, 2012, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. @@ -998,6 +998,9 @@ convert_error_code_to_mysql( case DB_OUT_OF_FILE_SPACE: return(HA_ERR_RECORD_FILE_FULL); + case DB_TABLE_IN_FK_CHECK: + return(HA_ERR_TABLE_IN_FK_CHECK); + case DB_TABLE_IS_BEING_USED: return(HA_ERR_WRONG_COMMAND); @@ -7573,6 +7576,8 @@ innobase_rename_table( normalize_table_name(norm_to, to); normalize_table_name(norm_from, from); + DEBUG_SYNC_C("innodb_rename_table_ready"); + /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h index e0952f0709d..95ccef16be0 100644 --- a/storage/innobase/include/db0err.h +++ b/storage/innobase/include/db0err.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -112,6 +112,8 @@ enum db_err { limit */ DB_INDEX_CORRUPT, /* we have corrupted index */ DB_UNDO_RECORD_TOO_BIG, /* the undo log record is too big */ + DB_TABLE_IN_FK_CHECK, /* table is being used in foreign + key check */ /* The following are partial failure codes */ DB_FAIL = 1000, diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index d02d0e986aa..2b77c6f929d 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -49,6 +49,8 @@ Created 4/20/1996 Heikki Tuuri #include "data0data.h" #include "usr0sess.h" #include "buf0lru.h" +#include "m_string.h" +#include "my_sys.h" #define ROW_INS_PREV 1 #define ROW_INS_NEXT 2 @@ -1085,6 +1087,9 @@ row_ins_foreign_check_on_constraint( release the latch. */ row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); + + DEBUG_SYNC_C("innodb_dml_cascade_dict_unfreeze"); + row_mysql_freeze_data_dictionary(thr_get_trx(thr)); mtr_start(mtr); diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 3f81604a1a2..837adc84818 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2000, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2000, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -51,6 +51,9 @@ Created 9/17/2000 Heikki Tuuri #include "btr0sea.h" #include "fil0fil.h" #include "ibuf0ibuf.h" +#include "m_string.h" +#include "my_sys.h" + /** Provide optional 4.x backwards compatibility for 5.0 and above */ UNIV_INTERN ibool row_rollback_on_timeout = FALSE; @@ -1443,6 +1446,8 @@ row_update_for_mysql( return(DB_ERROR); } + DEBUG_SYNC_C("innodb_row_update_for_mysql_begin"); + trx->op_info = "updating or deleting"; row_mysql_delay_if_needed(); @@ -3803,6 +3808,7 @@ row_rename_table_for_mysql( ulint n_constraints_to_drop = 0; ibool old_is_tmp, new_is_tmp; pars_info_t* info = NULL; + int retry; ut_a(old_name != NULL); ut_a(new_name != NULL); @@ -3885,6 +3891,25 @@ row_rename_table_for_mysql( } } + /* Is a foreign key check running on this table? */ + for (retry = 0; retry < 100 + && table->n_foreign_key_checks_running > 0; ++retry) { + row_mysql_unlock_data_dictionary(trx); + os_thread_yield(); + row_mysql_lock_data_dictionary(trx); + } + + if (table->n_foreign_key_checks_running > 0) { + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: in ALTER TABLE ", stderr); + ut_print_name(stderr, trx, TRUE, old_name); + fprintf(stderr, "\n" + "InnoDB: a FOREIGN KEY check is running.\n" + "InnoDB: Cannot rename table.\n"); + err = DB_TABLE_IN_FK_CHECK; + goto funct_exit; + } + /* We use the private SQL parser of Innobase to generate the query graphs needed in updating the dictionary data from system tables. */ diff --git a/storage/innobase/ut/ut0ut.c b/storage/innobase/ut/ut0ut.c index f6dfb3ba0b3..117a777cb98 100644 --- a/storage/innobase/ut/ut0ut.c +++ b/storage/innobase/ut/ut0ut.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2011, Oracle Corpn. All Rights Reserved. +Copyright (c) 2011, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -718,6 +718,8 @@ ut_strerr( return("Undo record too big"); case DB_END_OF_INDEX: return("End of index"); + case DB_TABLE_IN_FK_CHECK: + return("Table is being used in foreign key check"); /* do not add default: in order to produce a warning if new code is added to the enum but not added here */ } -- cgit v1.2.1 From 27ecea534c6921c9cedd0d378caafda3e632c250 Mon Sep 17 00:00:00 2001 From: Annamalai Gurusami Date: Thu, 1 Mar 2012 11:05:51 +0530 Subject: Bug#13635833: MULTIPLE CRASHES IN FOREIGN KEY CODE WITH CONCURRENT DDL/DML There are two threads. In one thread, dml operation is going on involving cascaded update operation. In another thread, alter table add foreign key constraint is happening. Under these circumstances, it is possible for the dml thread to access a dict_foreign_t object that has been freed by the ddl thread. The debug sync test case provides the sequence of operations. Without fix, the test case will crash the server (because of newly added assert). With fix, the alter table stmt will return an error message. Backporting the fix from MySQL 5.5 to 5.1 rb:961 rb:947 --- include/my_base.h | 7 ++- .../suite/innodb/r/innodb_bug13635833.result | 45 +++++++++++++++ mysql-test/suite/innodb/t/innodb_bug13635833.test | 64 ++++++++++++++++++++++ .../innodb_plugin/r/innodb_bug13635833.result | 45 +++++++++++++++ .../suite/innodb_plugin/t/innodb_bug13635833.test | 64 ++++++++++++++++++++++ mysys/my_handler_errors.h | 6 +- sql/handler.cc | 2 + storage/innobase/dict/dict0dict.c | 4 ++ storage/innobase/handler/ha_innodb.cc | 4 ++ storage/innobase/include/db0err.h | 2 + storage/innobase/row/row0ins.c | 3 + storage/innobase/row/row0mysql.c | 24 ++++++++ storage/innodb_plugin/dict/dict0dict.c | 4 ++ storage/innodb_plugin/handler/ha_innodb.cc | 3 + storage/innodb_plugin/include/db0err.h | 2 + storage/innodb_plugin/row/row0ins.c | 3 + storage/innodb_plugin/row/row0mysql.c | 24 ++++++++ 17 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug13635833.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug13635833.test create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug13635833.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test diff --git a/include/my_base.h b/include/my_base.h index 80ba4bcd004..ffd68046029 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -445,7 +445,12 @@ enum ha_base_keytype { #define HA_ERR_FILE_TOO_SHORT 175 /* File too short */ #define HA_ERR_WRONG_CRC 176 /* Wrong CRC on page */ #define HA_ERR_TOO_MANY_CONCURRENT_TRXS 177 /*Too many active concurrent transactions */ -#define HA_ERR_LAST 177 /* Copy of last error nr */ + +/* The error codes from 178 to 180 is not used, because we need to +maintain forward compatibility with higher versions. */ + +#define HA_ERR_TABLE_IN_FK_CHECK 181 /* Table being used in foreign key check */ +#define HA_ERR_LAST 181 /* Copy of last error nr */ /* Number of different errors */ #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) diff --git a/mysql-test/suite/innodb/r/innodb_bug13635833.result b/mysql-test/suite/innodb/r/innodb_bug13635833.result new file mode 100644 index 00000000000..e9107d5c125 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug13635833.result @@ -0,0 +1,45 @@ +SET DEBUG_SYNC='reset'; +create table t1 (f1 integer, key k1 (f1)) engine=innodb; +create table t2 (f1 int, f2 int, key(f1), key(f2)) engine=innodb; +create table t3 (f2 int, key(f2)) engine=innodb; +insert into t1 values (10); +insert into t2 values (10, 20); +insert into t3 values (20); +alter table t2 add constraint c1 foreign key (f1) +references t1(f1) on update cascade; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f1` int(11) DEFAULT NULL, + KEY `k1` (`f1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `f1` int(11) DEFAULT NULL, + `f2` int(11) DEFAULT NULL, + KEY `f1` (`f1`), + KEY `f2` (`f2`), + CONSTRAINT `c1` FOREIGN KEY (`f1`) REFERENCES `t1` (`f1`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `f2` int(11) DEFAULT NULL, + KEY `f2` (`f2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SET DEBUG_SYNC='alter_table_before_rename_result_table + SIGNAL update_can_proceed WAIT_FOR dict_unfreeze'; +alter table t2 add constraint z1 foreign key (f2) +references t3(f2) on update cascade; +SET DEBUG_SYNC='innodb_row_update_for_mysql_begin + WAIT_FOR update_can_proceed'; +SET DEBUG_SYNC='innodb_dml_cascade_dict_unfreeze SIGNAL dict_unfreeze + WAIT_FOR foreign_free_cache'; +update ignore t1 set f1 = 20; +ERROR HY000: Error on rename of './test/t2' to '#sql2-temporary' (errno: 181) +SET DEBUG_SYNC='now SIGNAL foreign_free_cache'; +drop table t2; +drop table t1; +drop table t3; +SET DEBUG_SYNC='reset'; diff --git a/mysql-test/suite/innodb/t/innodb_bug13635833.test b/mysql-test/suite/innodb/t/innodb_bug13635833.test new file mode 100644 index 00000000000..b66b99b7681 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug13635833.test @@ -0,0 +1,64 @@ +--source include/have_innodb.inc +--source include/have_debug_sync.inc +--source include/not_embedded.inc + +SET DEBUG_SYNC='reset'; + +# Save the initial number of concurrent sessions +--source include/count_sessions.inc + +create table t1 (f1 integer, key k1 (f1)) engine=innodb; +create table t2 (f1 int, f2 int, key(f1), key(f2)) engine=innodb; +create table t3 (f2 int, key(f2)) engine=innodb; + +insert into t1 values (10); +insert into t2 values (10, 20); +insert into t3 values (20); + +alter table t2 add constraint c1 foreign key (f1) + references t1(f1) on update cascade; + +show create table t1; +show create table t2; +show create table t3; + +SET DEBUG_SYNC='alter_table_before_rename_result_table + SIGNAL update_can_proceed WAIT_FOR dict_unfreeze'; + +--send +alter table t2 add constraint z1 foreign key (f2) + references t3(f2) on update cascade; + +connect (thr2,localhost,root,,); +connection thr2; + +SET DEBUG_SYNC='innodb_row_update_for_mysql_begin + WAIT_FOR update_can_proceed'; +SET DEBUG_SYNC='innodb_dml_cascade_dict_unfreeze SIGNAL dict_unfreeze + WAIT_FOR foreign_free_cache'; + +--send +update ignore t1 set f1 = 20; + +connection default; +--replace_regex /'[^']*test\/#sql2-[0-9a-f-]*'/'#sql2-temporary'/ +--error ER_ERROR_ON_RENAME +reap; + +SET DEBUG_SYNC='now SIGNAL foreign_free_cache'; + +connection thr2; +reap; +disconnect thr2; +--source include/wait_until_disconnected.inc + +connection default; + +drop table t2; +drop table t1; +drop table t3; + +# Wait till we reached the initial number of concurrent sessions +--source include/wait_until_count_sessions.inc + +SET DEBUG_SYNC='reset'; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug13635833.result b/mysql-test/suite/innodb_plugin/r/innodb_bug13635833.result new file mode 100644 index 00000000000..e9107d5c125 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug13635833.result @@ -0,0 +1,45 @@ +SET DEBUG_SYNC='reset'; +create table t1 (f1 integer, key k1 (f1)) engine=innodb; +create table t2 (f1 int, f2 int, key(f1), key(f2)) engine=innodb; +create table t3 (f2 int, key(f2)) engine=innodb; +insert into t1 values (10); +insert into t2 values (10, 20); +insert into t3 values (20); +alter table t2 add constraint c1 foreign key (f1) +references t1(f1) on update cascade; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f1` int(11) DEFAULT NULL, + KEY `k1` (`f1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `f1` int(11) DEFAULT NULL, + `f2` int(11) DEFAULT NULL, + KEY `f1` (`f1`), + KEY `f2` (`f2`), + CONSTRAINT `c1` FOREIGN KEY (`f1`) REFERENCES `t1` (`f1`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `f2` int(11) DEFAULT NULL, + KEY `f2` (`f2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SET DEBUG_SYNC='alter_table_before_rename_result_table + SIGNAL update_can_proceed WAIT_FOR dict_unfreeze'; +alter table t2 add constraint z1 foreign key (f2) +references t3(f2) on update cascade; +SET DEBUG_SYNC='innodb_row_update_for_mysql_begin + WAIT_FOR update_can_proceed'; +SET DEBUG_SYNC='innodb_dml_cascade_dict_unfreeze SIGNAL dict_unfreeze + WAIT_FOR foreign_free_cache'; +update ignore t1 set f1 = 20; +ERROR HY000: Error on rename of './test/t2' to '#sql2-temporary' (errno: 181) +SET DEBUG_SYNC='now SIGNAL foreign_free_cache'; +drop table t2; +drop table t1; +drop table t3; +SET DEBUG_SYNC='reset'; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test b/mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test new file mode 100644 index 00000000000..90df623e8a0 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test @@ -0,0 +1,64 @@ +--source include/have_innodb_plugin.inc +--source include/have_debug_sync.inc +--source include/not_embedded.inc + +SET DEBUG_SYNC='reset'; + +# Save the initial number of concurrent sessions +--source include/count_sessions.inc + +create table t1 (f1 integer, key k1 (f1)) engine=innodb; +create table t2 (f1 int, f2 int, key(f1), key(f2)) engine=innodb; +create table t3 (f2 int, key(f2)) engine=innodb; + +insert into t1 values (10); +insert into t2 values (10, 20); +insert into t3 values (20); + +alter table t2 add constraint c1 foreign key (f1) + references t1(f1) on update cascade; + +show create table t1; +show create table t2; +show create table t3; + +SET DEBUG_SYNC='alter_table_before_rename_result_table + SIGNAL update_can_proceed WAIT_FOR dict_unfreeze'; + +--send +alter table t2 add constraint z1 foreign key (f2) + references t3(f2) on update cascade; + +connect (thr2,localhost,root,,); +connection thr2; + +SET DEBUG_SYNC='innodb_row_update_for_mysql_begin + WAIT_FOR update_can_proceed'; +SET DEBUG_SYNC='innodb_dml_cascade_dict_unfreeze SIGNAL dict_unfreeze + WAIT_FOR foreign_free_cache'; + +--send +update ignore t1 set f1 = 20; + +connection default; +--replace_regex /'[^']*test\/#sql2-[0-9a-f-]*'/'#sql2-temporary'/ +--error ER_ERROR_ON_RENAME +reap; + +SET DEBUG_SYNC='now SIGNAL foreign_free_cache'; + +connection thr2; +reap; +disconnect thr2; +--source include/wait_until_disconnected.inc + +connection default; + +drop table t2; +drop table t1; +drop table t3; + +# Wait till we reached the initial number of concurrent sessions +--source include/wait_until_count_sessions.inc + +SET DEBUG_SYNC='reset'; diff --git a/mysys/my_handler_errors.h b/mysys/my_handler_errors.h index 44c79466e84..1710ac81395 100644 --- a/mysys/my_handler_errors.h +++ b/mysys/my_handler_errors.h @@ -78,6 +78,10 @@ static const char *handler_error_messages[]= "Got a fatal error during initialzaction of handler", "File to short; Expected more data in file", "Read page with wrong checksum", - "Too many active concurrent transactions" + "Too many active concurrent transactions", + "", + "", + "", + "Table is being used in foreign key check" /* HA_ERR_TABLE_IN_FK_CHECK */ }; diff --git a/sql/handler.cc b/sql/handler.cc index 895e7cc4df0..d2eaab19ad8 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -345,6 +345,7 @@ int ha_init_errors(void) SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED)); SETMSG(HA_ERR_AUTOINC_ERANGE, ER(ER_WARN_DATA_OUT_OF_RANGE)); SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER(ER_TOO_MANY_CONCURRENT_TRXS)); + SETMSG(HA_ERR_TABLE_IN_FK_CHECK, "Table being used in foreign key check"); /* Register the error messages for use with my_error(). */ return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -2820,6 +2821,7 @@ void handler::print_error(int error, myf errflag) case HA_ERR_TOO_MANY_CONCURRENT_TRXS: textno= ER_TOO_MANY_CONCURRENT_TRXS; break; + case HA_ERR_TABLE_IN_FK_CHECK: default: { /* The error was "unknown" to this function. diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index beea0a2f411..b2baa81b73f 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -26,6 +26,8 @@ Created 1/8/1996 Heikki Tuuri #include "pars0sym.h" #include "que0que.h" #include "rem0cmp.h" +#include "m_string.h" +#include "my_sys.h" #ifndef UNIV_HOTBACKUP # include "m_ctype.h" /* my_isspace() */ #endif /* !UNIV_HOTBACKUP */ @@ -1889,6 +1891,8 @@ dict_foreign_free( /*==============*/ dict_foreign_t* foreign) /* in, own: foreign key struct */ { + ut_a(foreign->foreign_table->n_foreign_key_checks_running == 0); + mem_heap_free(foreign->heap); } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 20ab9c7a101..c59c02abe1b 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -746,6 +746,10 @@ convert_error_code_to_mysql( return(HA_ERR_RECORD_FILE_FULL); + } else if (error == (int) DB_TABLE_IN_FK_CHECK) { + + return(HA_ERR_TABLE_IN_FK_CHECK); + } else if (error == (int) DB_TABLE_IS_BEING_USED) { return(HA_ERR_WRONG_COMMAND); diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h index bab6fa46370..1514b19639c 100644 --- a/storage/innobase/include/db0err.h +++ b/storage/innobase/include/db0err.h @@ -82,6 +82,8 @@ Created 5/24/1996 Heikki Tuuri #define DB_REFERENCING_NO_INDEX 52 /* the parent (referencing) table does not have an index that contains the foreign keys as its prefix columns */ +#define DB_TABLE_IN_FK_CHECK 53 /* table is being used in foreign + key check */ /* The following are partial failure codes */ #define DB_FAIL 1000 diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index bb5c95a572b..785a0426195 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -1074,6 +1074,9 @@ row_ins_foreign_check_on_constraint( release the latch. */ row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); + + DEBUG_SYNC_C("innodb_dml_cascade_dict_unfreeze"); + row_mysql_freeze_data_dictionary(thr_get_trx(thr)); mtr_start(mtr); diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 6148b01af9d..e8b3c485b0e 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -31,6 +31,8 @@ Created 9/17/2000 Heikki Tuuri #include "btr0sea.h" #include "fil0fil.h" #include "ibuf0ibuf.h" +#include "m_string.h" +#include "my_sys.h" /* A dummy variable used to fool the compiler */ ibool row_mysql_identically_false = FALSE; @@ -1373,6 +1375,8 @@ row_update_for_mysql( return(DB_ERROR); } + DEBUG_SYNC_C("innodb_row_update_for_mysql_begin"); + trx->op_info = "updating or deleting"; row_mysql_delay_if_needed(); @@ -3652,6 +3656,7 @@ row_rename_table_for_mysql( ulint n_constraints_to_drop = 0; ibool old_is_tmp, new_is_tmp; pars_info_t* info = NULL; + ulint retry = 0; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_a(old_name != NULL); @@ -3750,6 +3755,25 @@ row_rename_table_for_mysql( } } + /* Is a foreign key check running on this table? */ + for (retry = 0; retry < 100 + && table->n_foreign_key_checks_running > 0; ++retry) { + row_mysql_unlock_data_dictionary(trx); + os_thread_yield(); + row_mysql_lock_data_dictionary(trx); + } + + if (table->n_foreign_key_checks_running > 0) { + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: in ALTER TABLE ", stderr); + ut_print_name(stderr, trx, TRUE, old_name); + fprintf(stderr, "\n" + "InnoDB: a FOREIGN KEY check is running.\n" + "InnoDB: Cannot rename table.\n"); + err = DB_TABLE_IN_FK_CHECK; + goto funct_exit; + } + /* We use the private SQL parser of Innobase to generate the query graphs needed in deleting the dictionary data from system tables in Innobase. Deleting a row from SYS_INDEXES table also frees the file diff --git a/storage/innodb_plugin/dict/dict0dict.c b/storage/innodb_plugin/dict/dict0dict.c index 2d2262acf87..b70d5929f53 100644 --- a/storage/innodb_plugin/dict/dict0dict.c +++ b/storage/innodb_plugin/dict/dict0dict.c @@ -24,6 +24,8 @@ Created 1/8/1996 Heikki Tuuri ***********************************************************************/ #include "dict0dict.h" +#include "m_string.h" +#include "my_sys.h" #ifdef UNIV_NONINL #include "dict0dict.ic" @@ -2272,6 +2274,8 @@ dict_foreign_free( /*==============*/ dict_foreign_t* foreign) /*!< in, own: foreign key struct */ { + ut_a(foreign->foreign_table->n_foreign_key_checks_running == 0); + mem_heap_free(foreign->heap); } diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 09094c0146e..47d309f2360 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -868,6 +868,9 @@ convert_error_code_to_mysql( case DB_OUT_OF_FILE_SPACE: return(HA_ERR_RECORD_FILE_FULL); + case DB_TABLE_IN_FK_CHECK: + return(HA_ERR_TABLE_IN_FK_CHECK); + case DB_TABLE_IS_BEING_USED: return(HA_ERR_WRONG_COMMAND); diff --git a/storage/innodb_plugin/include/db0err.h b/storage/innodb_plugin/include/db0err.h index c7fa6d2a444..e4a3844cd22 100644 --- a/storage/innodb_plugin/include/db0err.h +++ b/storage/innodb_plugin/include/db0err.h @@ -97,6 +97,8 @@ enum db_err { DB_FOREIGN_EXCEED_MAX_CASCADE, /* Foreign key constraint related cascading delete/update exceeds maximum allowed depth */ + DB_TABLE_IN_FK_CHECK, /* table is being used in foreign + key check */ /* The following are partial failure codes */ DB_FAIL = 1000, diff --git a/storage/innodb_plugin/row/row0ins.c b/storage/innodb_plugin/row/row0ins.c index 9a603d5690f..2d4afa6c5ed 100644 --- a/storage/innodb_plugin/row/row0ins.c +++ b/storage/innodb_plugin/row/row0ins.c @@ -1102,6 +1102,9 @@ row_ins_foreign_check_on_constraint( release the latch. */ row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); + + DEBUG_SYNC_C("innodb_dml_cascade_dict_unfreeze"); + row_mysql_freeze_data_dictionary(thr_get_trx(thr)); mtr_start(mtr); diff --git a/storage/innodb_plugin/row/row0mysql.c b/storage/innodb_plugin/row/row0mysql.c index 36aad4f8ca2..cd46244eb34 100644 --- a/storage/innodb_plugin/row/row0mysql.c +++ b/storage/innodb_plugin/row/row0mysql.c @@ -51,6 +51,8 @@ Created 9/17/2000 Heikki Tuuri #include "btr0sea.h" #include "fil0fil.h" #include "ibuf0ibuf.h" +#include "m_string.h" +#include "my_sys.h" /** Provide optional 4.x backwards compatibility for 5.0 and above */ UNIV_INTERN ibool row_rollback_on_timeout = FALSE; @@ -1350,6 +1352,8 @@ row_update_for_mysql( return(DB_ERROR); } + DEBUG_SYNC_C("innodb_row_update_for_mysql_begin"); + trx->op_info = "updating or deleting"; row_mysql_delay_if_needed(); @@ -3741,6 +3745,7 @@ row_rename_table_for_mysql( ulint n_constraints_to_drop = 0; ibool old_is_tmp, new_is_tmp; pars_info_t* info = NULL; + ulint retry = 0; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_a(old_name != NULL); @@ -3824,6 +3829,25 @@ row_rename_table_for_mysql( } } + /* Is a foreign key check running on this table? */ + for (retry = 0; retry < 100 + && table->n_foreign_key_checks_running > 0; ++retry) { + row_mysql_unlock_data_dictionary(trx); + os_thread_yield(); + row_mysql_lock_data_dictionary(trx); + } + + if (table->n_foreign_key_checks_running > 0) { + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: in ALTER TABLE ", stderr); + ut_print_name(stderr, trx, TRUE, old_name); + fprintf(stderr, "\n" + "InnoDB: a FOREIGN KEY check is running.\n" + "InnoDB: Cannot rename table.\n"); + err = DB_TABLE_IN_FK_CHECK; + goto funct_exit; + } + /* We use the private SQL parser of Innobase to generate the query graphs needed in updating the dictionary data from system tables. */ -- cgit v1.2.1 From 98642459db8481a90f54bddd9fc24aa4a06df805 Mon Sep 17 00:00:00 2001 From: Annamalai Gurusami Date: Thu, 1 Mar 2012 15:44:23 +0530 Subject: The innodb plugin module cannot use DEBUG_SYNC_C facility on Windows. Taking care of it. --- mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test | 2 ++ storage/innodb_plugin/row/row0mysql.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test b/mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test index 90df623e8a0..5db7a41e201 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug13635833.test @@ -1,6 +1,8 @@ --source include/have_innodb_plugin.inc --source include/have_debug_sync.inc --source include/not_embedded.inc +# InnoDB Plugin cannot use DEBUG_SYNC on Windows +--source include/not_windows.inc SET DEBUG_SYNC='reset'; diff --git a/storage/innodb_plugin/row/row0mysql.c b/storage/innodb_plugin/row/row0mysql.c index cd46244eb34..b7df3479a3c 100644 --- a/storage/innodb_plugin/row/row0mysql.c +++ b/storage/innodb_plugin/row/row0mysql.c @@ -51,8 +51,14 @@ Created 9/17/2000 Heikki Tuuri #include "btr0sea.h" #include "fil0fil.h" #include "ibuf0ibuf.h" -#include "m_string.h" -#include "my_sys.h" + +#ifdef __WIN__ +/* error LNK2001: unresolved external symbol _debug_sync_C_callback_ptr */ +# define DEBUG_SYNC_C(dummy) ((void) 0) +#else +# include "m_string.h" /* for my_sys.h */ +# include "my_sys.h" /* DEBUG_SYNC_C */ +#endif /** Provide optional 4.x backwards compatibility for 5.0 and above */ UNIV_INTERN ibool row_rollback_on_timeout = FALSE; -- cgit v1.2.1 From 44d88da575abd81bed9ef036c98de2d5b0862d0c Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Mon, 5 Mar 2012 21:58:07 +0400 Subject: Fix for BUG#12414917 - ISCLOSED() CRASHES ON 64-BIT BUILDS Problem: lack of incoming geometry data validation may lead to a server crash when ISCLOSED() function called. Solution: necessary incoming data check added. --- mysql-test/r/gis.result | 6 ++++++ mysql-test/t/gis.test | 7 +++++++ sql/spatial.cc | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index acb55d225a7..9b901e0f93f 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -1075,4 +1075,10 @@ SPATIAL INDEX i1 (col1, col2) ); ERROR HY000: Incorrect arguments to SPATIAL INDEX DROP TABLE t0, t1, t2; +# +# BUG#12414917 - ISCLOSED() CRASHES ON 64-BIT BUILDS +# +SELECT ISCLOSED(CONVERT(CONCAT(' ', 0x2), BINARY(20))); +ISCLOSED(CONVERT(CONCAT(' ', 0x2), BINARY(20))) +NULL End of 5.1 tests diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test index f8cec14d9ae..fbd4c87cb97 100644 --- a/mysql-test/t/gis.test +++ b/mysql-test/t/gis.test @@ -812,4 +812,11 @@ CREATE TABLE t3 ( # cleanup DROP TABLE t0, t1, t2; + +--echo # +--echo # BUG#12414917 - ISCLOSED() CRASHES ON 64-BIT BUILDS +--echo # +SELECT ISCLOSED(CONVERT(CONCAT(' ', 0x2), BINARY(20))); + + --echo End of 5.1 tests diff --git a/sql/spatial.cc b/sql/spatial.cc index ed3d72b91ec..0d2dd81c71e 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -627,7 +627,8 @@ int Gis_line_string::is_closed(int *closed) const return 0; } data+= 4; - if (no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) + if (n_points == 0 || + no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) return 1; /* Get first point */ -- cgit v1.2.1 From 97c429f65b14335a3322c0c78700582e136607ec Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Mon, 5 Mar 2012 22:15:23 +0400 Subject: BUG#12537203 - CRASH WHEN SUBSELECTING GLOBAL VARIABLES IN GEOMETRY FUNCTION ARGUMENTS A defect in the subquery substitution code may lead to a server crash: setting substitution's name should be followed by setting its length (to keep them in sync). --- mysql-test/r/gis.result | 6 ++++++ mysql-test/t/gis.test | 7 +++++++ sql/item_subselect.cc | 1 + 3 files changed, 14 insertions(+) diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index 9b901e0f93f..9af100e479c 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -1081,4 +1081,10 @@ DROP TABLE t0, t1, t2; SELECT ISCLOSED(CONVERT(CONCAT(' ', 0x2), BINARY(20))); ISCLOSED(CONVERT(CONCAT(' ', 0x2), BINARY(20))) NULL +# +# BUG#12537203 - CRASH WHEN SUBSELECTING GLOBAL VARIABLES IN +# GEOMETRY FUNCTION ARGUMENTS +# +SELECT GEOMETRYCOLLECTION((SELECT @@OLD)); +ERROR 22007: Illegal non geometric '' value found during parsing End of 5.1 tests diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test index fbd4c87cb97..c2a1416f9a1 100644 --- a/mysql-test/t/gis.test +++ b/mysql-test/t/gis.test @@ -818,5 +818,12 @@ DROP TABLE t0, t1, t2; --echo # SELECT ISCLOSED(CONVERT(CONCAT(' ', 0x2), BINARY(20))); +--echo # +--echo # BUG#12537203 - CRASH WHEN SUBSELECTING GLOBAL VARIABLES IN +--echo # GEOMETRY FUNCTION ARGUMENTS +--echo # +--error ER_ILLEGAL_VALUE_FOR_TYPE +SELECT GEOMETRYCOLLECTION((SELECT @@OLD)); + --echo End of 5.1 tests diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index e9e2e8bacf9..8335ae2ca8d 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -173,6 +173,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) (*ref)= substitution; substitution->name= name; + substitution->name_length= name_length; if (have_to_be_excluded) engine->exclude(); substitution= 0; -- cgit v1.2.1 From 930a5b918d36ee7190d973eab45c6fcd031865a7 Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Tue, 6 Mar 2012 15:13:56 +0400 Subject: BUG#12537203 - CRASH WHEN SUBSELECTING GLOBAL VARIABLES IN GEOMETRY FUNCTION ARGUMENTS Fixed --ps-protocol gis test failure. --- mysql-test/t/gis.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test index c2a1416f9a1..abda3e997bd 100644 --- a/mysql-test/t/gis.test +++ b/mysql-test/t/gis.test @@ -822,6 +822,7 @@ SELECT ISCLOSED(CONVERT(CONCAT(' ', 0x2), BINARY(20))); --echo # BUG#12537203 - CRASH WHEN SUBSELECTING GLOBAL VARIABLES IN --echo # GEOMETRY FUNCTION ARGUMENTS --echo # +--replace_regex /non geometric .* value/non geometric '' value/ --error ER_ILLEGAL_VALUE_FOR_TYPE SELECT GEOMETRYCOLLECTION((SELECT @@OLD)); -- cgit v1.2.1 From 35d865f2fe497fb5bf15810cf31f717d5d9c4e46 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 9 Mar 2012 15:04:49 +0200 Subject: Bug #12408412: GROUP_CONCAT + ORDER BY + INPUT/OUTPUT SAME USER VARIABLE = CRASH Moved the preparation of the variables that receive the output from SELECT INTO from execution time (JOIN:execute) to compile time (JOIN::prepare). This ensures that if the same variable is used in the SELECT part of SELECT INTO it will be properly marked as non-const for this query. Test case added. Used proper fast iterator. --- mysql-test/r/user_var.result | 6 ++++++ mysql-test/t/user_var.test | 9 +++++++++ sql/sql_class.cc | 43 ++++++++++++++++++++++++++++++++++++------- sql/sql_class.h | 1 + 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result index 589186184c3..b0859658077 100644 --- a/mysql-test/r/user_var.result +++ b/mysql-test/r/user_var.result @@ -485,4 +485,10 @@ f1 f2 1 4 DROP TRIGGER trg1; DROP TABLE t1; +# +# Bug #12408412: GROUP_CONCAT + ORDER BY + INPUT/OUTPUT +# SAME USER VARIABLE = CRASH +# +SET @bug12408412=1; +SELECT GROUP_CONCAT(@bug12408412 ORDER BY 1) INTO @bug12408412; End of 5.5 tests diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test index 6a64343b609..fa23d118634 100644 --- a/mysql-test/t/user_var.test +++ b/mysql-test/t/user_var.test @@ -404,4 +404,13 @@ SELECT f1, f2 FROM t1 ORDER BY f2; DROP TRIGGER trg1; DROP TABLE t1; + +--echo # +--echo # Bug #12408412: GROUP_CONCAT + ORDER BY + INPUT/OUTPUT +--echo # SAME USER VARIABLE = CRASH +--echo # + +SET @bug12408412=1; +SELECT GROUP_CONCAT(@bug12408412 ORDER BY 1) INTO @bug12408412; + --echo End of 5.5 tests diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b68726c2920..20ab1b71542 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2855,13 +2855,42 @@ bool select_exists_subselect::send_data(List &items) int select_dumpvar::prepare(List &list, SELECT_LEX_UNIT *u) { unit= u; + List_iterator_fast var_li(var_list); + List_iterator_fast it(list); + Item *item; + my_var *mv; + Item_func_set_user_var **suv; if (var_list.elements != list.elements) { my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0)); return 1; - } + } + + /* + Iterate over the destination variables and mark them as being + updated in this query. + We need to do this at JOIN::prepare time to ensure proper + const detection of Item_func_get_user_var that is determined + by the presence of Item_func_set_user_vars + */ + + suv= set_var_items= (Item_func_set_user_var **) + sql_alloc(sizeof(Item_func_set_user_var *) * list.elements); + + while ((mv= var_li++) && (item= it++)) + { + if (!mv->local) + { + *suv= new Item_func_set_user_var(mv->s, item); + (*suv)->fix_fields(thd, 0); + } + else + *suv= NULL; + suv++; + } + return 0; } @@ -3178,6 +3207,7 @@ bool select_dumpvar::send_data(List &items) List_iterator it(items); Item *item; my_var *mv; + Item_func_set_user_var **suv; DBUG_ENTER("select_dumpvar::send_data"); if (unit->offset_limit_cnt) @@ -3190,20 +3220,19 @@ bool select_dumpvar::send_data(List &items) my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0)); DBUG_RETURN(1); } - while ((mv= var_li++) && (item= it++)) + for (suv= set_var_items; ((mv= var_li++) && (item= it++)); suv++) { if (mv->local) { + DBUG_ASSERT(!*suv); if (thd->spcont->set_variable(thd, mv->offset, &item)) DBUG_RETURN(1); } else { - Item_func_set_user_var *suv= new Item_func_set_user_var(mv->s, item); - if (suv->fix_fields(thd, 0)) - DBUG_RETURN (1); - suv->save_item_result(item); - if (suv->update()) + DBUG_ASSERT(*suv); + (*suv)->save_item_result(item); + if ((*suv)->update()) DBUG_RETURN (1); } } diff --git a/sql/sql_class.h b/sql/sql_class.h index 36091546ff4..5434d5ae325 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3531,6 +3531,7 @@ public: class select_dumpvar :public select_result_interceptor { ha_rows row_count; + Item_func_set_user_var **set_var_items; public: List var_list; select_dumpvar() { var_list.empty(); row_count= 0;} -- cgit v1.2.1 From 745b849455e39d38d56f51bfd4de6c95b76675eb Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Sun, 11 Mar 2012 16:05:42 +0400 Subject: Fixed test case for bug #13105873 "valgrind warning:possible crash in foreign key handling on subsequent create table if not exists". Do not leave current database unassigned after the end of the test case. --- mysql-test/r/sp-bugs.result | 1 + mysql-test/t/sp-bugs.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/r/sp-bugs.result b/mysql-test/r/sp-bugs.result index 59588768d14..3b4e2855e46 100644 --- a/mysql-test/r/sp-bugs.result +++ b/mysql-test/r/sp-bugs.result @@ -128,4 +128,5 @@ CALL p1(); Warnings: Note 1050 Table 't2' already exists DROP DATABASE testdb; +USE test; End of 5.1 tests diff --git a/mysql-test/t/sp-bugs.test b/mysql-test/t/sp-bugs.test index a23ccea8189..2d143b6d86a 100644 --- a/mysql-test/t/sp-bugs.test +++ b/mysql-test/t/sp-bugs.test @@ -161,5 +161,6 @@ CALL p1(); --echo # below stmt should not return valgrind warnings CALL p1(); DROP DATABASE testdb; +USE test; --echo End of 5.1 tests -- cgit v1.2.1 From 2e72df370ba0b89cae9afd42b621326ab6dccd04 Mon Sep 17 00:00:00 2001 From: "Norvald H. Ryeng" Date: Mon, 12 Mar 2012 08:56:56 +0100 Subject: Bug#13031606 VALUES() IN A SELECT STATEMENT CRASHES SERVER Problem: Grouping results by VALUES(alias for string literal) causes the server to crash. Item_insert_values is not constructed to handle other types of arguments than field and reference to field. In this case, the argument is an Item_string, and this causes Item_insert_values::fix_fields() to crash. Fix: Issue an error message when the argument to Item_insert_values is not a field or a reference to a field. This is slightly in breach with documentation, which states that VALUES should return NULL, but the error message is only issued in cases where the server otherwise would crash, so there is no change in behavior for queries that already work. Future versions will restrict syntax so that using VALUES in this way is illegal. --- mysql-test/r/errors.result | 14 ++++++++++++++ mysql-test/t/errors.test | 18 ++++++++++++++++++ sql/item.cc | 16 ++++------------ 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/errors.result b/mysql-test/r/errors.result index 022a32d9c9b..bcc92296945 100644 --- a/mysql-test/r/errors.result +++ b/mysql-test/r/errors.result @@ -55,3 +55,17 @@ Error 1054 Unknown column 'b' in 'field list' INSERT INTO t1 SELECT b FROM t1; ERROR 42S22: Unknown column 'b' in 'field list' DROP TABLE t1; +CREATE TABLE t1 (a INT); +CREATE TABLE t2(a INT PRIMARY KEY, b INT); +SELECT '' AS b FROM t1 GROUP BY VALUES(b); +ERROR 42S22: Unknown column '' in 'VALUES() function' +REPLACE t2(b) SELECT '' AS b FROM t1 GROUP BY VALUES(b); +ERROR 42S22: Unknown column '' in 'VALUES() function' +UPDATE t2 SET a=(SELECT '' AS b FROM t1 GROUP BY VALUES(b)); +ERROR 42S22: Unknown column '' in 'VALUES() function' +INSERT INTO t2 VALUES (1,0) ON DUPLICATE KEY UPDATE +b=(SELECT '' AS b FROM t1 GROUP BY VALUES(b)); +ERROR 42S22: Unknown column '' in 'VALUES() function' +INSERT INTO t2(a,b) VALUES (1,0) ON DUPLICATE KEY UPDATE +b=(SELECT VALUES(a)+2 FROM t1); +DROP TABLE t1, t2; diff --git a/mysql-test/t/errors.test b/mysql-test/t/errors.test index 89579ec1739..b426d9fd5b0 100644 --- a/mysql-test/t/errors.test +++ b/mysql-test/t/errors.test @@ -67,3 +67,21 @@ SHOW ERRORS; INSERT INTO t1 SELECT b FROM t1; DROP TABLE t1; # End of 5.0 tests + +# +# Bug #13031606 VALUES() IN A SELECT STATEMENT CRASHES SERVER +# +CREATE TABLE t1 (a INT); +CREATE TABLE t2(a INT PRIMARY KEY, b INT); +--error ER_BAD_FIELD_ERROR +SELECT '' AS b FROM t1 GROUP BY VALUES(b); +--error ER_BAD_FIELD_ERROR +REPLACE t2(b) SELECT '' AS b FROM t1 GROUP BY VALUES(b); +--error ER_BAD_FIELD_ERROR +UPDATE t2 SET a=(SELECT '' AS b FROM t1 GROUP BY VALUES(b)); +--error ER_BAD_FIELD_ERROR +INSERT INTO t2 VALUES (1,0) ON DUPLICATE KEY UPDATE + b=(SELECT '' AS b FROM t1 GROUP BY VALUES(b)); +INSERT INTO t2(a,b) VALUES (1,0) ON DUPLICATE KEY UPDATE + b=(SELECT VALUES(a)+2 FROM t1); +DROP TABLE t1, t2; diff --git a/sql/item.cc b/sql/item.cc index 2a7d2453889..90bfb0d2852 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6657,20 +6657,12 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) } if (arg->type() == REF_ITEM) + arg= static_cast(arg)->ref[0]; + if (arg->type() != FIELD_ITEM) { - Item_ref *ref= (Item_ref *)arg; - if (ref->ref[0]->type() != FIELD_ITEM) - { - my_error(ER_BAD_FIELD_ERROR, MYF(0), "", "VALUES() function"); - return TRUE; - } - arg= ref->ref[0]; + my_error(ER_BAD_FIELD_ERROR, MYF(0), "", "VALUES() function"); + return TRUE; } - /* - According to our SQL grammar, VALUES() function can reference - only to a column. - */ - DBUG_ASSERT(arg->type() == FIELD_ITEM); Item_field *field_arg= (Item_field *)arg; -- cgit v1.2.1 From c8f4e2793dffe2b044d510baa36ce23dcddeac62 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 14 Mar 2012 13:25:14 +0100 Subject: Bug#13721076 CRASH WITH TIME TYPE/TIMESTAMP() AND WARNINGS IN SUBQUERY The table contains one time value: '00:00:32' This value is converted to timestamp by a subquery. In convert_constant_item we call (*item)->is_null() which triggers execution of the Item_singlerow_subselect subquery, and the string "0000-00-00 00:00:32" is cached by Item_cache_datetime. We continue execution and call update_null_value, which calls val_int() on the cached item, which converts the time value to ((longlong) 32) Then we continue to do (*item)->save_in_field() which ends up in Item_cache_datetime::val_str() which fails, since (32 < 101) in number_to_datetime, and val_str() returns NULL. Item_singlerow_subselect::val_str isnt prepared for this: if exec() succeeds, and return !null_value, then val_str() *must* succeed. Solution: refuse to cache strings like "0000-00-00 00:00:32" in Item_cache_datetime::cache_value, and return NULL instead. This is similar to the solution for Bug#11766860 - 60085: CRASH IN ITEM::SAVE_IN_FIELD() WITH TIME DATA TYPE This patch is for 5.5 only. The issue is not present after WL#946, since a time value will be converted to a proper timestamp, with the current date rather than "0000-00-00" --- mysql-test/r/subselect.result | 9 +++++++++ mysql-test/t/subselect.test | 10 ++++++++++ sql/item.cc | 27 +++++++++++++++++++++++++++ sql/item_timefunc.cc | 2 +- 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 37741b79695..a2011f46ac8 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -5113,3 +5113,12 @@ SELECT 1 FROM 1) FROM t1) AS e; ERROR 21000: Operand should contain 1 column(s) DROP TABLE t1; +# +# Bug#13721076 CRASH WITH TIME TYPE/TIMESTAMP() AND WARNINGS IN SUBQUERY +# +CREATE TABLE t1(a TIME NOT NULL); +INSERT INTO t1 VALUES ('00:00:32'); +SELECT 1 FROM t1 WHERE a > +(SELECT timestamp(a) AS a FROM t1); +1 +DROP TABLE t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index f3be94ebb94..088915b2590 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -4067,4 +4067,14 @@ SELECT 1 FROM DROP TABLE t1; +--echo # +--echo # Bug#13721076 CRASH WITH TIME TYPE/TIMESTAMP() AND WARNINGS IN SUBQUERY +--echo # + +CREATE TABLE t1(a TIME NOT NULL); +INSERT INTO t1 VALUES ('00:00:32'); +SELECT 1 FROM t1 WHERE a > +(SELECT timestamp(a) AS a FROM t1); + +DROP TABLE t1; diff --git a/sql/item.cc b/sql/item.cc index 45ce1cac961..22b5adc4597 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -7647,6 +7647,33 @@ bool Item_cache_datetime::cache_value() str_value.copy(*res); null_value= example->null_value; unsigned_flag= example->unsigned_flag; + + if (!null_value) + { + switch(field_type()) + { + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + { + MYSQL_TIME ltime; + int was_cut; + const timestamp_type tt= + str_to_datetime(str_value.charset(), + str_value.ptr(), + str_value.length(), + <ime, + TIME_DATETIME_ONLY, + &was_cut); + if (tt != MYSQL_TIMESTAMP_DATETIME || was_cut) + null_value= true; + else + my_datetime_to_str(<ime, const_cast(str_value.ptr())); + } + default: + {} + } + } + return TRUE; } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index ddb00874b62..6fc85a01668 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2694,7 +2694,7 @@ longlong Item_datetime_typecast::val_int() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (get_arg0_date(<ime,1)) + if (get_arg0_date(<ime, TIME_FUZZY_DATE)) { null_value= 1; return 0; -- cgit v1.2.1 From 2ae9a5e454e19982377ad5896e8892192b7f6061 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 22 Mar 2012 11:22:54 +0800 Subject: Fix Bug #13849910 - INNODB ASSERTS ON TOO LONG TABLENAME WHEN USING PARTITIONS rb://981 approved by Sunny Bains --- storage/innobase/include/univ.i | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 71c75469ee9..3394fc576cb 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -303,11 +303,17 @@ management to ensure correct alignment for doubles etc. */ /* Maximum number of parallel threads in a parallelized operation */ #define UNIV_MAX_PARALLELISM 32 -/* The maximum length of a table name. This is the MySQL limit and is -defined in mysql_com.h like NAME_CHAR_LEN*SYSTEM_CHARSET_MBMAXLEN, the -number does not include a terminating '\0'. InnoDB probably can handle -longer names internally */ -#define MAX_TABLE_NAME_LEN 192 +/** This is the "mbmaxlen" for my_charset_filename (defined in +strings/ctype-utf8.c), which is used to encode File and Database names. */ +#define FILENAME_CHARSET_MAXNAMLEN 5 + +/** The maximum length of an encode table name in bytes. The max +table and database names are NAME_CHAR_LEN (64) characters. After the +encoding, the max length would be NAME_CHAR_LEN (64) * +FILENAME_CHARSET_MAXNAMLEN (5) = 320 bytes. The number does not include a +terminating '\0'. InnoDB can handle longer names internally */ +#define MAX_TABLE_NAME_LEN 320 + /* The maximum length of a database name. Like MAX_TABLE_NAME_LEN this is the MySQL's NAME_LEN, see check_and_convert_db_name(). */ -- cgit v1.2.1 From 4a03cdb7b65fd4c35433c3ad36c51af26fed69fb Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Wed, 28 Mar 2012 12:22:31 +0400 Subject: Bug #11880012: INDEX_SUBQUERY, BLACKHOLE, HANG IN PREPARING WITH 100% CPU USAGE Infinite loop in the subselect_indexsubquery_engine::exec() function caused Server hang with 100% CPU usage. The BLACKHOLE storage engine didn't update handler's table->status variable after index operations, that caused an infinite "while(!table->status)" execution. Index access methods of the BLACKHOLE engine handler have been updated to set table->status variable to STATUS_NOT_FOUND or 0 when such a method returns a HA_ERR_END_OF_FILE error or 0 respectively. --- mysql-test/r/blackhole.result | 11 +++++++++++ mysql-test/t/blackhole.test | 21 +++++++++++++++++++++ storage/blackhole/ha_blackhole.cc | 9 ++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/blackhole.result create mode 100644 mysql-test/t/blackhole.test diff --git a/mysql-test/r/blackhole.result b/mysql-test/r/blackhole.result new file mode 100644 index 00000000000..317070ca08e --- /dev/null +++ b/mysql-test/r/blackhole.result @@ -0,0 +1,11 @@ +# +# Bug #11880012: INDEX_SUBQUERY, BLACKHOLE, +# HANG IN PREPARING WITH 100% CPU USAGE +# +CREATE TABLE t1(a INT NOT NULL); +INSERT INTO t1 VALUES (1), (2), (3); +CREATE TABLE t2 (a INT UNSIGNED, b INT, UNIQUE KEY (a, b)) ENGINE=BLACKHOLE; +SELECT 1 FROM t1 WHERE a = ANY (SELECT a FROM t2); +1 +DROP TABLE t1, t2; +End of 5.5 tests diff --git a/mysql-test/t/blackhole.test b/mysql-test/t/blackhole.test new file mode 100644 index 00000000000..1451f7606e0 --- /dev/null +++ b/mysql-test/t/blackhole.test @@ -0,0 +1,21 @@ +# +# Tests for the BLACKHOLE storage engine +# + +--source include/have_blackhole.inc + +--echo # +--echo # Bug #11880012: INDEX_SUBQUERY, BLACKHOLE, +--echo # HANG IN PREPARING WITH 100% CPU USAGE +--echo # + +CREATE TABLE t1(a INT NOT NULL); +INSERT INTO t1 VALUES (1), (2), (3); +CREATE TABLE t2 (a INT UNSIGNED, b INT, UNIQUE KEY (a, b)) ENGINE=BLACKHOLE; + +SELECT 1 FROM t1 WHERE a = ANY (SELECT a FROM t2); + +DROP TABLE t1, t2; + +--echo End of 5.5 tests + diff --git a/storage/blackhole/ha_blackhole.cc b/storage/blackhole/ha_blackhole.cc index c12d5afbfa5..9d0f89915a5 100644 --- a/storage/blackhole/ha_blackhole.cc +++ b/storage/blackhole/ha_blackhole.cc @@ -151,6 +151,7 @@ int ha_blackhole::rnd_next(uchar *buf) else rc= HA_ERR_END_OF_FILE; MYSQL_READ_ROW_DONE(rc); + table->status= rc ? STATUS_NOT_FOUND : 0; DBUG_RETURN(rc); } @@ -241,6 +242,7 @@ int ha_blackhole::index_read_map(uchar * buf, const uchar * key, else rc= HA_ERR_END_OF_FILE; MYSQL_INDEX_READ_ROW_DONE(rc); + table->status= rc ? STATUS_NOT_FOUND : 0; DBUG_RETURN(rc); } @@ -258,6 +260,7 @@ int ha_blackhole::index_read_idx_map(uchar * buf, uint idx, const uchar * key, else rc= HA_ERR_END_OF_FILE; MYSQL_INDEX_READ_ROW_DONE(rc); + table->status= rc ? STATUS_NOT_FOUND : 0; DBUG_RETURN(rc); } @@ -274,6 +277,7 @@ int ha_blackhole::index_read_last_map(uchar * buf, const uchar * key, else rc= HA_ERR_END_OF_FILE; MYSQL_INDEX_READ_ROW_DONE(rc); + table->status= rc ? STATUS_NOT_FOUND : 0; DBUG_RETURN(rc); } @@ -285,6 +289,7 @@ int ha_blackhole::index_next(uchar * buf) MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); rc= HA_ERR_END_OF_FILE; MYSQL_INDEX_READ_ROW_DONE(rc); + table->status= STATUS_NOT_FOUND; DBUG_RETURN(rc); } @@ -296,6 +301,7 @@ int ha_blackhole::index_prev(uchar * buf) MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); rc= HA_ERR_END_OF_FILE; MYSQL_INDEX_READ_ROW_DONE(rc); + table->status= STATUS_NOT_FOUND; DBUG_RETURN(rc); } @@ -307,8 +313,8 @@ int ha_blackhole::index_first(uchar * buf) MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); rc= HA_ERR_END_OF_FILE; MYSQL_INDEX_READ_ROW_DONE(rc); + table->status= STATUS_NOT_FOUND; DBUG_RETURN(rc); - DBUG_RETURN(HA_ERR_END_OF_FILE); } @@ -319,6 +325,7 @@ int ha_blackhole::index_last(uchar * buf) MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); rc= HA_ERR_END_OF_FILE; MYSQL_INDEX_READ_ROW_DONE(rc); + table->status= STATUS_NOT_FOUND; DBUG_RETURN(rc); } -- cgit v1.2.1 From 17817a30094aff44fc1fbaf4f39a3f0cbe9a13da Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Wed, 4 Apr 2012 13:29:45 +0400 Subject: Bug#11766300 59387: FAILING ASSERTION: CURSOR->POS_STATE == 1997660512 (BTR_PCUR_IS_POSITIONE Bug#13639204 64111: CRASH ON SELECT SUBQUERY WITH NON UNIQUE INDEX The crash happened due to wrong calculation of key length during creation of reference for sort order index. The problem is that keyuse->used_tables can have OUTER_REF_TABLE_BIT enabled but used_tables parameter(create_ref_for_key() func) does not have it. So key parts which have OUTER_REF_TABLE_BIT are ommited and it could lead to incorrect key length calculation(zero key length). --- mysql-test/r/subselect_innodb.result | 60 ++++++++++++++++++++++++++++++ mysql-test/t/subselect_innodb.test | 56 ++++++++++++++++++++++++++++ sql/sql_select.cc | 7 +++- storage/innobase/handler/ha_innodb.cc | 1 + storage/innodb_plugin/handler/ha_innodb.cc | 1 + 5 files changed, 124 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect_innodb.result b/mysql-test/r/subselect_innodb.result index 6c6d563e284..4069958267a 100644 --- a/mysql-test/r/subselect_innodb.result +++ b/mysql-test/r/subselect_innodb.result @@ -245,3 +245,63 @@ x NULL drop procedure p1; drop tables t1,t2,t3; +# +# Bug #11766300 59387: FAILING ASSERTION: CURSOR->POS_STATE == 1997660512 (BTR_PCUR_IS_POSITIONE +# +CREATE TABLE t1 (a INT) ENGINE=INNODB; +INSERT INTO t1 VALUES (0); +CREATE TABLE t2 (d BINARY(2), PRIMARY KEY (d(1)), UNIQUE KEY (d)) ENGINE=INNODB; +SELECT 1 FROM t1 WHERE NOT EXISTS +(SELECT 1 FROM t2 WHERE d = (SELECT d FROM t2 WHERE a >= 1) ORDER BY d); +1 +1 +EXPLAIN SELECT 1 FROM t1 WHERE NOT EXISTS +(SELECT 1 FROM t2 WHERE d = (SELECT d FROM t2 WHERE a >= 1) ORDER BY d); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 1 Using where +2 DEPENDENT SUBQUERY t2 eq_ref PRIMARY,d d 2 func 1 Using where +3 DEPENDENT SUBQUERY t2 index NULL d 2 NULL 1 Using where; Using index +DROP TABLE t2; +CREATE TABLE t2 (b INT, c INT, UNIQUE KEY (b), UNIQUE KEY (b, c )) ENGINE=INNODB; +INSERT INTO t2 VALUES (1, 1); +SELECT 1 FROM t1 +WHERE a != (SELECT 1 FROM t2 WHERE a <=> b OR a > '' AND 6 = 7 ORDER BY b, c); +1 +DROP TABLE t1, t2; +# +# Bug #13639204 64111: CRASH ON SELECT SUBQUERY WITH NON UNIQUE +# INDEX +# +CREATE TABLE t1 ( +id int +) ENGINE=InnoDB; +INSERT INTO t1 (id) VALUES (11); +CREATE TABLE t2 ( +t1_id int, +position int, +KEY t1_id (t1_id), +KEY t1_id_position (t1_id,position) +) ENGINE=InnoDB; +EXPLAIN SELECT +(SELECT position FROM t2 +WHERE t2.t1_id = t1.id +ORDER BY t2.t1_id , t2.position +LIMIT 10,1 +) AS maxkey +FROM t1 +LIMIT 1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 1 +2 DEPENDENT SUBQUERY t2 ref t1_id,t1_id_position t1_id_position 5 test.t1.id 1 Using where +SELECT +(SELECT position FROM t2 +WHERE t2.t1_id = t1.id +ORDER BY t2.t1_id , t2.position +LIMIT 10,1 +) AS maxkey +FROM t1 +LIMIT 1; +maxkey +NULL +DROP TABLE t1,t2; +End of 5.1 tests diff --git a/mysql-test/t/subselect_innodb.test b/mysql-test/t/subselect_innodb.test index 573fe0c1810..ce070b454c3 100644 --- a/mysql-test/t/subselect_innodb.test +++ b/mysql-test/t/subselect_innodb.test @@ -238,3 +238,59 @@ call p1(); call p1(); drop procedure p1; drop tables t1,t2,t3; + +--echo # +--echo # Bug #11766300 59387: FAILING ASSERTION: CURSOR->POS_STATE == 1997660512 (BTR_PCUR_IS_POSITIONE +--echo # + +CREATE TABLE t1 (a INT) ENGINE=INNODB; +INSERT INTO t1 VALUES (0); +CREATE TABLE t2 (d BINARY(2), PRIMARY KEY (d(1)), UNIQUE KEY (d)) ENGINE=INNODB; + +SELECT 1 FROM t1 WHERE NOT EXISTS +(SELECT 1 FROM t2 WHERE d = (SELECT d FROM t2 WHERE a >= 1) ORDER BY d); + +EXPLAIN SELECT 1 FROM t1 WHERE NOT EXISTS +(SELECT 1 FROM t2 WHERE d = (SELECT d FROM t2 WHERE a >= 1) ORDER BY d); + +DROP TABLE t2; + +CREATE TABLE t2 (b INT, c INT, UNIQUE KEY (b), UNIQUE KEY (b, c )) ENGINE=INNODB; +INSERT INTO t2 VALUES (1, 1); + +SELECT 1 FROM t1 +WHERE a != (SELECT 1 FROM t2 WHERE a <=> b OR a > '' AND 6 = 7 ORDER BY b, c); + +DROP TABLE t1, t2; + +--echo # +--echo # Bug #13639204 64111: CRASH ON SELECT SUBQUERY WITH NON UNIQUE +--echo # INDEX +--echo # +CREATE TABLE t1 ( +id int +) ENGINE=InnoDB; +INSERT INTO t1 (id) VALUES (11); + +CREATE TABLE t2 ( +t1_id int, +position int, +KEY t1_id (t1_id), +KEY t1_id_position (t1_id,position) +) ENGINE=InnoDB; + +let $query=SELECT +(SELECT position FROM t2 +WHERE t2.t1_id = t1.id +ORDER BY t2.t1_id , t2.position +LIMIT 10,1 +) AS maxkey +FROM t1 +LIMIT 1; + +eval EXPLAIN $query; +eval $query; + +DROP TABLE t1,t2; + +--echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0d8dc740431..a0390baffaa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5793,6 +5793,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, } keyuse++; } while (keyuse->table == table && keyuse->key == key); + DBUG_ASSERT(length > 0 && keyparts != 0); } /* not ftkey */ /* set up fieldref */ @@ -13426,6 +13427,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, DBUG_ENTER("test_if_skip_sort_order"); LINT_INIT(ref_key_parts); + /* Check that we are always called with first non-const table */ + DBUG_ASSERT(tab == tab->join->join_tab + tab->join->const_tables); + /* Keys disabled by ALTER TABLE ... DISABLE KEYS should have already been taken into account. @@ -13507,7 +13511,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, while (keyuse->key != new_ref_key && keyuse->table == tab->table) keyuse++; if (create_ref_for_key(tab->join, tab, keyuse, - tab->join->const_table_map)) + (tab->join->const_table_map | + OUTER_REF_TABLE_BIT))) DBUG_RETURN(0); pick_table_access_method(tab); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index c59c02abe1b..6576016501f 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4871,6 +4871,7 @@ ha_innobase::index_read( DBUG_ENTER("index_read"); ut_a(prebuilt->trx == thd_to_trx(user_thd)); + ut_ad(key_len != 0 || find_flag != HA_READ_KEY_EXACT); ha_statistic_increment(&SSV::ha_read_key_count); diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index d1e9649be07..b8e9ccc72ef 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -5575,6 +5575,7 @@ ha_innobase::index_read( DBUG_ENTER("index_read"); ut_a(prebuilt->trx == thd_to_trx(user_thd)); + ut_ad(key_len != 0 || find_flag != HA_READ_KEY_EXACT); ha_statistic_increment(&SSV::ha_read_key_count); -- cgit v1.2.1 From 5ac773b9b48116f0ed924b5c1359dd8313a6ebe7 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 6 Apr 2012 12:04:07 +0300 Subject: Bug #13934049: 64884: LOGINS WITH INCORRECT PASSWORD ARE ALLOWED Fixed an improper type conversion on return that can make the server accept logins with a wrong password. --- sql/password.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/password.c b/sql/password.c index e9e81fb7bf3..e029ba51fc4 100644 --- a/sql/password.c +++ b/sql/password.c @@ -531,7 +531,7 @@ check_scramble(const char *scramble_arg, const char *message, mysql_sha1_reset(&sha1_context); mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE); mysql_sha1_result(&sha1_context, hash_stage2_reassured); - return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); + return test(memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE)); } -- cgit v1.2.1 From 582b7283472a43af5d7fb51386df055ea9d44a96 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Thu, 12 Apr 2012 15:04:22 +0200 Subject: Bug#13871079 RQG_MYISAM_DML_ALTER_VALGRIND FAILS ON VALGRIND PN PB2 The class Copy_field contains a String tmp, which may allocate memory on the heap. That means that all instances of Copy_field must be properly destroyed. Alas they are not. Solution: don't use Copy_field::tmp for copying from_field => tmp => to_field in do_field_string() --- sql/field.cc | 14 +++++++++++++- sql/field.h | 8 +++++++- sql/field_conv.cc | 11 ++++++----- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 59eb83da5fa..c7f19976260 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8257,7 +8257,19 @@ String *Field_set::val_str(String *val_buffer, ulonglong tmp=(ulonglong) Field_enum::val_int(); uint bitnr=0; - val_buffer->set("", 0, field_charset); + if (tmp == 0) + { + /* + Some callers expect *val_buffer to contain the result, + so we assign to it, rather than doing 'return &empty_set_string. + */ + *val_buffer= empty_set_string; + return val_buffer; + } + + val_buffer->set_charset(field_charset); + val_buffer->length(0); + while (tmp && bitnr < (uint) typelib->count) { if (tmp & 1) diff --git a/sql/field.h b/sql/field.h index b4adb764b22..674b58108d4 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1985,7 +1985,8 @@ public: :Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, packlength_arg, - typelib_arg,charset_arg) + typelib_arg,charset_arg), + empty_set_string("", 0, charset_arg) { flags=(flags & ~ENUM_FLAG) | SET_FLAG; } @@ -1996,8 +1997,11 @@ public: virtual bool zero_pack() const { return 1; } String *val_str(String*,String *); void sql_type(String &str) const; + uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return MYSQL_TYPE_SET; } bool has_charset(void) const { return TRUE; } +private: + const String empty_set_string; }; @@ -2192,6 +2196,8 @@ public: { return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0; } +private: + const String empty_set_string; }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 594449174bc..605ac752e0d 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -317,10 +317,11 @@ static void do_save_blob(Copy_field *copy) static void do_field_string(Copy_field *copy) { char buff[MAX_FIELD_WIDTH]; - copy->tmp.set_quick(buff,sizeof(buff),copy->tmp.charset()); - copy->from_field->val_str(©->tmp); - copy->to_field->store(copy->tmp.c_ptr_quick(),copy->tmp.length(), - copy->tmp.charset()); + String res(buff, sizeof(buff), copy->from_field->charset()); + res.length(0U); + + copy->from_field->val_str(&res); + copy->to_field->store(res.c_ptr_quick(), res.length(), res.charset()); } @@ -563,7 +564,7 @@ void Copy_field::set(uchar *to,Field *from) /* To do: - If 'save\ is set to true and the 'from' is a blob field, do_copy is set to + If 'save' is set to true and the 'from' is a blob field, do_copy is set to do_save_blob rather than do_conv_blob. The only differences between them appears to be: -- cgit v1.2.1