From fc34cd1c2180976426b2c6fbcd9bdadd7b1ce337 Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Mon, 22 Mar 2010 09:51:16 +0000 Subject: [PATCH 01/29] BUG#51987: revoke privileges logs wrong error code A failed REVOKE statement is logged with error=0, thus causing the slave to stop. The slave should not stop as this was an expected error. Given that the execution failed on the master as well the error code should be logged so that the slave can replay the statement, get an error and compare with the master's execution outcome. If errors match, then slave can proceed with replication, as the error it got, when replaying the statement, was expected. In this particular case, the bug surfaces because the error code is pushed to the THD diagnostics area after writing the event to the binary log. Therefore, it would be logged with the THD diagnostics area clean, hence its error code would not contain the correct code. We fix this by moving the error reporting ahead of the call to the routine that writes the event to the binary log. --- mysql-test/suite/rpl/r/rpl_do_grant.result | 14 +++++++++++ mysql-test/suite/rpl/t/rpl_do_grant.test | 29 ++++++++++++++++++++++ sql/sql_acl.cc | 10 ++++---- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_do_grant.result b/mysql-test/suite/rpl/r/rpl_do_grant.result index 65c60acc651..4d8cfe3d8b8 100644 --- a/mysql-test/suite/rpl/r/rpl_do_grant.result +++ b/mysql-test/suite/rpl/r/rpl_do_grant.result @@ -242,4 +242,18 @@ GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION DROP TABLE t1; DROP PROCEDURE p1; DROP USER 'user49119'@'localhost'; +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +grant all on *.* to foo@"1.2.3.4"; +revoke all privileges, grant option from "foo"; +ERROR HY000: Can't revoke all privileges for one or more of the requested users +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; grant all on *.* to foo@"1.2.3.4" +master-bin.000001 # Query # # use `test`; revoke all privileges, grant option from "foo" +DROP USER foo@"1.2.3.4"; "End of test" diff --git a/mysql-test/suite/rpl/t/rpl_do_grant.test b/mysql-test/suite/rpl/t/rpl_do_grant.test index d6a06f43d18..88120aa3512 100644 --- a/mysql-test/suite/rpl/t/rpl_do_grant.test +++ b/mysql-test/suite/rpl/t/rpl_do_grant.test @@ -316,4 +316,33 @@ DROP USER 'user49119'@'localhost'; -- sync_slave_with_master +# +# Bug #51987 revoke privileges logs wrong error code +# + +-- connection master +-- source include/master-slave-reset.inc +-- connection master + +grant all on *.* to foo@"1.2.3.4"; +-- error ER_REVOKE_GRANTS +revoke all privileges, grant option from "foo"; + +## assertion: revoke is logged +-- source include/show_binlog_events.inc + +-- sync_slave_with_master + +## assertion: slave replicates revoke and does not fail because master +## logged revoke with correct expected error code +-- let $err= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1) + if ($err) +{ + -- die UNEXPECTED ERROR AT SLAVE: $err +} + +-- connection master +DROP USER foo@"1.2.3.4"; +-- sync_slave_with_master + --echo "End of test" diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index a8828d15cae..dd256c70ecb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6117,19 +6117,19 @@ bool mysql_revoke_all(THD *thd, List &list) VOID(pthread_mutex_unlock(&acl_cache->lock)); - int binlog_error= + if (result) + my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0)); + + result= result | write_bin_log(thd, FALSE, thd->query(), thd->query_length()); rw_unlock(&LOCK_grant); close_thread_tables(thd); - /* error for writing binary log has already been reported */ - if (result && !binlog_error) - my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0)); /* Restore the state of binlog format */ thd->current_stmt_binlog_row_based= save_binlog_row_based; - DBUG_RETURN(result || binlog_error); + DBUG_RETURN(result); } From 7e97cc0ad462c8c328f50270b659d4cb870c03b1 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Mar 2010 08:18:05 +0100 Subject: [PATCH 02/29] Set version number for mysql-5.1.43sp1 release --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 38eb511ac18..baaa561c81a 100644 --- a/configure.in +++ b/configure.in @@ -10,7 +10,7 @@ AC_CANONICAL_SYSTEM # # When changing major version number please also check switch statement # in mysqlbinlog::check_master_version(). -AM_INIT_AUTOMAKE(mysql, 5.1.43) +AM_INIT_AUTOMAKE(mysql, 5.1.43sp1) AM_CONFIG_HEADER([include/config.h:config.h.in]) # Request support for automake silent-rules if available. From 960f1d37b65afaf47790b325d5fe0b020ea47746 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 10:45:18 +0100 Subject: [PATCH 03/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3302.1.1 > revision-id: kristofer.pettersson@sun.com-20100113113900-o3m4jcm4l6qzum57 > parent: dao-gang.qu@sun.com-20091231040419-i5dnn06ahs256qcy > committer: Kristofer Pettersson > branch nick: mysql-5.1-bugteam > timestamp: Wed 2010-01-13 12:39:00 +0100 > message: > Bug#33982 debug assertion and crash reloading grant tables after sighup or kill > > In certain rare cases when a process was interrupted > during a FLUSH PRIVILEGES operation the diagnostic > area would be set to an error state but the function > responsible for the operation would still signal > success. This would lead to a debug assertion error > later on when the server would attempt to reset the > DA before sending the error message. > > This patch fixes the issue by assuring that > reload_acl_and_cache() always fails if an error > condition is raised. > > The second issue was that a KILL could cause > a console error message which referred to a DA > state without first making sure that such a > state existed. > > This patch fixes this issue in two different > palces by first checking DA state before > fetching the error message. > > --- sql/sql_parse.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 48743a2d48f..eaea6e55b09 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6789,13 +6789,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, thd->store_globals(); lex_start(thd); } - + if (thd) { bool reload_acl_failed= acl_reload(thd); bool reload_grants_failed= grant_reload(thd); bool reload_servers_failed= servers_reload(thd); - + if (reload_acl_failed || reload_grants_failed || reload_servers_failed) { result= 1; @@ -6951,7 +6951,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (options & REFRESH_USER_RESOURCES) reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ *write_to_binlog= tmp_write_to_binlog; - return result; + /* + If the query was killed then this function must fail. + */ + return result || thd->killed; } From 1da0fb9d227eb1fd8ecdb3cac2b2d6ed663c8ee9 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:02:25 +0100 Subject: [PATCH 04/29] Backporting to 5.1.43sp1 patch --- mysql-test/r/partition_innodb.result | 44 ++++++++++++++++ mysql-test/t/partition_innodb-master.opt | 1 + mysql-test/t/partition_innodb.test | 45 ++++++++++++++++- sql/ha_partition.cc | 64 +++++++++++++++++------- sql/sql_parse.cc | 2 +- sql/sql_partition.cc | 8 ++- sql/sql_table.cc | 23 +++++++-- 7 files changed, 158 insertions(+), 29 deletions(-) create mode 100644 mysql-test/t/partition_innodb-master.opt diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result index b8cfa25349d..f2f6ef138ff 100644 --- a/mysql-test/r/partition_innodb.result +++ b/mysql-test/r/partition_innodb.result @@ -274,3 +274,47 @@ CREATE TABLE t1 (a INT) ENGINE=InnoDB PARTITION BY list(a) (PARTITION p1 VALUES IN (1)); CREATE INDEX i1 ON t1 (a); DROP TABLE t1; +# +# Bug#47343: InnoDB fails to clean-up after lock wait timeout on +# REORGANIZE PARTITION +# +CREATE TABLE t1 ( +a INT, +b DATE NOT NULL, +PRIMARY KEY (a, b) +) ENGINE=InnoDB +PARTITION BY RANGE (a) ( +PARTITION pMAX VALUES LESS THAN MAXVALUE +) ; +INSERT INTO t1 VALUES (1, '2001-01-01'), (2, '2002-02-02'), (3, '2003-03-03'); +START TRANSACTION; +SELECT * FROM t1 FOR UPDATE; +a b +1 2001-01-01 +2 2002-02-02 +3 2003-03-03 +# Connection con1 +ALTER TABLE t1 REORGANIZE PARTITION pMAX INTO +(PARTITION p3 VALUES LESS THAN (3), +PARTITION pMAX VALUES LESS THAN MAXVALUE); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SHOW WARNINGS; +Level Code Message +Error 1205 Lock wait timeout exceeded; try restarting transaction +ALTER TABLE t1 REORGANIZE PARTITION pMAX INTO +(PARTITION p3 VALUES LESS THAN (3), +PARTITION pMAX VALUES LESS THAN MAXVALUE); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SHOW WARNINGS; +Level Code Message +Error 1205 Lock wait timeout exceeded; try restarting transaction +t1.frm +t1.par +# Connection default +SELECT * FROM t1; +a b +1 2001-01-01 +2 2002-02-02 +3 2003-03-03 +COMMIT; +DROP TABLE t1; diff --git a/mysql-test/t/partition_innodb-master.opt b/mysql-test/t/partition_innodb-master.opt new file mode 100644 index 00000000000..462f8fbe828 --- /dev/null +++ b/mysql-test/t/partition_innodb-master.opt @@ -0,0 +1 @@ +--innodb_lock_wait_timeout=1 diff --git a/mysql-test/t/partition_innodb.test b/mysql-test/t/partition_innodb.test index aba28b76f01..b7fe4477a13 100644 --- a/mysql-test/t/partition_innodb.test +++ b/mysql-test/t/partition_innodb.test @@ -5,6 +5,8 @@ drop table if exists t1; --enable_warnings +let $MYSQLD_DATADIR= `SELECT @@datadir`; + # # Bug#47029: Crash when reorganize partition with subpartition # @@ -296,6 +298,47 @@ CREATE TABLE t1 (a INT) ENGINE=InnoDB PARTITION BY list(a) (PARTITION p1 VALUES IN (1)); CREATE INDEX i1 ON t1 (a); DROP TABLE t1; -let $MYSQLD_DATADIR= `SELECT @@datadir`; + # Before the fix it should show extra file like #sql-2405_2.par --list_files $MYSQLD_DATADIR/test/ * + +--echo # +--echo # Bug#47343: InnoDB fails to clean-up after lock wait timeout on +--echo # REORGANIZE PARTITION +--echo # +CREATE TABLE t1 ( + a INT, + b DATE NOT NULL, + PRIMARY KEY (a, b) +) ENGINE=InnoDB +PARTITION BY RANGE (a) ( + PARTITION pMAX VALUES LESS THAN MAXVALUE +) ; + +INSERT INTO t1 VALUES (1, '2001-01-01'), (2, '2002-02-02'), (3, '2003-03-03'); + +START TRANSACTION; +SELECT * FROM t1 FOR UPDATE; + +connect (con1, localhost, root,,); +--echo # Connection con1 +--error ER_LOCK_WAIT_TIMEOUT +ALTER TABLE t1 REORGANIZE PARTITION pMAX INTO +(PARTITION p3 VALUES LESS THAN (3), + PARTITION pMAX VALUES LESS THAN MAXVALUE); +SHOW WARNINGS; +--error ER_LOCK_WAIT_TIMEOUT +ALTER TABLE t1 REORGANIZE PARTITION pMAX INTO +(PARTITION p3 VALUES LESS THAN (3), + PARTITION pMAX VALUES LESS THAN MAXVALUE); +SHOW WARNINGS; + +#Contents of the 'test' database directory: +--list_files $MYSQLD_DATADIR/test + +disconnect con1; +connection default; +--echo # Connection default +SELECT * FROM t1; +COMMIT; +DROP TABLE t1; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b854e270029..555a0b97ddd 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1215,17 +1215,28 @@ int ha_partition::prepare_new_partition(TABLE *tbl, partition_element *p_elem) { int error; - bool create_flag= FALSE; DBUG_ENTER("prepare_new_partition"); if ((error= set_up_table_before_create(tbl, part_name, create_info, 0, p_elem))) - goto error; + goto error_create; if ((error= file->ha_create(part_name, tbl, create_info))) - goto error; - create_flag= TRUE; + { + /* + Added for safety, InnoDB reports HA_ERR_FOUND_DUPP_KEY + if the table/partition already exists. + If we return that error code, then print_error would try to + get_dup_key on a non-existing partition. + So return a more reasonable error code. + */ + if (error == HA_ERR_FOUND_DUPP_KEY) + error= HA_ERR_TABLE_EXIST; + goto error_create; + } + DBUG_PRINT("info", ("partition %s created", part_name)); if ((error= file->ha_open(tbl, part_name, m_mode, m_open_test_lock))) - goto error; + goto error_open; + DBUG_PRINT("info", ("partition %s opened", part_name)); /* Note: if you plan to add another call that may return failure, better to do it before external_lock() as cleanup_new_partition() @@ -1233,12 +1244,15 @@ int ha_partition::prepare_new_partition(TABLE *tbl, Otherwise see description for cleanup_new_partition(). */ if ((error= file->ha_external_lock(ha_thd(), m_lock_type))) - goto error; + goto error_external_lock; + DBUG_PRINT("info", ("partition %s external locked", part_name)); DBUG_RETURN(0); -error: - if (create_flag) - VOID(file->ha_delete_table(part_name)); +error_external_lock: + VOID(file->close()); +error_open: + VOID(file->ha_delete_table(part_name)); +error_create: DBUG_RETURN(error); } @@ -1272,19 +1286,23 @@ error: void ha_partition::cleanup_new_partition(uint part_count) { - handler **save_m_file= m_file; DBUG_ENTER("ha_partition::cleanup_new_partition"); - if (m_added_file && m_added_file[0]) + if (m_added_file) { - m_file= m_added_file; + THD *thd= ha_thd(); + handler **file= m_added_file; + while ((part_count > 0) && (*file)) + { + (*file)->ha_external_lock(thd, F_UNLCK); + (*file)->close(); + + /* Leave the (*file)->ha_delete_table(part_name) to the ddl-log */ + + file++; + part_count--; + } m_added_file= NULL; - - external_lock(ha_thd(), F_UNLCK); - /* delete_table also needed, a bit more complex */ - close(); - - m_file= save_m_file; } DBUG_VOID_RETURN; } @@ -1590,7 +1608,15 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, part_elem->part_state= PART_TO_BE_DROPPED; } m_new_file= new_file_array; - DBUG_RETURN(copy_partitions(copied, deleted)); + if ((error= copy_partitions(copied, deleted))) + { + /* + Close and unlock the new temporary partitions. + They will later be deleted through the ddl-log. + */ + cleanup_new_partition(part_count); + } + DBUG_RETURN(error); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index eaea6e55b09..7eabc1887f9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6954,7 +6954,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, /* If the query was killed then this function must fail. */ - return result || thd->killed; + return result || (thd ? thd->killed : 0); } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index e0697f563ea..441adc9ddd9 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -5708,8 +5708,7 @@ static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt) part_info->first_log_entry= NULL; build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); - build_table_filename(tmp_path, sizeof(tmp_path) - 1, lpt->db, - lpt->table_name, "#", 0); + build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); pthread_mutex_lock(&LOCK_gdl); if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, FALSE)) @@ -5765,8 +5764,7 @@ static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); - build_table_filename(tmp_path, sizeof(tmp_path) - 1, lpt->db, - lpt->table_name, "#", 0); + build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); pthread_mutex_lock(&LOCK_gdl); if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, FALSE)) @@ -5991,7 +5989,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, partition_info *part_info= lpt->part_info; DBUG_ENTER("handle_alter_part_error"); - if (!part_info->first_log_entry && + if (part_info->first_log_entry && execute_ddl_log_entry(current_thd, part_info->first_log_entry->entry_pos)) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3b7354111ba..0e36ecfcb46 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -647,7 +647,7 @@ static bool read_ddl_log_file_entry(uint entry_no) Write one entry from ddl log file SYNOPSIS write_ddl_log_file_entry() - entry_no Entry number to read + entry_no Entry number to write RETURN VALUES TRUE Error FALSE Success @@ -748,10 +748,10 @@ static uint read_ddl_log_header() else successful_open= TRUE; } - entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]); - global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]); if (successful_open) { + entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]); + global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]); global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]); DBUG_ASSERT(global_ddl_log.io_size <= sizeof(global_ddl_log.file_entry_buf)); @@ -832,6 +832,7 @@ static bool init_ddl_log() goto end; global_ddl_log.io_size= IO_SIZE; + global_ddl_log.name_len= FN_LEN; create_ddl_log_file_name(file_name); if ((global_ddl_log.file_id= my_create(file_name, CREATE_MODE, @@ -884,6 +885,13 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) { DBUG_RETURN(FALSE); } + DBUG_PRINT("ddl_log", + ("execute type %c next %u name '%s' from_name '%s' handler '%s'", + ddl_log_entry->action_type, + ddl_log_entry->next_entry, + ddl_log_entry->name, + ddl_log_entry->from_name, + ddl_log_entry->handler_name)); handler_name.str= (char*)ddl_log_entry->handler_name; handler_name.length= strlen(ddl_log_entry->handler_name); init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); @@ -1091,6 +1099,15 @@ bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry, DBUG_RETURN(TRUE); } error= FALSE; + DBUG_PRINT("ddl_log", + ("write type %c next %u name '%s' from_name '%s' handler '%s'", + (char) global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS], + ddl_log_entry->next_entry, + (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS], + (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + + FN_LEN], + (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + + (2*FN_LEN)])); if (write_ddl_log_file_entry((*active_entry)->entry_pos)) { error= TRUE; From 4fddae591e0fcb1374086147de3ab053cd776b5f Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:05:17 +0100 Subject: [PATCH 05/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3315.1.1 > revision-id: mattias.jonsson@sun.com-20100118164918-afjah8vmey4ya4ox > parent: joro@sun.com-20100115090646-0g4tjrmqf20axlpv > committer: Mattias Jonsson > branch nick: b47343-51-bt > timestamp: Mon 2010-01-18 17:49:18 +0100 > message: > Bug#47343: InnoDB fails to clean-up after lock wait timeout on > REORGANIZE PARTITION > > There were several problems which lead to this this, > all related to bad error handling. > > 1) There was several bugs preventing the ddl-log to be used for > cleaning up created files on error. > > 2) The error handling after the copy partition rows did not close > and unlock the tables, resulting in deletion of partitions > which were in use, which lead InnoDB to put the partition to > drop in a background queue. > ------------------------------------------------------------ > revno: 3325 > revision-id: mattias.jonsson@sun.com-20100119160251-0xvcgzw0y08xwk6r > parent: joro@sun.com-20091223151122-ada73up1yydh0emt > committer: Mattias Jonsson > branch nick: topush-51-bugteam > timestamp: Tue 2010-01-19 17:02:51 +0100 > message: > post-push patch for bug#47343. > > Missing ha_rnd_end in copy_partitions, found due to a > DBUG_ASSERT in mysql-pe --- sql/ha_partition.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 555a0b97ddd..7b9ecd7d902 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1705,6 +1705,7 @@ int ha_partition::copy_partitions(ulonglong * const copied, } DBUG_RETURN(FALSE); error: + m_reorged_file[reorg_part]->ha_rnd_end(); DBUG_RETURN(result); } From 6c026b7ee4b86b3650e5b743a96b33f3fdbfb2dd Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:06:50 +0100 Subject: [PATCH 06/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3324 > revision-id: joro@sun.com-20091223151122-ada73up1yydh0emt > parent: joro@sun.com-20100119124841-38vva51cuq3if7dc > committer: Georgi Kodinov > branch nick: B49512-5.1-bugteam > timestamp: Wed 2009-12-23 17:11:22 +0200 > message: > Bug #49512 : subquery with aggregate function crash > subselect_single_select_engine::exec() > > When a subquery doesn't need to be evaluated because > it returns only aggregate functions and these aggregates > can be calculated from the metadata about the table it > was not updating all the relevant members of the JOIN > structure to reflect that this is a constant query. > This caused problems to the enclosing subquery > ('<> SOME' in the test case above) trying to read some > data about the tables. > > Fixed by setting const_tables to the number of tables > when the SELECT is optimized away. --- mysql-test/r/subselect.result | 13 +++++++++++++ mysql-test/t/subselect.test | 15 +++++++++++++++ sql/sql_select.cc | 1 + 3 files changed, 29 insertions(+) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index f446d8feec3..dc3cff68731 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -4602,4 +4602,17 @@ SELECT 1 FROM t1 GROUP BY 1 1 DROP TABLE t1; +# +# Bug #49512 : subquery with aggregate function crash +# subselect_single_select_engine::exec() +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(); +# should not crash +SELECT 1 FROM t1 WHERE a <> SOME +( +SELECT MAX((SELECT a FROM t1 LIMIT 1)) AS d +FROM t1,t1 a +); +1 +DROP TABLE t1; End of 5.1 tests. diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index a4314c45cba..027578fc6bd 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -3585,4 +3585,19 @@ SELECT 1 FROM t1 GROUP BY (SELECT LAST_INSERT_ID() FROM t1 ORDER BY MIN(a) ASC LIMIT 1); DROP TABLE t1; +--echo # +--echo # Bug #49512 : subquery with aggregate function crash +--echo # subselect_single_select_engine::exec() + +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(); + +--echo # should not crash +SELECT 1 FROM t1 WHERE a <> SOME +( + SELECT MAX((SELECT a FROM t1 LIMIT 1)) AS d + FROM t1,t1 a +); +DROP TABLE t1; + --echo End of 5.1 tests. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 644f0072b7b..d5ce32902c4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -942,6 +942,7 @@ JOIN::optimize() DBUG_PRINT("info",("Select tables optimized away")); zero_result_cause= "Select tables optimized away"; tables_list= 0; // All tables resolved + const_tables= tables; /* Extract all table-independent conditions and replace the WHERE clause with them. All other conditions were computed by opt_sum_query From 6424a3463eacb5a302f7e0ed96254e901d2b8e4a Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:08:44 +0100 Subject: [PATCH 07/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3329.2.3 > revision-id: svoj@sun.com-20100122095702-e18xzhmyll1e5s25 > parent: svoj@sun.com-20100122095632-j8ssd5csnlzp1zpf > committer: Sergey Vojtovich > branch nick: mysql-5.1-bugteam > timestamp: Fri 2010-01-22 13:57:02 +0400 > message: > Applying InnoDB snapshot, fixes BUG#46193. > > Detailed revision comments: > > r6424 | marko | 2010-01-12 12:22:19 +0200 (Tue, 12 Jan 2010) | 16 lines > branches/5.1: In innobase_initialize_autoinc(), do not attempt to read > the maximum auto-increment value from the table if > innodb_force_recovery is set to at least 4, so that writes are > disabled. (Bug #46193) > > innobase_get_int_col_max_value(): Move the function definition before > ha_innobase::innobase_initialize_autoinc(), because that function now > calls this function. > > ha_innobase::innobase_initialize_autoinc(): Change the return type to > void. Do not attempt to read the maximum auto-increment value from > the table if innodb_force_recovery is set to at least 4. Issue > ER_AUTOINC_READ_FAILED to the client when the auto-increment value > cannot be read. > > rb://144 by Sunny, revised by Marko --- storage/innobase/handler/ha_innodb.cc | 237 +++++++++++++++----------- storage/innobase/handler/ha_innodb.h | 2 +- 2 files changed, 134 insertions(+), 105 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index a1b8c551845..9b6c2cf9895 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2575,58 +2575,151 @@ normalize_table_name( #endif } +/************************************************************************ +Get the upper limit of the MySQL integral and floating-point type. */ +static +ulonglong +innobase_get_int_col_max_value( +/*===========================*/ + /* out: maximum allowed value for the field */ + const Field* field) /* in: MySQL field */ +{ + ulonglong max_value = 0; + + switch(field->key_type()) { + /* TINY */ + case HA_KEYTYPE_BINARY: + max_value = 0xFFULL; + break; + case HA_KEYTYPE_INT8: + max_value = 0x7FULL; + break; + /* SHORT */ + case HA_KEYTYPE_USHORT_INT: + max_value = 0xFFFFULL; + break; + case HA_KEYTYPE_SHORT_INT: + max_value = 0x7FFFULL; + break; + /* MEDIUM */ + case HA_KEYTYPE_UINT24: + max_value = 0xFFFFFFULL; + break; + case HA_KEYTYPE_INT24: + max_value = 0x7FFFFFULL; + break; + /* LONG */ + case HA_KEYTYPE_ULONG_INT: + max_value = 0xFFFFFFFFULL; + break; + case HA_KEYTYPE_LONG_INT: + max_value = 0x7FFFFFFFULL; + break; + /* BIG */ + case HA_KEYTYPE_ULONGLONG: + max_value = 0xFFFFFFFFFFFFFFFFULL; + break; + case HA_KEYTYPE_LONGLONG: + max_value = 0x7FFFFFFFFFFFFFFFULL; + break; + case HA_KEYTYPE_FLOAT: + /* We use the maximum as per IEEE754-2008 standard, 2^24 */ + max_value = 0x1000000ULL; + break; + case HA_KEYTYPE_DOUBLE: + /* We use the maximum as per IEEE754-2008 standard, 2^53 */ + max_value = 0x20000000000000ULL; + break; + default: + ut_error; + } + + return(max_value); +} + /************************************************************************ Set the autoinc column max value. This should only be called once from ha_innobase::open(). Therefore there's no need for a covering lock. */ -ulong +void ha_innobase::innobase_initialize_autoinc() /*======================================*/ { - dict_index_t* index; ulonglong auto_inc; - const char* col_name; - ulint error = DB_SUCCESS; - dict_table_t* innodb_table = prebuilt->table; - - col_name = table->found_next_number_field->field_name; - index = innobase_get_index(table->s->next_number_index); - - /* Execute SELECT MAX(col_name) FROM TABLE; */ - error = row_search_max_autoinc(index, col_name, &auto_inc); - - if (error == DB_SUCCESS) { - - /* At the this stage we dont' know the increment - or the offset, so use default inrement of 1. */ - ++auto_inc; - - dict_table_autoinc_initialize(innodb_table, auto_inc); - - } else if (error == DB_RECORD_NOT_FOUND) { - ut_print_timestamp(stderr); - fprintf(stderr, " InnoDB: MySQL and InnoDB data " - "dictionaries are out of sync.\n" - "InnoDB: Unable to find the AUTOINC column %s in the " - "InnoDB table %s.\n" - "InnoDB: We set the next AUTOINC column value to the " - "maximum possible value,\n" - "InnoDB: in effect disabling the AUTOINC next value " - "generation.\n" - "InnoDB: You can either set the next AUTOINC value " - "explicitly using ALTER TABLE\n" - "InnoDB: or fix the data dictionary by recreating " - "the table.\n", - col_name, index->table->name); + const Field* field = table->found_next_number_field; + if (field != NULL) { + auto_inc = innobase_get_int_col_max_value(field); + } else { + /* We have no idea what's been passed in to us as the + autoinc column. We set it to the MAX_INT of our table + autoinc type. */ auto_inc = 0xFFFFFFFFFFFFFFFFULL; - dict_table_autoinc_initialize(innodb_table, auto_inc); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Unable to determine the AUTOINC " + "column name\n"); + } - error = DB_SUCCESS; - } /* else other errors are still fatal */ + if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { + /* If the recovery level is set so high that writes + are disabled we force the AUTOINC counter to the MAX + value effectively disabling writes to the table. + Secondly, we avoid reading the table in case the read + results in failure due to a corrupted table/index. - return(ulong(error)); + We will not return an error to the client, so that the + tables can be dumped with minimal hassle. If an error + were returned in this case, the first attempt to read + the table would fail and subsequent SELECTs would succeed. */ + } else if (field == NULL) { + my_error(ER_AUTOINC_READ_FAILED, MYF(0)); + } else { + dict_index_t* index; + const char* col_name; + ulonglong read_auto_inc; + ulint err; + + update_thd(ha_thd()); + col_name = field->field_name; + index = innobase_get_index(table->s->next_number_index); + + /* Execute SELECT MAX(col_name) FROM TABLE; */ + err = row_search_max_autoinc(index, col_name, &read_auto_inc); + + switch (err) { + case DB_SUCCESS: + /* At the this stage we do not know the increment + or the offset, so use a default increment of 1. */ + auto_inc = read_auto_inc + 1; + break; + + case DB_RECORD_NOT_FOUND: + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: MySQL and InnoDB data " + "dictionaries are out of sync.\n" + "InnoDB: Unable to find the AUTOINC column " + "%s in the InnoDB table %s.\n" + "InnoDB: We set the next AUTOINC column " + "value to the maximum possible value,\n" + "InnoDB: in effect disabling the AUTOINC " + "next value generation.\n" + "InnoDB: You can either set the next " + "AUTOINC value explicitly using ALTER TABLE\n" + "InnoDB: or fix the data dictionary by " + "recreating the table.\n", + col_name, index->table->name); + + my_error(ER_AUTOINC_READ_FAILED, MYF(0)); + break; + default: + /* row_search_max_autoinc() should only return + one of DB_SUCCESS or DB_RECORD_NOT_FOUND. */ + ut_error; + } + } + + dict_table_autoinc_initialize(prebuilt->table, auto_inc); } /********************************************************************* @@ -2824,8 +2917,6 @@ retry: /* Only if the table has an AUTOINC column. */ if (prebuilt->table != NULL && table->found_next_number_field != NULL) { - ulint error; - dict_table_autoinc_lock(prebuilt->table); /* Since a table can already be "open" in InnoDB's internal @@ -2834,8 +2925,7 @@ retry: autoinc value from a previous MySQL open. */ if (dict_table_autoinc_read(prebuilt->table) == 0) { - error = innobase_initialize_autoinc(); - ut_a(error == DB_SUCCESS); + innobase_initialize_autoinc(); } dict_table_autoinc_unlock(prebuilt->table); @@ -3650,67 +3740,6 @@ skip_field: } } -/************************************************************************ -Get the upper limit of the MySQL integral and floating-point type. */ - -ulonglong -ha_innobase::innobase_get_int_col_max_value( -/*========================================*/ - const Field* field) -{ - ulonglong max_value = 0; - - switch(field->key_type()) { - /* TINY */ - case HA_KEYTYPE_BINARY: - max_value = 0xFFULL; - break; - case HA_KEYTYPE_INT8: - max_value = 0x7FULL; - break; - /* SHORT */ - case HA_KEYTYPE_USHORT_INT: - max_value = 0xFFFFULL; - break; - case HA_KEYTYPE_SHORT_INT: - max_value = 0x7FFFULL; - break; - /* MEDIUM */ - case HA_KEYTYPE_UINT24: - max_value = 0xFFFFFFULL; - break; - case HA_KEYTYPE_INT24: - max_value = 0x7FFFFFULL; - break; - /* LONG */ - case HA_KEYTYPE_ULONG_INT: - max_value = 0xFFFFFFFFULL; - break; - case HA_KEYTYPE_LONG_INT: - max_value = 0x7FFFFFFFULL; - break; - /* BIG */ - case HA_KEYTYPE_ULONGLONG: - max_value = 0xFFFFFFFFFFFFFFFFULL; - break; - case HA_KEYTYPE_LONGLONG: - max_value = 0x7FFFFFFFFFFFFFFFULL; - break; - case HA_KEYTYPE_FLOAT: - /* We use the maximum as per IEEE754-2008 standard, 2^24 */ - max_value = 0x1000000ULL; - break; - case HA_KEYTYPE_DOUBLE: - /* We use the maximum as per IEEE754-2008 standard, 2^53 */ - max_value = 0x20000000000000ULL; - break; - default: - ut_error; - } - - return(max_value); -} - /************************************************************************ This special handling is really to overcome the limitations of MySQL's binlogging. We need to eliminate the non-determinism that will arise in diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 9ddb516c3dc..7abdd689816 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -78,7 +78,7 @@ class ha_innobase: public handler ulong innobase_reset_autoinc(ulonglong auto_inc); ulong innobase_get_autoinc(ulonglong* value); ulong innobase_update_autoinc(ulonglong auto_inc); - ulong innobase_initialize_autoinc(); + void innobase_initialize_autoinc(); dict_index_t* innobase_get_index(uint keynr); ulonglong innobase_get_int_col_max_value(const Field* field); From e21c35375a8ddc9e34320a83292d3534659bc8e3 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:10:21 +0100 Subject: [PATCH 08/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3333.1.7 [merge] > revision-id: ramil@mysql.com-20100129110849-1nm85j95594epnme > parent: joro@sun.com-20100129093628-sze9cv0neu0xbabm > parent: ramil@mysql.com-20100129091757-81r640na2t5bzbiz > committer: Ramil Kalimullin > branch nick: mysql-5.1-bugteam > timestamp: Fri 2010-01-29 15:08:49 +0400 > message: > Auto-merge. > ------------------------------------------------------------ > Use --include-merges or -n0 to see merged revisions. --- mysql-test/r/select.result | 63 ++++++++++++++++++++++++++++++++++++++ mysql-test/t/select.test | 40 ++++++++++++++++++++++++ sql/filesort.cc | 2 ++ sql/sql_select.cc | 13 ++++++++ 4 files changed, 118 insertions(+) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index dce927a5c8d..bd7968583dc 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -4526,6 +4526,69 @@ id select_type table type possible_keys key key_len ref rows filtered Extra Warnings: Note 1003 select '2001-01-01 00:00:00' AS `a`,'2001-01-01 00:00:00' AS `a`,'2001-01-01 00:00:00' AS `a` from `test`.`t1` `x` join `test`.`t1` `y` join `test`.`t1` `z` where 1 DROP TABLE t1; +# +# Bug #49897: crash in ptr_compare when char(0) NOT NULL +# column is used for ORDER BY +# +SET @old_sort_buffer_size= @@session.sort_buffer_size; +SET @@sort_buffer_size= 40000; +CREATE TABLE t1(a CHAR(0) NOT NULL); +INSERT INTO t1 VALUES (0), (0), (0); +INSERT INTO t1 SELECT t11.a FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a FROM t1 t11, t1 t12; +EXPLAIN SELECT a FROM t1 ORDER BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 24492 +SELECT a FROM t1 ORDER BY a; +DROP TABLE t1; +CREATE TABLE t1(a CHAR(0) NOT NULL, b CHAR(0) NOT NULL, c int); +INSERT INTO t1 VALUES (0, 0, 0), (0, 0, 2), (0, 0, 1); +INSERT INTO t1 SELECT t11.a, t11.b, t11.c FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a, t11.b, t11.c FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a, t11.b, t11.c FROM t1 t11, t1 t12; +EXPLAIN SELECT a FROM t1 ORDER BY a LIMIT 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 24492 +SELECT a FROM t1 ORDER BY a LIMIT 5; +a + + + + + +EXPLAIN SELECT * FROM t1 ORDER BY a, b LIMIT 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 24492 +SELECT * FROM t1 ORDER BY a, b LIMIT 5; +a b c + 0 + 2 + 1 + 0 + 2 +EXPLAIN SELECT * FROM t1 ORDER BY a, b, c LIMIT 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 24492 Using filesort +SELECT * FROM t1 ORDER BY a, b, c LIMIT 5; +a b c + 0 + 0 + 0 + 0 + 0 +EXPLAIN SELECT * FROM t1 ORDER BY c, a LIMIT 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 24492 Using filesort +SELECT * FROM t1 ORDER BY c, a LIMIT 5; +a b c + 0 + 0 + 0 + 0 + 0 +SET @@sort_buffer_size= @old_sort_buffer_size; +DROP TABLE t1; End of 5.0 tests create table t1(a INT, KEY (a)); INSERT INTO t1 VALUES (1),(2),(3),(4),(5); diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index e58cea1eef3..f61db538fb4 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -3836,6 +3836,46 @@ EXPLAIN EXTENDED SELECT x.a, y.a, z.a FROM t1 x DROP TABLE t1; +--echo # +--echo # Bug #49897: crash in ptr_compare when char(0) NOT NULL +--echo # column is used for ORDER BY +--echo # +SET @old_sort_buffer_size= @@session.sort_buffer_size; +SET @@sort_buffer_size= 40000; + +CREATE TABLE t1(a CHAR(0) NOT NULL); +--disable_warnings +INSERT INTO t1 VALUES (0), (0), (0); +--enable_warnings +INSERT INTO t1 SELECT t11.a FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a FROM t1 t11, t1 t12; +EXPLAIN SELECT a FROM t1 ORDER BY a; +--disable_result_log +SELECT a FROM t1 ORDER BY a; +--enable_result_log +DROP TABLE t1; + +CREATE TABLE t1(a CHAR(0) NOT NULL, b CHAR(0) NOT NULL, c int); +--disable_warnings +INSERT INTO t1 VALUES (0, 0, 0), (0, 0, 2), (0, 0, 1); +--enable_warnings +INSERT INTO t1 SELECT t11.a, t11.b, t11.c FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a, t11.b, t11.c FROM t1 t11, t1 t12; +INSERT INTO t1 SELECT t11.a, t11.b, t11.c FROM t1 t11, t1 t12; +EXPLAIN SELECT a FROM t1 ORDER BY a LIMIT 5; +SELECT a FROM t1 ORDER BY a LIMIT 5; +EXPLAIN SELECT * FROM t1 ORDER BY a, b LIMIT 5; +SELECT * FROM t1 ORDER BY a, b LIMIT 5; +EXPLAIN SELECT * FROM t1 ORDER BY a, b, c LIMIT 5; +SELECT * FROM t1 ORDER BY a, b, c LIMIT 5; +EXPLAIN SELECT * FROM t1 ORDER BY c, a LIMIT 5; +SELECT * FROM t1 ORDER BY c, a LIMIT 5; + +SET @@sort_buffer_size= @old_sort_buffer_size; +DROP TABLE t1; + + --echo End of 5.0 tests # diff --git a/sql/filesort.cc b/sql/filesort.cc index 5d8b4e869c8..7b584b39516 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -142,6 +142,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, error= 1; bzero((char*) ¶m,sizeof(param)); param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset); + /* filesort cannot handle zero-length records. */ + DBUG_ASSERT(param.sort_length); param.ref_length= table->file->ref_length; param.addon_field= 0; param.addon_length= 0; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d5ce32902c4..c1c3827106a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -539,13 +539,26 @@ JOIN::prepare(Item ***rref_pointer_array, if (order) { + bool real_order= FALSE; ORDER *ord; for (ord= order; ord; ord= ord->next) { Item *item= *ord->item; + /* + Disregard sort order if there's only "{VAR}CHAR(0) NOT NULL" fields + there. Such fields don't contain any data to sort. + */ + if (!real_order && + (item->type() != Item::Item::FIELD_ITEM || + ((Item_field *) item)->field->maybe_null() || + ((Item_field *) item)->field->sort_length())) + real_order= TRUE; + if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) item->split_sum_func(thd, ref_pointer_array, all_fields); } + if (!real_order) + order= NULL; } if (having && having->with_sum_func) From b68a8cb23aef639397eede714bbe0a007d39dfa3 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:11:57 +0100 Subject: [PATCH 09/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3333.1.11 [merge] > revision-id: joro@sun.com-20100201115030-hgvq6489bt0w3rty > parent: li-bing.song@sun.com-20100130124925-o6sfex42b6noyc6x > parent: joro@sun.com-20100201114016-jylx4hivgqbs0vg2 > committer: Georgi Kodinov > branch nick: test-5.1-bugteam > timestamp: Mon 2010-02-01 13:50:30 +0200 > message: > merge > ------------------------------------------------------------ > Use --include-merges or -n0 to see merged revisions. --- sql/sql_select.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c1c3827106a..0e36d35289f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -549,7 +549,7 @@ JOIN::prepare(Item ***rref_pointer_array, there. Such fields don't contain any data to sort. */ if (!real_order && - (item->type() != Item::Item::FIELD_ITEM || + (item->type() != Item::FIELD_ITEM || ((Item_field *) item)->field->maybe_null() || ((Item_field *) item)->field->sort_length())) real_order= TRUE; From 5aa2394a64fcc2ab0efd78ed45c1940d494a1437 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:16:49 +0100 Subject: [PATCH 10/29] Backporting of 5.1.43sp1 release, files mysql-test/r/bug39022.result, mysql-test/t/bug39022.test added --- mysql-test/r/bug39022.result | 32 ++++++++++++++++++++ mysql-test/t/bug39022.test | 58 ++++++++++++++++++++++++++++++++++++ sql/sql_select.cc | 46 +++++++++++++++++++++------- 3 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 mysql-test/r/bug39022.result create mode 100644 mysql-test/t/bug39022.test diff --git a/mysql-test/r/bug39022.result b/mysql-test/r/bug39022.result new file mode 100644 index 00000000000..1c02d7873e4 --- /dev/null +++ b/mysql-test/r/bug39022.result @@ -0,0 +1,32 @@ +# +# Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees +# +CREATE TABLE t1(a TINYINT NOT NULL,b TINYINT,PRIMARY KEY(b)) ENGINE=innodb; +CREATE TABLE t2(d TINYINT NOT NULL,UNIQUE KEY(d)) ENGINE=innodb; +INSERT INTO t1 VALUES (13,0),(8,1),(9,2),(6,3), +(11,5),(11,6),(7,7),(7,8),(4,9),(6,10),(3,11),(11,12), +(12,13),(7,14); +INSERT INTO t2 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10), +(11),(12),(13),(14); +# in thread1 +START TRANSACTION; +# in thread2 +REPLACE INTO t2 VALUES (-17); +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); +d +# in thread1 +REPLACE INTO t1(a,b) VALUES (67,20); +# in thread2 +COMMIT; +START TRANSACTION; +REPLACE INTO t1(a,b) VALUES (65,-50); +REPLACE INTO t2 VALUES (-91); +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); +# in thread1 +# should not crash +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# in thread2 +d +# in default +DROP TABLE t1,t2; diff --git a/mysql-test/t/bug39022.test b/mysql-test/t/bug39022.test new file mode 100644 index 00000000000..1a1d10f5592 --- /dev/null +++ b/mysql-test/t/bug39022.test @@ -0,0 +1,58 @@ +-- source include/have_log_bin.inc +-- source include/have_innodb.inc + +--echo # +--echo # Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees +--echo # + +CREATE TABLE t1(a TINYINT NOT NULL,b TINYINT,PRIMARY KEY(b)) ENGINE=innodb; +CREATE TABLE t2(d TINYINT NOT NULL,UNIQUE KEY(d)) ENGINE=innodb; +INSERT INTO t1 VALUES (13,0),(8,1),(9,2),(6,3), +(11,5),(11,6),(7,7),(7,8),(4,9),(6,10),(3,11),(11,12), +(12,13),(7,14); +INSERT INTO t2 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10), +(11),(12),(13),(14); + +connect (thread1, localhost, root,,); +connect (thread2, localhost, root,,); + +connection thread1; +--echo # in thread1 +START TRANSACTION; + +connection thread2; +--echo # in thread2 +REPLACE INTO t2 VALUES (-17); +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); + +connection thread1; +--echo # in thread1 +REPLACE INTO t1(a,b) VALUES (67,20); + +connection thread2; +--echo # in thread2 +COMMIT; +START TRANSACTION; +REPLACE INTO t1(a,b) VALUES (65,-50); +REPLACE INTO t2 VALUES (-91); +send; +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); #waits + +connection thread1; +--echo # in thread1 + +--echo # should not crash +--error ER_LOCK_DEADLOCK +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); #crashes + +connection thread2; +--echo # in thread2 +REAP; + +connection default; +--echo # in default + +disconnect thread1; +disconnect thread2; + +DROP TABLE t1,t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0e36d35289f..2ebe4fdbe87 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -11509,21 +11509,45 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last) return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */ } SQL_SELECT *select=join_tab->select; - if (rc == NESTED_LOOP_OK && - (!join_tab->cache.select || !join_tab->cache.select->skip_record())) + if (rc == NESTED_LOOP_OK) { - uint i; - reset_cache_read(&join_tab->cache); - for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;) + bool consider_record= !join_tab->cache.select || + !join_tab->cache.select->skip_record(); + + /* + Check for error: skip_record() can execute code by calling + Item_subselect::val_*. We need to check for errors (if any) + after such call. + */ + if (join->thd->is_error()) { - read_cached_record(join_tab); - if (!select || !select->skip_record()) + reset_cache_write(&join_tab->cache); + return NESTED_LOOP_ERROR; + } + + if (consider_record) + { + uint i; + reset_cache_read(&join_tab->cache); + for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;) { - rc= (join_tab->next_select)(join,join_tab+1,0); - if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS) + read_cached_record(join_tab); + if (!select || !select->skip_record()) { - reset_cache_write(&join_tab->cache); - return rc; + /* + Check for error: skip_record() can execute code by calling + Item_subselect::val_*. We need to check for errors (if any) + after such call. + */ + if (join->thd->is_error()) + rc= NESTED_LOOP_ERROR; + else + rc= (join_tab->next_select)(join,join_tab+1,0); + if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS) + { + reset_cache_write(&join_tab->cache); + return rc; + } } } } From f6f93eefa73053fa7dc3d2edb065866beeda40bd Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:18:38 +0100 Subject: [PATCH 11/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3333.1.31 > revision-id: joro@sun.com-20091223104518-o29t0i3thgs7wgm1 > parent: sergey.glukhov@sun.com-20100205093946-bx1hsljxlm12h7uf > committer: Georgi Kodinov > branch nick: B39022-5.1-bugteam > timestamp: Wed 2009-12-23 12:45:18 +0200 > message: > Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees > > flush_cached_records() was not correctly checking for errors after calling > Item::val_xxx() methods. The expressions may contain subqueries > or stored procedures that cause errors that should stop the statement. > Fixed by correctly checking for errors and propagating them up the call stack. > ------------------------------------------------------------ > revno: 3358 > revision-id: sergey.glukhov@sun.com-20100226113925-mxwn1hfxe3l8khc4 > parent: gshchepa@mysql.com-20100225191311-1x71dkk0h5e1alvx > committer: Sergey Glukhov > branch nick: mysql-5.1-bugteam > timestamp: Fri 2010-02-26 15:39:25 +0400 > message: > Bug#50995 Having clause on subquery result produces incorrect results. > The problem is that cond->fix_fields(thd, 0) breaks > condition(cuts off 'having'). The reason of that is > that NULL valued Item pointer is present in the > middle of Item list and it breaks the Item processing > loop. --- mysql-test/r/having.result | 20 ++++++++++++++++++++ mysql-test/t/having.test | 26 ++++++++++++++++++++++++++ sql/item_cmpfunc.h | 18 +++++++++++++++--- sql/sql_select.cc | 8 +++++--- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result index 9c3cc8fc89e..68ba34e353c 100644 --- a/mysql-test/r/having.result +++ b/mysql-test/r/having.result @@ -430,4 +430,24 @@ SELECT b, COUNT(DISTINCT a) FROM t1 GROUP BY b HAVING b is NULL; b COUNT(DISTINCT a) NULL 1 DROP TABLE t1; +# +# Bug#50995 Having clause on subquery result produces incorrect results. +# +CREATE TABLE t1 +( +id1 INT, +id2 INT NOT NULL, +INDEX id1(id2) +); +INSERT INTO t1 SET id1=1, id2=1; +INSERT INTO t1 SET id1=2, id2=1; +INSERT INTO t1 SET id1=3, id2=1; +SELECT t1.id1, +(SELECT 0 FROM DUAL +WHERE t1.id1=t1.id1) AS amount FROM t1 +WHERE t1.id2 = 1 +HAVING amount > 0 +ORDER BY t1.id1; +id1 amount +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/having.test b/mysql-test/t/having.test index af9af4fe1fc..185ca4bdddb 100644 --- a/mysql-test/t/having.test +++ b/mysql-test/t/having.test @@ -442,4 +442,30 @@ INSERT INTO t1 VALUES (1, 1), (2,2), (3, NULL); SELECT b, COUNT(DISTINCT a) FROM t1 GROUP BY b HAVING b is NULL; DROP TABLE t1; + +--echo # +--echo # Bug#50995 Having clause on subquery result produces incorrect results. +--echo # + +CREATE TABLE t1 +( + id1 INT, + id2 INT NOT NULL, + INDEX id1(id2) +); + +INSERT INTO t1 SET id1=1, id2=1; +INSERT INTO t1 SET id1=2, id2=1; +INSERT INTO t1 SET id1=3, id2=1; + +SELECT t1.id1, +(SELECT 0 FROM DUAL + WHERE t1.id1=t1.id1) AS amount FROM t1 +WHERE t1.id2 = 1 +HAVING amount > 0 +ORDER BY t1.id1; + +DROP TABLE t1; + + --echo End of 5.0 tests diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 38025ff0af5..425f54fb079 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1474,9 +1474,21 @@ public: Item_cond(THD *thd, Item_cond *item); Item_cond(List &nlist) :Item_bool_func(), list(nlist), abort_on_null(0) {} - bool add(Item *item) { return list.push_back(item); } - bool add_at_head(Item *item) { return list.push_front(item); } - void add_at_head(List *nlist) { list.prepand(nlist); } + bool add(Item *item) + { + DBUG_ASSERT(item); + return list.push_back(item); + } + bool add_at_head(Item *item) + { + DBUG_ASSERT(item); + return list.push_front(item); + } + void add_at_head(List *nlist) + { + DBUG_ASSERT(nlist->elements); + list.prepand(nlist); + } bool fix_fields(THD *, Item **ref); enum Type type() const { return COND_ITEM; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2ebe4fdbe87..d73aa3964d0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8176,7 +8176,8 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, else { DBUG_ASSERT(cond->type() == Item::COND_ITEM); - ((Item_cond *) cond)->add_at_head(&eq_list); + if (eq_list.elements) + ((Item_cond *) cond)->add_at_head(&eq_list); } cond->quick_fix_field(); @@ -15613,7 +15614,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) Item_cond_and *cond=new Item_cond_and(); TABLE *table=join_tab->table; - int error; + int error= 0; if (!cond) DBUG_RETURN(TRUE); @@ -15631,7 +15632,8 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) cond->fix_fields(thd, (Item**)&cond); if (join_tab->select) { - error=(int) cond->add(join_tab->select->cond); + if (join_tab->select->cond) + error=(int) cond->add(join_tab->select->cond); join_tab->select_cond=join_tab->select->cond=cond; } else if ((join_tab->select= make_select(join_tab->table, 0, 0, cond, 0, From 93a35a95e4e5dc393e8aab554b033128f0ddb38d Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:50:26 +0100 Subject: [PATCH 12/29] Backporting for 5.1.43sp1 ,file added mysql-test/include/not_binlog_format_row.inc --- mysql-test/include/not_binlog_format_row.inc | 4 ++++ mysql-test/r/bug39022.result | 2 +- mysql-test/t/bug39022.test | 13 +++++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 mysql-test/include/not_binlog_format_row.inc diff --git a/mysql-test/include/not_binlog_format_row.inc b/mysql-test/include/not_binlog_format_row.inc new file mode 100644 index 00000000000..f9354e7cd33 --- /dev/null +++ b/mysql-test/include/not_binlog_format_row.inc @@ -0,0 +1,4 @@ +if (`SELECT @@binlog_format = 'ROW'`) +{ + skip Test cannot run with binlog_format row; +} diff --git a/mysql-test/r/bug39022.result b/mysql-test/r/bug39022.result index 1c02d7873e4..5963709aa2a 100644 --- a/mysql-test/r/bug39022.result +++ b/mysql-test/r/bug39022.result @@ -28,5 +28,5 @@ SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # in thread2 d -# in default +# in thread1; DROP TABLE t1,t2; diff --git a/mysql-test/t/bug39022.test b/mysql-test/t/bug39022.test index 1a1d10f5592..268b207e0e5 100644 --- a/mysql-test/t/bug39022.test +++ b/mysql-test/t/bug39022.test @@ -1,5 +1,6 @@ -- source include/have_log_bin.inc -- source include/have_innodb.inc +-- source include/not_binlog_format_row.inc --echo # --echo # Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees @@ -49,10 +50,14 @@ connection thread2; --echo # in thread2 REAP; -connection default; ---echo # in default - -disconnect thread1; disconnect thread2; +--source include/wait_until_disconnected.inc + +connection thread1; +--echo # in thread1; +disconnect thread1; +--source include/wait_until_disconnected.inc + +connection default; DROP TABLE t1,t2; From 766d9cff72929f355d6fb8e7074eab9fa8d3fc44 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Wed, 24 Mar 2010 13:53:23 +0100 Subject: [PATCH 13/29] Backport into build-201003230706-5.1.43sp1 > ------------------------------------------------------------ > revno: 3345.2.1 > revision-id: joro@sun.com-20100218084815-53nb9oonzd7r4gmj > parent: sergey.glukhov@sun.com-20100217121457-jqx19u6x387rgk7e > committer: Georgi Kodinov > branch nick: fix-5.1-bugteam > timestamp: Thu 2010-02-18 10:48:15 +0200 > message: > Bug #51049: main.bug39022 fails in mysql-trunk-merge > > Fixed the test to behave correctly with ps-protocol > and binlog format row. > ------------------------------------------------------------ > revno: 3333.1.6 > revision-id: joro@sun.com-20100129093628-sze9cv0neu0xbabm > parent: davi.arnaut@sun.com-20100128215140-x0w6fe2de0b28opp > committer: Georgi Kodinov > branch nick: B49552-5.1-bugteam > timestamp: Fri 2010-01-29 11:36:28 +0200 > message: > Bug #49552 : sql_buffer_result cause crash + not found records > in multitable delete/subquery > > SQL_BUFFER_RESULT should not have an effect on non-SELECT > statements according to our documentation. > Fixed by not passing it through to multi-table DELETE (similarly > to how it's done for multi-table UPDATE). --- mysql-test/r/delete.result | 12 ++++++++++++ mysql-test/t/delete.test | 15 +++++++++++++++ sql/sql_parse.cc | 4 ++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index 1df19a75854..8144ed937e3 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -278,6 +278,18 @@ DELETE FROM t1 ORDER BY (f1(10)) LIMIT 1; ERROR 42000: Incorrect number of arguments for FUNCTION test.f1; expected 0, got 1 DROP TABLE t1; DROP FUNCTION f1; +# +# Bug #49552 : sql_buffer_result cause crash + not found records +# in multitable delete/subquery +# +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (1),(2),(3); +SET SESSION SQL_BUFFER_RESULT=1; +DELETE t1 FROM (SELECT SUM(a) a FROM t1) x,t1; +SET SESSION SQL_BUFFER_RESULT=DEFAULT; +SELECT * FROM t1; +a +DROP TABLE t1; End of 5.0 tests # # Bug#46958: Assertion in Diagnostics_area::set_ok_status, trigger, diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index a5dff38c078..537f419a454 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -291,6 +291,21 @@ DELETE FROM t1 ORDER BY (f1(10)) LIMIT 1; DROP TABLE t1; DROP FUNCTION f1; + +--echo # +--echo # Bug #49552 : sql_buffer_result cause crash + not found records +--echo # in multitable delete/subquery +--echo # + +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (1),(2),(3); +SET SESSION SQL_BUFFER_RESULT=1; +DELETE t1 FROM (SELECT SUM(a) a FROM t1) x,t1; + +SET SESSION SQL_BUFFER_RESULT=DEFAULT; +SELECT * FROM t1; +DROP TABLE t1; + --echo End of 5.0 tests --echo # diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7eabc1887f9..b59ebbbb2a4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3350,9 +3350,9 @@ end_with_restore_list: select_lex->where, 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, (ORDER *)NULL, - select_lex->options | thd->options | + (select_lex->options | thd->options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE, + OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, del_result, unit, select_lex); res|= thd->is_error(); if (res) From edbb0ba6c2ff46c42919963c412d716a2e16cfaf Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Thu, 25 Mar 2010 11:17:29 +0100 Subject: [PATCH 14/29] Backport of "Bug #50409 Solaris 8 compatibility broken by assumption about printstack() being present" --- include/my_stacktrace.h | 2 +- storage/innobase/handler/ha_innodb.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/my_stacktrace.h b/include/my_stacktrace.h index df384b1c566..9250fd4579e 100644 --- a/include/my_stacktrace.h +++ b/include/my_stacktrace.h @@ -23,7 +23,7 @@ (defined(__alpha__) && defined(__GNUC__)) #define HAVE_STACKTRACE 1 #endif -#elif defined(__WIN__) || defined(__sun) +#elif defined(__WIN__) || defined(HAVE_PRINTSTACK) #define HAVE_STACKTRACE 1 #endif diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 7abdd689816..5b3df16875a 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -80,7 +80,6 @@ class ha_innobase: public handler ulong innobase_update_autoinc(ulonglong auto_inc); void innobase_initialize_autoinc(); dict_index_t* innobase_get_index(uint keynr); - ulonglong innobase_get_int_col_max_value(const Field* field); /* Init values for the class: */ public: From 7357c325b987953281d004bbe4ab835704948615 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 6 Apr 2010 15:57:33 +0200 Subject: [PATCH 15/29] Raise version number after cloning 5.1.46 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 904c54abb5f..7148c26187a 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.46], [], [mysql]) +AC_INIT([MySQL Server], [5.1.47], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM From f34a731b6a3ae0b2f1d9a3e8f9d987873ceaf32b Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 12 Apr 2010 11:56:28 +0300 Subject: [PATCH 16/29] Bug #43594: mysqlhotcopy does not ignore log tables and others in mysql database Added a filter to mysqlhotcopy to filter out the same tables in the 'mysql' database that mysqldump filters out. --- scripts/mysqlhotcopy.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/mysqlhotcopy.sh b/scripts/mysqlhotcopy.sh index 21fca0c0848..398573875d9 100644 --- a/scripts/mysqlhotcopy.sh +++ b/scripts/mysqlhotcopy.sh @@ -267,6 +267,14 @@ foreach my $rdb ( @db_desc ) { my $db = $rdb->{src}; my @dbh_tables = get_list_of_tables( $db ); + ## filter out certain system non-lockable tables. + ## keep in sync with mysqldump. + if ($db =~ m/^mysql$/i) + { + @dbh_tables = grep + { !/^(apply_status|schema|general_log|slow_log)$/ } @dbh_tables + } + ## generate regex for tables/files my $t_regex; my $negated; From 4aa36ee7b689c4e9f74ccdc08fa278df66042fe8 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Wed, 14 Apr 2010 13:53:59 +0400 Subject: [PATCH 17/29] BUG#39053 - UNISTALL PLUGIN does not allow the storage engine to cleanup open connections It was possible to UNINSTALL storage engine plugin when binding between THD object and storage engine is still active (e.g. in the middle of transaction). To avoid unclean deactivation (uninstall) of storage engine plugin in the middle of transaction, additional storage engine plugin lock is acquired by thd_set_ha_data(). If ha_data is not null and storage engine plugin was not locked by thd_set_ha_data() in this connection before, storage engine plugin gets locked. If ha_data is null and storage engine plugin was locked by thd_set_ha_data() in this connection before, storage engine plugin lock gets released. If handlerton::close_connection() didn't reset ha_data, server does it immediately after calling handlerton::close_connection(). Note that this is just a framework fix, storage engines must switch to thd_set_ha_data() from thd_ha_data() if they want to see fit. include/mysql/plugin.h: As thd_{get|set}_ha_data() have some extra logic now, they must be implemented on server side. include/mysql/plugin.h.pp: As thd_{get|set}_ha_data() have some extra logic now, they must be implemented on server side. sql/handler.cc: Make sure ha_data is reset and ha_data lock is released. sql/handler.h: hton is not supposed to be updated by ha_lock_engine(), make it const. sql/sql_class.cc: As thd_{get|set}_ha_data() have some extra logic now, they must be implemented on server side. sql/sql_class.h: Added ha_data lock. --- include/mysql/plugin.h | 39 +++++++++++++++++++++++---------------- include/mysql/plugin.h.pp | 3 +++ sql/handler.cc | 12 ++++++++---- sql/handler.h | 2 +- sql/sql_class.cc | 31 +++++++++++++++++++++++++++++++ sql/sql_class.h | 6 +++++- 6 files changed, 71 insertions(+), 22 deletions(-) diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index 58b3848b85f..55ef6070f85 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -801,30 +801,37 @@ void mysql_query_cache_invalidate4(MYSQL_THD thd, const char *key, unsigned int key_length, int using_trx); -#ifdef __cplusplus -} -#endif -#ifdef __cplusplus /** Provide a handler data getter to simplify coding */ -inline -void * -thd_get_ha_data(const MYSQL_THD thd, const struct handlerton *hton) -{ - return *thd_ha_data(thd, hton); -} +void *thd_get_ha_data(const MYSQL_THD thd, const struct handlerton *hton); + /** Provide a handler data setter to simplify coding + + @details + Set ha_data pointer (storage engine per-connection information). + + To avoid unclean deactivation (uninstall) of storage engine plugin + in the middle of transaction, additional storage engine plugin + lock is acquired. + + If ha_data is not null and storage engine plugin was not locked + by thd_set_ha_data() in this connection before, storage engine + plugin gets locked. + + If ha_data is null and storage engine plugin was locked by + thd_set_ha_data() in this connection before, storage engine + plugin lock gets released. + + If handlerton::close_connection() didn't reset ha_data, server does + it immediately after calling handlerton::close_connection(). */ -inline -void -thd_set_ha_data(const MYSQL_THD thd, const struct handlerton *hton, - const void *ha_data) -{ - *thd_ha_data(thd, hton)= (void*) ha_data; +void thd_set_ha_data(MYSQL_THD thd, const struct handlerton *hton, + const void *ha_data); +#ifdef __cplusplus } #endif diff --git a/include/mysql/plugin.h.pp b/include/mysql/plugin.h.pp index 50511f515ab..e4906ea6547 100644 --- a/include/mysql/plugin.h.pp +++ b/include/mysql/plugin.h.pp @@ -137,3 +137,6 @@ void thd_get_xid(const void* thd, MYSQL_XID *xid); void mysql_query_cache_invalidate4(void* thd, const char *key, unsigned int key_length, int using_trx); +void *thd_get_ha_data(const void* thd, const struct handlerton *hton); +void thd_set_ha_data(void* thd, const struct handlerton *hton, + const void *ha_data); diff --git a/sql/handler.cc b/sql/handler.cc index 216228ed509..19f397ef09f 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -159,7 +159,7 @@ redo: } -plugin_ref ha_lock_engine(THD *thd, handlerton *hton) +plugin_ref ha_lock_engine(THD *thd, const handlerton *hton) { if (hton) { @@ -601,9 +601,13 @@ static my_bool closecon_handlerton(THD *thd, plugin_ref plugin, there's no need to rollback here as all transactions must be rolled back already */ - if (hton->state == SHOW_OPTION_YES && hton->close_connection && - thd_get_ha_data(thd, hton)) - hton->close_connection(hton, thd); + if (hton->state == SHOW_OPTION_YES && thd_get_ha_data(thd, hton)) + { + if (hton->close_connection) + hton->close_connection(hton, thd); + /* make sure ha_data is reset and ha_data_lock is released */ + thd_set_ha_data(thd, hton, NULL); + } return FALSE; } diff --git a/sql/handler.h b/sql/handler.h index 7fc2bf2fece..d9dfd4f0707 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1956,7 +1956,7 @@ extern ulong total_ha, total_ha_2pc; /* lookups */ handlerton *ha_default_handlerton(THD *thd); plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name); -plugin_ref ha_lock_engine(THD *thd, handlerton *hton); +plugin_ref ha_lock_engine(THD *thd, const handlerton *hton); handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type); handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc, handlerton *db_type); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 266064f9f08..bf5af7141c0 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -284,6 +284,37 @@ void **thd_ha_data(const THD *thd, const struct handlerton *hton) return (void **) &thd->ha_data[hton->slot].ha_ptr; } + +/** + Provide a handler data getter to simplify coding +*/ +extern "C" +void *thd_get_ha_data(const THD *thd, const struct handlerton *hton) +{ + return *thd_ha_data(thd, hton); +} + + +/** + Provide a handler data setter to simplify coding + @see thd_set_ha_data() definition in plugin.h +*/ +extern "C" +void thd_set_ha_data(THD *thd, const struct handlerton *hton, + const void *ha_data) +{ + plugin_ref *lock= &thd->ha_data[hton->slot].lock; + if (ha_data && !*lock) + *lock= ha_lock_engine(NULL, (handlerton*) hton); + else if (!ha_data && *lock) + { + plugin_unlock(NULL, *lock); + *lock= NULL; + } + *thd_ha_data(thd, hton)= (void*) ha_data; +} + + extern "C" long long thd_test_options(const THD *thd, long long test_options) { diff --git a/sql/sql_class.h b/sql/sql_class.h index 2ddd9358382..4d0552c5b9d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1264,7 +1264,11 @@ struct Ha_data @sa trans_register_ha() */ Ha_trx_info ha_info[2]; - + /** + NULL: engine is not bound to this thread + non-NULL: engine is bound to this thread, engine shutdown forbidden + */ + plugin_ref lock; Ha_data() :ha_ptr(NULL) {} }; From 16fadb10b5d2a9328b5caa85963a3d851b35cef4 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 16 Apr 2010 10:30:53 +0300 Subject: [PATCH 18/29] Bug #52629: memory leak from sys_var_thd_dbug in binlog.binlog_write_error When re-setting (SET GLOBAL debug='') the GLOBAL debug settings the server was not freeing the data elements from the top (initial) frame before setting them to 0 without freeing the underlying memory. As these are global settings there's a chance that something is there already. Fixed by : 1. making sure the allocated data are cleaned up before re-setting them while parsing a debug string 2. making sure the stuff allocated in the global settings is freed on shutdown. --- dbug/dbug.c | 10 ++++++++++ mysql-test/r/variables_debug.result | 13 +++++++++++++ mysql-test/t/variables_debug.test | 13 +++++++++++++ 3 files changed, 36 insertions(+) diff --git a/dbug/dbug.c b/dbug/dbug.c index baf080f5e27..30ad6c2c6d1 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -455,6 +455,13 @@ static void DbugParse(CODE_STATE *cs, const char *control) rel= control[0] == '+' || control[0] == '-'; if ((!rel || (!stack->out_file && !stack->next))) { + /* + We need to free what's already in init_settings, because unlike + the thread related stack frames there's a chance that something + is in these variables already. + */ + if (stack == &init_settings) + FreeState(cs, stack, 0); stack->flags= 0; stack->delay= 0; stack->maxdepth= 0; @@ -1510,7 +1517,10 @@ void _db_end_() while ((discard= cs->stack)) { if (discard == &init_settings) + { + FreeState (cs, discard, 0); break; + } cs->stack= discard->next; FreeState(cs, discard, 1); } diff --git a/mysql-test/r/variables_debug.result b/mysql-test/r/variables_debug.result index 9cd133dddb1..85eaf34b033 100644 --- a/mysql-test/r/variables_debug.result +++ b/mysql-test/r/variables_debug.result @@ -10,3 +10,16 @@ set debug= '-P'; select @@debug; @@debug T +# +# Bug #52629: memory leak from sys_var_thd_dbug in +# binlog.binlog_write_error +# +SET GLOBAL debug='d,injecting_fault_writing'; +SELECT @@global.debug; +@@global.debug +d,injecting_fault_writing +SET GLOBAL debug=''; +SELECT @@global.debug; +@@global.debug + +End of 5.1 tests diff --git a/mysql-test/t/variables_debug.test b/mysql-test/t/variables_debug.test index 7dcaf246803..8f2bde7ae42 100644 --- a/mysql-test/t/variables_debug.test +++ b/mysql-test/t/variables_debug.test @@ -10,3 +10,16 @@ set debug= '+P'; select @@debug; set debug= '-P'; select @@debug; + +--echo # +--echo # Bug #52629: memory leak from sys_var_thd_dbug in +--echo # binlog.binlog_write_error +--echo # + +SET GLOBAL debug='d,injecting_fault_writing'; +SELECT @@global.debug; +SET GLOBAL debug=''; +SELECT @@global.debug; + + +--echo End of 5.1 tests From 649deaa8a1ab7660322152156b862a27de78d327 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Fri, 16 Apr 2010 16:42:34 +0500 Subject: [PATCH 19/29] Bug#52124 memory leaks like a sieve in datetime, timestamp, time, date fields + warnings Arg_comparator initializes 'comparators' array in case of ROW comparison and does not free this array on destruction. It leads to memory leaks. The fix: -added Arg_comparator::cleanup() method which frees 'comparators' array. -added Item_bool_func2::cleanup() method which calls Arg_comparator::cleanup() method mysql-test/r/ps.result: test case mysql-test/r/row.result: test case mysql-test/t/ps.test: test case mysql-test/t/row.test: test case sql/item_cmpfunc.h: -added Arg_comparator::cleanup() method which frees 'comparators' array. -added Item_bool_func2::cleanup() method which calls Arg_comparator::cleanup() method --- mysql-test/r/ps.result | 13 +++++++++++++ mysql-test/r/row.result | 9 +++++++++ mysql-test/t/ps.test | 14 ++++++++++++++ mysql-test/t/row.test | 11 +++++++++++ sql/item_cmpfunc.h | 14 ++++++++++++-- 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index cf08d763e5c..37ac3426e43 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -2988,4 +2988,17 @@ select @plaintext; bcd deallocate prepare encode; deallocate prepare decode; +# +# Bug#52124 memory leaks like a sieve in datetime, timestamp, time, date fields + warnings +# +CREATE TABLE t1 (a DATETIME NOT NULL, b TINYINT); +INSERT INTO t1 VALUES (0, 0),(0, 0); +PREPARE stmt FROM "SELECT 1 FROM t1 WHERE +ROW(a, b) >= ROW('1', (SELECT 1 FROM t1 WHERE a > 1234))"; +EXECUTE stmt; +1 +EXECUTE stmt; +1 +DEALLOCATE PREPARE stmt; +DROP TABLE t1; End of 5.1 tests. diff --git a/mysql-test/r/row.result b/mysql-test/r/row.result index 2eaec67c547..2962123fcb2 100644 --- a/mysql-test/r/row.result +++ b/mysql-test/r/row.result @@ -457,3 +457,12 @@ abc 1 abc 1 select host,user from mysql.user where (host,user) = ('localhost','test'); host user drop table t1,t2; +# +# Bug#52124 memory leaks like a sieve in datetime, timestamp, time, date fields + warnings +# +CREATE TABLE t1 (a DATETIME NOT NULL, b TINYINT); +INSERT INTO t1 VALUES (0, 0),(0, 0); +SELECT 1 FROM t1 WHERE ROW(a, b) >= +ROW('1', (SELECT 1 FROM t1 WHERE a > 1234)); +1 +DROP TABLE t1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index d5f7eda5032..4390b70e9e9 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -3065,4 +3065,18 @@ select @plaintext; deallocate prepare encode; deallocate prepare decode; +--echo # +--echo # Bug#52124 memory leaks like a sieve in datetime, timestamp, time, date fields + warnings +--echo # +CREATE TABLE t1 (a DATETIME NOT NULL, b TINYINT); +INSERT INTO t1 VALUES (0, 0),(0, 0); +PREPARE stmt FROM "SELECT 1 FROM t1 WHERE +ROW(a, b) >= ROW('1', (SELECT 1 FROM t1 WHERE a > 1234))"; +--disable_warnings +EXECUTE stmt; +EXECUTE stmt; +--enable_warnings +DEALLOCATE PREPARE stmt; +DROP TABLE t1; + --echo End of 5.1 tests. diff --git a/mysql-test/t/row.test b/mysql-test/t/row.test index fcc4259168b..cec44078279 100644 --- a/mysql-test/t/row.test +++ b/mysql-test/t/row.test @@ -255,3 +255,14 @@ select * from t1,t2 where (a,b) = (c,d); select host,user from mysql.user where (host,user) = ('localhost','test'); drop table t1,t2; + +--echo # +--echo # Bug#52124 memory leaks like a sieve in datetime, timestamp, time, date fields + warnings +--echo # +CREATE TABLE t1 (a DATETIME NOT NULL, b TINYINT); +INSERT INTO t1 VALUES (0, 0),(0, 0); +--disable_warnings +SELECT 1 FROM t1 WHERE ROW(a, b) >= +ROW('1', (SELECT 1 FROM t1 WHERE a > 1234)); +--enable_warnings +DROP TABLE t1; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index f7d222a47d1..4eb27988984 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -54,9 +54,9 @@ public: /* Allow owner function to use string buffers. */ String value1, value2; - Arg_comparator(): thd(0), a_cache(0), b_cache(0), set_null(TRUE), + Arg_comparator(): comparators(0), thd(0), a_cache(0), b_cache(0), set_null(TRUE), get_value_a_func(0), get_value_b_func(0) {}; - Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), thd(0), + Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), comparators(0), thd(0), a_cache(0), b_cache(0), set_null(TRUE), get_value_a_func(0), get_value_b_func(0) {}; @@ -112,6 +112,11 @@ public: return (owner->type() == Item::FUNC_ITEM && ((Item_func*)owner)->functype() == Item_func::EQUAL_FUNC); } + void cleanup() + { + delete [] comparators; + comparators= 0; + } friend class Item_func; }; @@ -365,6 +370,11 @@ public: CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; } uint decimal_precision() const { return 1; } void top_level_item() { abort_on_null= TRUE; } + void cleanup() + { + Item_int_func::cleanup(); + cmp.cleanup(); + } friend class Arg_comparator; }; From 9743819d361bc4caff7db26041854b737f497d91 Mon Sep 17 00:00:00 2001 From: Staale Smedseng Date: Fri, 16 Apr 2010 15:02:23 +0200 Subject: [PATCH 20/29] Bug#51591 deadlock in the plugins+status+variables Potential deadlock situation involving LOCK_plugin, LOCK_global_system_variables and LOCK_status. This patch backports the fix from next-mr, unlocking LOCK_plugin before calling plugin->init() and add_status_vars(). --- sql/sql_plugin.cc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 0706ef24881..87c0adc3133 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1006,9 +1006,14 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) static int plugin_initialize(struct st_plugin_int *plugin) { + int ret= 1; DBUG_ENTER("plugin_initialize"); safe_mutex_assert_owner(&LOCK_plugin); + uint state= plugin->state; + DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED); + + pthread_mutex_unlock(&LOCK_plugin); if (plugin_type_initialize[plugin->plugin->type]) { if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) @@ -1027,8 +1032,7 @@ static int plugin_initialize(struct st_plugin_int *plugin) goto err; } } - - plugin->state= PLUGIN_IS_READY; + state= PLUGIN_IS_READY; // plugin->init() succeeded if (plugin->plugin->status_vars) { @@ -1047,7 +1051,8 @@ static int plugin_initialize(struct st_plugin_int *plugin) if (add_status_vars(array)) // add_status_vars makes a copy goto err; #else - add_status_vars(plugin->plugin->status_vars); // add_status_vars makes a copy + if (add_status_vars(plugin->plugin->status_vars)) + goto err; #endif /* FIX_LATER */ } @@ -1067,9 +1072,12 @@ static int plugin_initialize(struct st_plugin_int *plugin) } } - DBUG_RETURN(0); + ret= 0; + err: - DBUG_RETURN(1); + pthread_mutex_lock(&LOCK_plugin); + plugin->state= state; + DBUG_RETURN(ret); } From 794a441317fa88b5561eb26224dfc1b454097a45 Mon Sep 17 00:00:00 2001 From: Kristofer Pettersson Date: Fri, 16 Apr 2010 16:10:47 +0200 Subject: [PATCH 21/29] Bug#50373 --secure-file-priv="" The server variable opt_secure_file_priv wasn't normalized properly and caused the operations LOAD DATA INFILE .. INTO TABLE .. and SELECT load_file(..) to do different interpretations of the --secure-file-priv option. The patch moves code to the server initialization routines so that the path always is normalized once and only once. It was also intended that setting the option to an empty string should be equal to lifting all previously set restrictions. This is also fixed by this patch. sql/mysqld.cc: * If --secure_file_option is an empty string then the option variable should be unset. * opt_secure_file_option should be normalized once when the server starts. sql/sql_load.cc: * moved variable normalization code to fix_paths() --- .../suite/sys_vars/r/secure_file_priv.result | 22 +++++++++++++++++++ .../sys_vars/t/secure_file_priv-master.opt | 1 + .../suite/sys_vars/t/secure_file_priv.test | 21 ++++++++++++++++++ sql/mysqld.cc | 21 +++++++++++++++--- sql/sql_load.cc | 4 +--- 5 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 mysql-test/suite/sys_vars/r/secure_file_priv.result create mode 100644 mysql-test/suite/sys_vars/t/secure_file_priv-master.opt create mode 100644 mysql-test/suite/sys_vars/t/secure_file_priv.test diff --git a/mysql-test/suite/sys_vars/r/secure_file_priv.result b/mysql-test/suite/sys_vars/r/secure_file_priv.result new file mode 100644 index 00000000000..eeeb9a58c0f --- /dev/null +++ b/mysql-test/suite/sys_vars/r/secure_file_priv.result @@ -0,0 +1,22 @@ +# +# Bug50373 --secure-file-priv="" +# +CREATE TABLE t1 (c1 VARCHAR(50)); +INSERT INTO t1 VALUES ("one"),("two"),("three"),("four"),("five"); +SHOW VARIABLES LIKE 'secure_file_priv'; +Variable_name Value +secure_file_priv +c1 +one +two +three +four +five +loaded_file +one +two +three +four +five + +DROP TABLE t1; diff --git a/mysql-test/suite/sys_vars/t/secure_file_priv-master.opt b/mysql-test/suite/sys_vars/t/secure_file_priv-master.opt new file mode 100644 index 00000000000..b41d9b04b96 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/secure_file_priv-master.opt @@ -0,0 +1 @@ +--secure_file_priv='' diff --git a/mysql-test/suite/sys_vars/t/secure_file_priv.test b/mysql-test/suite/sys_vars/t/secure_file_priv.test new file mode 100644 index 00000000000..7a534e7d6e4 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/secure_file_priv.test @@ -0,0 +1,21 @@ +--echo # +--echo # Bug50373 --secure-file-priv="" +--echo # +CREATE TABLE t1 (c1 VARCHAR(50)); +INSERT INTO t1 VALUES ("one"),("two"),("three"),("four"),("five"); +SHOW VARIABLES LIKE 'secure_file_priv'; +--disable_query_log +# Atempt to create a file where we normally aren't allowed to create one. +# Doing this in a portable manner is difficult but we should be able to +# count on the depth of the directory hierarchy used. Three steps up from +# the datadir is the 'mysql_test' directory. +--let $PROTECTED_FILE=`SELECT concat(@@datadir,'/../../../bug50373.txt')`; +--eval SELECT * FROM t1 INTO OUTFILE '$PROTECTED_FILE'; +DELETE FROM t1; +--eval LOAD DATA INFILE '$PROTECTED_FILE' INTO TABLE t1; +SELECT * FROM t1; +--eval SELECT load_file('$PROTECTED_FILE') AS loaded_file; +--enable_query_log +remove_file $PROTECTED_FILE; +DROP TABLE t1; + diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a483b9e2381..9b66bdbcdf5 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -8818,10 +8818,25 @@ static int fix_paths(void) */ if (opt_secure_file_priv) { - convert_dirname(buff, opt_secure_file_priv, NullS); - my_free(opt_secure_file_priv, MYF(0)); - opt_secure_file_priv= my_strdup(buff, MYF(MY_FAE)); + if (*opt_secure_file_priv == 0) + { + opt_secure_file_priv= 0; + } + else + { + convert_dirname(buff, opt_secure_file_priv, NullS); + char *secure_file_real_path= (char *)my_malloc(FN_REFLEN, MYF(MY_FAE)); + if (secure_file_real_path == 0 || + my_realpath(secure_file_real_path, opt_secure_file_priv, 0)) + { + sql_print_warning("Failed to normalize the argument for --secure-file-priv."); + return 1; + } + my_free(opt_secure_file_priv, MYF(0)); + opt_secure_file_priv= secure_file_real_path; + } } + return 0; } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index ee3b442c83a..869a52325ea 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -350,9 +350,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } else if (opt_secure_file_priv) { - char secure_file_real_path[FN_REFLEN]; - (void) my_realpath(secure_file_real_path, opt_secure_file_priv, 0); - if (strncmp(secure_file_real_path, name, strlen(secure_file_real_path))) + if (strncmp(opt_secure_file_priv, name, strlen(opt_secure_file_priv))) { /* Read only allowed from within dir specified by secure_file_priv */ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); From 8b16a2a55b841e13af7bdac5be5f94bb3a4fe9f3 Mon Sep 17 00:00:00 2001 From: Staale Smedseng Date: Mon, 19 Apr 2010 15:48:37 +0200 Subject: [PATCH 22/29] Bug#51591 deadlock in the plugins+status+variables Unlocking/locking of LOCK_plugin in ha_ndbcluster.cc not needed anymore (but missing from the initial patch). --- sql/ha_ndbcluster.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 9fdb8b628ca..9c26105546d 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -7316,13 +7316,6 @@ static int ndbcluster_init(void *p) if (ndbcluster_inited) DBUG_RETURN(FALSE); - /* - Below we create new THD's. They'll need LOCK_plugin, but it's taken now by - plugin initialization code. Release it to avoid deadlocks. It's safe, as - there're no threads that may concurrently access plugin control structures. - */ - pthread_mutex_unlock(&LOCK_plugin); - pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST); pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_ndb_util_thread, NULL); @@ -7463,8 +7456,6 @@ static int ndbcluster_init(void *p) goto ndbcluster_init_error; } - pthread_mutex_lock(&LOCK_plugin); - ndbcluster_inited= 1; DBUG_RETURN(FALSE); @@ -7477,8 +7468,6 @@ ndbcluster_init_error: g_ndb_cluster_connection= NULL; ndbcluster_hton->state= SHOW_OPTION_DISABLED; // If we couldn't use handler - pthread_mutex_lock(&LOCK_plugin); - DBUG_RETURN(TRUE); } From 3a626d1027eb0348c954b0138c1f550cec86a424 Mon Sep 17 00:00:00 2001 From: Kristofer Pettersson Date: Tue, 20 Apr 2010 16:17:34 +0200 Subject: [PATCH 23/29] Bug#50373 --secure-file-priv="" Correcting a patch misstake. The converted file path is placed in 'buff' not in opt_secure_file_priv. mysql-test/r/loaddata.result: * Updated test case; Since secure_file_priv now is normalized the previous values are changed. sql/mysqld.cc: * Fixed patch misstake --- mysql-test/r/loaddata.result | 4 ++-- sql/mysqld.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) mode change 100755 => 100644 mysql-test/r/loaddata.result diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result old mode 100755 new mode 100644 index ef206565db5..8246cb40538 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -204,10 +204,10 @@ a b c 15 NULL Fifteen show variables like "secure_file_pri%"; Variable_name Value -secure_file_priv MYSQLTEST_VARDIR/ +secure_file_priv MYSQLTEST_VARDIR select @@secure_file_priv; @@secure_file_priv -MYSQLTEST_VARDIR/ +MYSQLTEST_VARDIR set @@secure_file_priv= 0; ERROR HY000: Variable 'secure_file_priv' is a read only variable truncate table t1; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 26dcc1753da..0d62c5953fc 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -8846,7 +8846,7 @@ static int fix_paths(void) convert_dirname(buff, opt_secure_file_priv, NullS); char *secure_file_real_path= (char *)my_malloc(FN_REFLEN, MYF(MY_FAE)); if (secure_file_real_path == 0 || - my_realpath(secure_file_real_path, opt_secure_file_priv, 0)) + my_realpath(secure_file_real_path, buff, 0)) { sql_print_warning("Failed to normalize the argument for --secure-file-priv."); return 1; From 20c9177518c3bb07c74df9fa8e25ba46af60f484 Mon Sep 17 00:00:00 2001 From: Staale Smedseng Date: Thu, 22 Apr 2010 15:52:00 +0200 Subject: [PATCH 24/29] Bug#46261 Plugins can be installed with --skip-grant-tables Previously installed dynamic plugins are explicitly not loaded on startup with --skip-grant-tables enabled. However, INSTALL PLUGIN/UNINSTALL PLUGIN commands are allowed, and result in inconsistent error messages (reporting duplicate plugin or plugin does not exist). This patch adds a check for --skip-grant-tables mode, and returns error ER_OPTION_PREVENTS_STATEMENT to the user when the above commands are attempted. --- mysql-test/r/bug46261.result | 8 ++++++++ mysql-test/t/bug46261-master.opt | 1 + mysql-test/t/bug46261.test | 16 ++++++++++++++++ sql/sql_plugin.cc | 12 ++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 mysql-test/r/bug46261.result create mode 100644 mysql-test/t/bug46261-master.opt create mode 100644 mysql-test/t/bug46261.test diff --git a/mysql-test/r/bug46261.result b/mysql-test/r/bug46261.result new file mode 100644 index 00000000000..f54b698e08f --- /dev/null +++ b/mysql-test/r/bug46261.result @@ -0,0 +1,8 @@ +# +# Bug#46261 Plugins can be installed with --skip-grant-tables +# +INSTALL PLUGIN example SONAME 'ha_example.so'; +ERROR HY000: The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement +UNINSTALL PLUGIN example; +ERROR HY000: The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement +End of 5.1 tests diff --git a/mysql-test/t/bug46261-master.opt b/mysql-test/t/bug46261-master.opt new file mode 100644 index 00000000000..6be4269e809 --- /dev/null +++ b/mysql-test/t/bug46261-master.opt @@ -0,0 +1 @@ +--skip-grant-tables $EXAMPLE_PLUGIN_OPT diff --git a/mysql-test/t/bug46261.test b/mysql-test/t/bug46261.test new file mode 100644 index 00000000000..67bdc995850 --- /dev/null +++ b/mysql-test/t/bug46261.test @@ -0,0 +1,16 @@ +--source include/not_embedded.inc +--source include/have_example_plugin.inc + +--echo # +--echo # Bug#46261 Plugins can be installed with --skip-grant-tables +--echo # + +--replace_regex /\.dll/.so/ +--error ER_OPTION_PREVENTS_STATEMENT +eval INSTALL PLUGIN example SONAME $HA_EXAMPLE_SO; + +--replace_regex /\.dll/.so/ +--error ER_OPTION_PREVENTS_STATEMENT +eval UNINSTALL PLUGIN example; + +--echo End of 5.1 tests diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 87c0adc3133..94a2b506207 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1665,6 +1665,12 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl struct st_plugin_int *tmp; DBUG_ENTER("mysql_install_plugin"); + if (opt_noacl) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); + DBUG_RETURN(TRUE); + } + bzero(&tables, sizeof(tables)); tables.db= (char *)"mysql"; tables.table_name= tables.alias= (char *)"plugin"; @@ -1741,6 +1747,12 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) struct st_plugin_int *plugin; DBUG_ENTER("mysql_uninstall_plugin"); + if (opt_noacl) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); + DBUG_RETURN(TRUE); + } + bzero(&tables, sizeof(tables)); tables.db= (char *)"mysql"; tables.table_name= tables.alias= (char *)"plugin"; From 6595861f5802dad8997fcaa6466d0ca1130d4df7 Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Sun, 25 Apr 2010 15:06:40 +0400 Subject: [PATCH 25/29] Fix for bug#50946: fast index creation still seems to copy the table Problem: ALTER TABLE ADD INDEX may lead to table copying if there's numeric field(s) with non-default display width modificator specified. Fix: compare numeric field's storage lenghts when we decide whether they can be considered 'equal' for table alteration purposes. mysql-test/r/error_simulation.result: Fix for bug#50946: fast index creation still seems to copy the table - test result. mysql-test/t/error_simulation.test: Fix for bug#50946: fast index creation still seems to copy the table - test case. sql/field.cc: Fix for bug#50946: fast index creation still seems to copy the table - check numeric field's pack lengths instead of it's display lenghts comparing fields equality for table alteration purposes. sql/sql_table.cc: Fix for bug#50946: fast index creation still seems to copy the table - check compare_tables() result for testing purposes. --- mysql-test/r/error_simulation.result | 23 +++++++++++++++++++++++ mysql-test/t/error_simulation.test | 16 ++++++++++++++++ sql/field.cc | 12 +++++++++--- sql/sql_table.cc | 7 +++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/error_simulation.result b/mysql-test/r/error_simulation.result index 6153dad2534..27e51a33112 100644 --- a/mysql-test/r/error_simulation.result +++ b/mysql-test/r/error_simulation.result @@ -18,3 +18,26 @@ SELECT MAX(a) FROM t1 GROUP BY a,b; ERROR 23000: Can't write; duplicate key in table 'tmp_table' set tmp_table_size=default; DROP TABLE t1; +# +# Bug #50946: fast index creation still seems to copy the table +# +CREATE TABLE t1 (a INT(100) NOT NULL); +INSERT INTO t1 VALUES (1), (0), (2); +SET SESSION debug='+d,alter_table_only_index_change'; +ALTER TABLE t1 ADD INDEX a(a); +SET SESSION debug=DEFAULT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(100) NOT NULL, + KEY `a` (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t1; +a +0 +1 +2 +DROP TABLE t1; +# +# End of 5.1 tests +# diff --git a/mysql-test/t/error_simulation.test b/mysql-test/t/error_simulation.test index f730c95208e..7cd16a6bc5a 100644 --- a/mysql-test/t/error_simulation.test +++ b/mysql-test/t/error_simulation.test @@ -33,3 +33,19 @@ set tmp_table_size=default; DROP TABLE t1; +--echo # +--echo # Bug #50946: fast index creation still seems to copy the table +--echo # +CREATE TABLE t1 (a INT(100) NOT NULL); +INSERT INTO t1 VALUES (1), (0), (2); +SET SESSION debug='+d,alter_table_only_index_change'; +ALTER TABLE t1 ADD INDEX a(a); +SET SESSION debug=DEFAULT; +SHOW CREATE TABLE t1; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # End of 5.1 tests +--echo # diff --git a/sql/field.cc b/sql/field.cc index b6323d7b839..b203a42a918 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8878,14 +8878,20 @@ bool Field_num::eq_def(Field *field) } +/** + Check whether two numeric fields can be considered 'equal' for table + alteration purposes. Fields are equal if they are of the same type + and retain the same pack length. +*/ + uint Field_num::is_equal(Create_field *new_field) { return ((new_field->sql_type == real_type()) && - ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & - UNSIGNED_FLAG)) && + ((new_field->flags & UNSIGNED_FLAG) == + (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == (uint) (flags & AUTO_INCREMENT_FLAG)) && - (new_field->length <= max_display_length())); + (new_field->pack_length == pack_length())); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ad72cab664e..1101be67d5e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6922,6 +6922,13 @@ view_err: &candidate_key_count)) goto err; + DBUG_EXECUTE_IF("alter_table_only_metadata_change", { + if (need_copy_table_res != ALTER_TABLE_METADATA_ONLY) + goto err; }); + DBUG_EXECUTE_IF("alter_table_only_index_change", { + if (need_copy_table_res != ALTER_TABLE_INDEX_CHANGED) + goto err; }); + if (need_copy_table == ALTER_TABLE_METADATA_ONLY) need_copy_table= need_copy_table_res; } From 6d43510a421cd450d8c43224f38a17b4a59ec556 Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Tue, 27 Apr 2010 00:06:00 +0400 Subject: [PATCH 26/29] Backport of the fix for bug #50335 to 5.0. The problem was in an incorrect debug assertion. The expression used in the failing assertion states that when finding references matching ORDER BY expressions, there can be only one reference to a single table. But that does not make any sense, all test cases for this bug are valid examples with multiple identical WHERE expressions referencing the same table which are also present in the ORDER BY list. Fixed by removing the failing assertion. We also have to take care of the 'found' counter so that we count multiple references only once. We rely on this fact later in eq_ref_table(). mysql-test/r/join.result: Added a test case for bug #50335. mysql-test/t/join.test: Added a test case for bug #50335. sql/sql_select.cc: Removing the assertion in eq_ref_table() as it does not make any sense. We also have to take care of the 'found' counter so that we count multiple references only once. We rely on this fact later in eq_ref_table(). --- mysql-test/r/join.result | 10 ++++++++++ mysql-test/t/join.test | 11 +++++++++++ sql/sql_select.cc | 8 +++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index 2585bf7a26e..beba47cd39b 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -942,4 +942,14 @@ ROW(t1.b, 1111.11) <=> ROW('',''); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables DROP TABLE t1; +# +# Bug #50335: Assertion `!(order->used & map)' in eq_ref_table +# +CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)); +INSERT INTO t1 VALUES (0,0), (1,1); +SELECT * FROM t1 STRAIGHT_JOIN t1 t2 ON t1.a=t2.a AND t1.a=t2.b ORDER BY t2.a, t1.a; +a b a b +0 0 0 0 +1 1 1 1 +DROP TABLE t1; End of 5.0 tests. diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index a9900c34f1e..d51740bb380 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -736,4 +736,15 @@ WHERE ROW(t1.a, 1111.11) = ROW(1111.11, 1111.11) AND ROW(t1.b, 1111.11) <=> ROW('',''); DROP TABLE t1; +--echo # +--echo # Bug #50335: Assertion `!(order->used & map)' in eq_ref_table +--echo # + +CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)); +INSERT INTO t1 VALUES (0,0), (1,1); + +SELECT * FROM t1 STRAIGHT_JOIN t1 t2 ON t1.a=t2.a AND t1.a=t2.b ORDER BY t2.a, t1.a; + +DROP TABLE t1; + --echo End of 5.0 tests. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 84de1fe241b..f0177893840 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6720,9 +6720,11 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab) } if (order) { - found++; - DBUG_ASSERT(!(order->used & map)); - order->used|=map; + if (!(order->used & map)) + { + found++; + order->used|= map; + } continue; // Used in ORDER BY } if (!only_eq_ref_tables(join,start_order, (*ref_item)->used_tables())) From f72dcc9be8ef88e6cda750e8bbf0e9ec7da112be Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 26 Apr 2010 23:59:50 +0200 Subject: [PATCH 27/29] Bug #48042 The description for Event ID ( 100 ) in Source ( MySQL ) cannot be found. The problem is that message resource (message.rc) is compiled as part of static library sql.lib rather than with executable mysqld.exe. resource files do not work in static libraries. The fix is to add message.rc to mysqld.exe source files list. --- sql/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 7f6074c903c..1f4c4034e8c 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -49,7 +49,7 @@ SET (SQL_SOURCE hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc - key.cc log.cc lock.cc message.rc + key.cc log.cc lock.cc log_event.cc rpl_record.cc rpl_reporting.cc log_event_old.cc rpl_record_old.cc message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c @@ -89,7 +89,7 @@ ADD_LIBRARY(sql ${SQL_SOURCE}) IF (NOT EXISTS cmake_dummy.cc) FILE (WRITE cmake_dummy.cc "") ENDIF (NOT EXISTS cmake_dummy.cc) -ADD_EXECUTABLE(mysqld cmake_dummy.cc) +ADD_EXECUTABLE(mysqld cmake_dummy.cc message.rc) SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) From 4d0e9957acdddf7a07e095ef2e8f53a2cb99b24b Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 28 Apr 2010 15:55:54 +0300 Subject: [PATCH 28/29] Bug #47453: InnoDB incorrectly changes TIMESTAMP columns when JOINed during an UPDATE Extended the fix for bug 29310 to multi-table update: When a table is being updated it has two set of fields - fields required for checks of conditions and fields to be updated. A storage engine is allowed not to retrieve columns marked for update. Due to this fact records can't be compared to see whether the data has been changed or not. This makes the server always update records independently of data change. Now when an auto-updatable timestamp field is present and server sees that a table handle isn't going to retrieve write-only fields then all of such fields are marked as to be read to force the handler to retrieve them. --- mysql-test/r/innodb_mysql.result | 28 ++++++++++++++++++++++++++++ mysql-test/t/innodb_mysql.test | 30 ++++++++++++++++++++++++++++++ sql/sql_update.cc | 10 ++++++++++ 3 files changed, 68 insertions(+) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 0e691611f02..2bf1ef8fbf0 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -2350,4 +2350,32 @@ Null Index_type BTREE Comment DROP TABLE t1; +# +# Bug #47453: InnoDB incorrectly changes TIMESTAMP columns when +# JOINed during an UPDATE +# +CREATE TABLE t1 (d INT) ENGINE=InnoDB; +CREATE TABLE t2 (a INT, b INT, +c TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB; +set up our data elements +INSERT INTO t1 (d) VALUES (1); +INSERT INTO t2 (a,b) VALUES (1,1); +SELECT SECOND(c) INTO @bug47453 FROM t2; +SELECT SECOND(c)-@bug47453 FROM t1 JOIN t2 ON d=a; +SECOND(c)-@bug47453 +0 +UPDATE t1 JOIN t2 ON d=a SET b=1 WHERE a=1; +SELECT SECOND(c)-@bug47453 FROM t1 JOIN t2 ON d=a; +SECOND(c)-@bug47453 +0 +SELECT SLEEP(1); +SLEEP(1) +0 +UPDATE t1 JOIN t2 ON d=a SET b=1 WHERE a=1; +#should be 0 +SELECT SECOND(c)-@bug47453 FROM t1 JOIN t2 ON d=a; +SECOND(c)-@bug47453 +0 +DROP TABLE t1, t2; End of 5.1 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 88ca8d42e3d..9564d3b41fb 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -589,4 +589,34 @@ ALTER TABLE t1 DROP INDEX k, ADD UNIQUE INDEX k (a,b); DROP TABLE t1; + +--echo # +--echo # Bug #47453: InnoDB incorrectly changes TIMESTAMP columns when +--echo # JOINed during an UPDATE +--echo # + +CREATE TABLE t1 (d INT) ENGINE=InnoDB; +CREATE TABLE t2 (a INT, b INT, + c TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB; + +--echo set up our data elements +INSERT INTO t1 (d) VALUES (1); +INSERT INTO t2 (a,b) VALUES (1,1); +SELECT SECOND(c) INTO @bug47453 FROM t2; + +SELECT SECOND(c)-@bug47453 FROM t1 JOIN t2 ON d=a; +UPDATE t1 JOIN t2 ON d=a SET b=1 WHERE a=1; +SELECT SECOND(c)-@bug47453 FROM t1 JOIN t2 ON d=a; + +SELECT SLEEP(1); + +UPDATE t1 JOIN t2 ON d=a SET b=1 WHERE a=1; + +--echo #should be 0 +SELECT SECOND(c)-@bug47453 FROM t1 JOIN t2 ON d=a; + +DROP TABLE t1, t2; + + --echo End of 5.1 tests diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 63af275cef3..d8141deba63 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1379,6 +1379,16 @@ int multi_update::prepare(List ¬_used_values, { table->read_set= &table->def_read_set; bitmap_union(table->read_set, &table->tmp_set); + /* + If a timestamp field settable on UPDATE is present then to avoid wrong + update force the table handler to retrieve write-only fields to be able + to compare records and detect data change. + */ + if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ && + table->timestamp_field && + (table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_UPDATE || + table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH)) + bitmap_union(table->read_set, table->write_set); } } From 0d5dbb166b1bcd39588268d4148f02c9f58575f8 Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Thu, 29 Apr 2010 08:42:32 +0400 Subject: [PATCH 29/29] Fix for bug #53237: mysql_list_fields/COM_FIELD_LIST stack smashing Problem: "COM_FIELD_LIST is an old command of the MySQL server, before there was real move to only SQL. Seems that the data sent to COM_FIELD_LIST( mysql_list_fields() function) is not checked for sanity. By sending long data for the table a buffer is overflown, which can be used deliberately to include code that harms". Fix: check incoming data length. sql/sql_parse.cc: Fix for bug #53237: mysql_list_fields/COM_FIELD_LIST stack smashing - check incoming mysql_list_fields() table name arg length. --- sql/sql_parse.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 64d6888d772..f1fb3d646b5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2025,8 +2025,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (thd->copy_db_to(&table_list.db, &table_list.db_length)) break; pend= strend(packet); + uint arg_length= pend - packet; + + /* Check given table name length. */ + if (arg_length >= packet_length || arg_length > NAME_LEN) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + break; + } thd->convert_string(&conv_name, system_charset_info, - packet, (uint) (pend-packet), thd->charset()); + packet, arg_length, thd->charset()); table_list.alias= table_list.table_name= conv_name.str; packet= pend+1;