From 654f0e96035ab3f0b6f4293cc8a1e47b38dc6a56 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 19 Nov 2010 15:45:18 +0200 Subject: [PATCH 01/52] Fix Bug#58279 Incorrect enabling of UNIV_DEBUG in debug builds Checking "IF(WITH_DEBUG)" does not work for multi-configuration CMake generators like VS or XCode. See http://forge.mysql.com/wiki/CMake#Debug-only_options --- storage/innobase/CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index fa45cc96687..7da2bc22e39 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -40,10 +40,9 @@ IF(UNIX) ENDIF() ENDIF() -# Enable InnoDB's UNIV_DEBUG if MySQL's WITH_DEBUG is defined -IF(WITH_DEBUG) - ADD_DEFINITIONS("-DUNIV_DEBUG") -ENDIF() +# Enable InnoDB's UNIV_DEBUG for debug builds +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DUNIV_DEBUG") +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DUNIV_DEBUG") IF(NOT MSVC) # either define HAVE_IB_GCC_ATOMIC_BUILTINS or not From 11948ba7980b75afbacc9210020cdf17f80f170d Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 22 Nov 2010 12:27:04 +0200 Subject: [PATCH 02/52] Add -innodb specific files for PB2 tests mysql-5.1-innodb.push and mysql-5.5-innodb.push are forked from default.push and mysql-trunk-innodb.push from mysql-trunk.push --- mysql-test/collections/mysql-5.1-innodb.push | 5 +++++ mysql-test/collections/mysql-5.5-innodb.push | 5 +++++ mysql-test/collections/mysql-trunk-innodb.push | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 mysql-test/collections/mysql-5.1-innodb.push create mode 100644 mysql-test/collections/mysql-5.5-innodb.push create mode 100644 mysql-test/collections/mysql-trunk-innodb.push diff --git a/mysql-test/collections/mysql-5.1-innodb.push b/mysql-test/collections/mysql-5.1-innodb.push new file mode 100644 index 00000000000..a213706498e --- /dev/null +++ b/mysql-test/collections/mysql-5.1-innodb.push @@ -0,0 +1,5 @@ +perl mysql-test-run.pl --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --comment=embedded --vardir=var-emebbed --embedded --experimental=collections/default.experimental --skip-ndb +perl mysql-test-run.pl --timer --force --parallel=auto --comment=rpl_binlog_row --vardir=var-rpl_binlog_row --suite=rpl,binlog --mysqld=--binlog-format=row --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 --experimental=collections/default.experimental --skip-ndb diff --git a/mysql-test/collections/mysql-5.5-innodb.push b/mysql-test/collections/mysql-5.5-innodb.push new file mode 100644 index 00000000000..a213706498e --- /dev/null +++ b/mysql-test/collections/mysql-5.5-innodb.push @@ -0,0 +1,5 @@ +perl mysql-test-run.pl --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --comment=embedded --vardir=var-emebbed --embedded --experimental=collections/default.experimental --skip-ndb +perl mysql-test-run.pl --timer --force --parallel=auto --comment=rpl_binlog_row --vardir=var-rpl_binlog_row --suite=rpl,binlog --mysqld=--binlog-format=row --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 --experimental=collections/default.experimental --skip-ndb diff --git a/mysql-test/collections/mysql-trunk-innodb.push b/mysql-test/collections/mysql-trunk-innodb.push new file mode 100644 index 00000000000..b87cc4b801f --- /dev/null +++ b/mysql-test/collections/mysql-trunk-innodb.push @@ -0,0 +1,5 @@ +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --suite=main,binlog,innodb,federated,rpl,sys_vars,perfschema --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --suite=main,binlog,innodb,federated,rpl,sys_vars,perfschema --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=embedded --vardir=var-emebbed --embedded --suite=main,binlog,innodb,federated,rpl,sys_vars,perfschema +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=rpl_binlog_row --vardir=var-rpl_binlog_row --mysqld=--binlog-format=row --suite=rpl,binlog --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 From 250d851129f8429a303faeb307fb09cd68c84cea Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 22 Nov 2010 12:29:44 +0200 Subject: [PATCH 03/52] Do not run federated tests for mysql-trunk-innodb --- mysql-test/collections/mysql-trunk-innodb.push | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/collections/mysql-trunk-innodb.push b/mysql-test/collections/mysql-trunk-innodb.push index b87cc4b801f..df00743c6d8 100644 --- a/mysql-test/collections/mysql-trunk-innodb.push +++ b/mysql-test/collections/mysql-trunk-innodb.push @@ -1,5 +1,5 @@ -perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --suite=main,binlog,innodb,federated,rpl,sys_vars,perfschema --skip-test-list=collections/disabled-per-push.list -perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --suite=main,binlog,innodb,federated,rpl,sys_vars,perfschema --skip-test-list=collections/disabled-per-push.list -perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=embedded --vardir=var-emebbed --embedded --suite=main,binlog,innodb,federated,rpl,sys_vars,perfschema +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --suite=main,binlog,innodb,rpl,sys_vars,perfschema --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --suite=main,binlog,innodb,rpl,sys_vars,perfschema --skip-test-list=collections/disabled-per-push.list +perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=embedded --vardir=var-emebbed --embedded --suite=main,binlog,innodb,rpl,sys_vars,perfschema perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=rpl_binlog_row --vardir=var-rpl_binlog_row --mysqld=--binlog-format=row --suite=rpl,binlog --skip-test-list=collections/disabled-per-push.list perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 From d5787a3c59694c36098690cbbe14e874d7ab3e67 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 22 Nov 2010 16:08:51 +0200 Subject: [PATCH 04/52] Fix Bug#57739 Scary messages in error log Silence a warning about old table name when InnoDB tests whether the format has changed using a nonexistent table name. Reviewed by: bar@mysql.com, marko.makela@oracle.com --- include/mysql/innodb_priv.h | 1 - sql/sql_table.cc | 14 ++++++++++++-- sql/sql_table.h | 6 +++++- storage/innobase/handler/ha_innodb.cc | 6 +++--- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/include/mysql/innodb_priv.h b/include/mysql/innodb_priv.h index 993dad7cf99..5406c292b18 100644 --- a/include/mysql/innodb_priv.h +++ b/include/mysql/innodb_priv.h @@ -22,7 +22,6 @@ class THD; -uint filename_to_tablename(const char *from, char *to, uint to_length); int get_quote_char_for_identifier(THD *thd, const char *name, uint length); bool schema_table_store_record(THD *thd, TABLE *table); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 772496a10d5..064c2b36e17 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -371,7 +371,11 @@ uint explain_filename(THD* thd, Table name length. */ -uint filename_to_tablename(const char *from, char *to, uint to_length) +uint filename_to_tablename(const char *from, char *to, uint to_length +#ifndef DBUG_OFF + , bool stay_quiet +#endif /* DBUG_OFF */ + ) { uint errors; size_t res; @@ -391,7 +395,13 @@ uint filename_to_tablename(const char *from, char *to, uint to_length) { res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) - to); - sql_print_error("Invalid (old?) table or database name '%s'", from); +#ifndef DBUG_OFF + if (!stay_quiet) { +#endif /* DBUG_OFF */ + sql_print_error("Invalid (old?) table or database name '%s'", from); +#ifndef DBUG_OFF + } +#endif /* DBUG_OFF */ /* TODO: add a stored procedure for fix table and database names, and mention its name in error log. diff --git a/sql/sql_table.h b/sql/sql_table.h index aa5738fd4c9..2924301e6b3 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -123,7 +123,11 @@ enum enum_explain_filename_mode #define NO_FRM_RENAME (1 << 2) #define FRM_ONLY (1 << 3) -uint filename_to_tablename(const char *from, char *to, uint to_length); +uint filename_to_tablename(const char *from, char *to, uint to_length +#ifndef DBUG_OFF + , bool stay_quiet = false +#endif /* DBUG_OFF */ + ); uint tablename_to_filename(const char *from, char *to, uint to_length); uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length); bool check_mysql50_prefix(const char *name); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index b0d620c060d..c9072b4c63e 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2164,13 +2164,13 @@ innobase_init( ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR); -#ifdef UNIV_DEBUG +#ifndef DBUG_OFF static const char test_filename[] = "-@"; char test_tablename[sizeof test_filename + sizeof srv_mysql50_table_name_prefix]; if ((sizeof test_tablename) - 1 != filename_to_tablename(test_filename, test_tablename, - sizeof test_tablename) + sizeof test_tablename, true) || strncmp(test_tablename, srv_mysql50_table_name_prefix, sizeof srv_mysql50_table_name_prefix) @@ -2180,7 +2180,7 @@ innobase_init( sql_print_error("tablename encoding has been changed"); goto error; } -#endif /* UNIV_DEBUG */ +#endif /* DBUG_OFF */ /* Check that values don't overflow on 32-bit systems. */ if (sizeof(ulint) == 4) { From 61c2b12da7eaaa0f586025fb4613195ca03d4bb1 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Wed, 24 Nov 2010 14:07:43 +1100 Subject: [PATCH 05/52] Fix bug# 18274 InnoDB auto_increment field reset on OPTIMIZE TABLE OPTIMIZE TABLE recreates the whole table. That is why the counter gets reset. Making the next autoinc column persistent is a separate issue from resetting the value after an OPTIMIZE TABLE. We already have a check for ALTER TABLE and CREATE INDEX to preserve the value on table recreate. We should be able to add an additional check for OPTIMIZE TABLE to preserve the next value. rb://519 Approved by Jimmy Yang. --- .../r/innodb-autoinc-18274.result | 26 +++++++++++++++++ .../innodb_plugin/t/innodb-autoinc-18274.test | 29 +++++++++++++++++++ storage/innodb_plugin/handler/ha_innodb.cc | 24 ++++++++------- 3 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 mysql-test/suite/innodb_plugin/r/innodb-autoinc-18274.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb-autoinc-18274.test diff --git a/mysql-test/suite/innodb_plugin/r/innodb-autoinc-18274.result b/mysql-test/suite/innodb_plugin/r/innodb-autoinc-18274.result new file mode 100644 index 00000000000..22afc65a649 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb-autoinc-18274.result @@ -0,0 +1,26 @@ +drop table if exists t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (null); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`c1`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 +DELETE FROM t1; +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize note Table does not support optimize, doing recreate + analyze instead +test.t1 optimize status OK +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`c1`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES(null); +SELECT * FROM t1; +c1 +2 +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/t/innodb-autoinc-18274.test b/mysql-test/suite/innodb_plugin/t/innodb-autoinc-18274.test new file mode 100644 index 00000000000..8734311dd7a --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb-autoinc-18274.test @@ -0,0 +1,29 @@ +-- source include/have_innodb_plugin.inc +# embedded server ignores 'delayed', so skip this +-- source include/not_embedded.inc + +let $innodb_file_format_check_orig=`select @@innodb_file_format_check`; + +--disable_warnings +drop table if exists t1; +--enable_warnings + +# +# Bug #18274 InnoDB auto_increment field reset on OPTIMIZE TABLE +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (null); +SHOW CREATE TABLE t1; +DELETE FROM t1; +OPTIMIZE TABLE t1; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES(null); +SELECT * FROM t1; +DROP TABLE t1; + +# +# restore environment to the state it was before this test execution +# + +-- disable_query_log +eval set global innodb_file_format_check=$innodb_file_format_check_orig; diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 5965bd0e59e..cffe1e8459e 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -6808,23 +6808,25 @@ ha_innobase::create( setup at this stage and so we use thd. */ /* We need to copy the AUTOINC value from the old table if - this is an ALTER TABLE or CREATE INDEX because CREATE INDEX - does a table copy too. */ + this is an ALTER|OPTIMIZE TABLE or CREATE INDEX because CREATE INDEX + does a table copy too. If query was one of : + + CREATE TABLE ...AUTO_INCREMENT = x; or + ALTER TABLE...AUTO_INCREMENT = x; or + OPTIMIZE TABLE t; or + CREATE INDEX x on t(...); + + Find out a table definition from the dictionary and get + the current value of the auto increment field. Set a new + value to the auto increment field if the value is greater + than the maximum value in the column. */ if (((create_info->used_fields & HA_CREATE_USED_AUTO) || thd_sql_command(thd) == SQLCOM_ALTER_TABLE + || thd_sql_command(thd) == SQLCOM_OPTIMIZE || thd_sql_command(thd) == SQLCOM_CREATE_INDEX) && create_info->auto_increment_value > 0) { - /* Query was one of : - CREATE TABLE ...AUTO_INCREMENT = x; or - ALTER TABLE...AUTO_INCREMENT = x; or - CREATE INDEX x on t(...); - Find out a table definition from the dictionary and get - the current value of the auto increment field. Set a new - value to the auto increment field if the value is greater - than the maximum value in the column. */ - auto_inc_value = create_info->auto_increment_value; dict_table_autoinc_lock(innobase_table); From e13bde85b5658d0236c6a701add861c088ca7584 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 25 Nov 2010 08:55:47 +0200 Subject: [PATCH 06/52] Bug#47350 Support innodb plugin without separate shared object Add a _commented_ workaround for Bug#47350. The full solution is tricky to get right as explained in the bug report. It is not worth the effort to extend the deprecated autotools framework to support conflicting plugins and would be too risky for MySQL 5.1 (GA). --- storage/innodb_plugin/plug.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/storage/innodb_plugin/plug.in b/storage/innodb_plugin/plug.in index 2ee45389e9c..b2af11295bc 100644 --- a/storage/innodb_plugin/plug.in +++ b/storage/innodb_plugin/plug.in @@ -17,6 +17,9 @@ MYSQL_STORAGE_ENGINE(innodb_plugin,, [InnoDB Storage Engine], [Transactional Tables using InnoDB], [max,max-no-ndb]) MYSQL_PLUGIN_DIRECTORY(innodb_plugin, [storage/innodb_plugin]) +# Enable if you know what you are doing (trying to link both InnoDB and +# InnoDB Plugin statically into MySQL does not work). +#MYSQL_PLUGIN_STATIC(innodb_plugin, [libinnobase.a]) MYSQL_PLUGIN_DYNAMIC(innodb_plugin, [ha_innodb_plugin.la]) MYSQL_PLUGIN_ACTIONS(innodb_plugin, [ AC_CHECK_HEADERS(sched.h) From e2e20a04dfaff2f71ef20a8213b9d892da16b52e Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 26 Nov 2010 10:54:12 +0200 Subject: [PATCH 07/52] Fix the PAUSE instruction handling in InnoDB Previously HAVE_IB_PAUSE_INSTRUCTION was never defined and thus InnoDB never used the PAUSE instruction on non-windows even if it was available. Probably the check was never migrated from autotools' storage/innobase/plug.in to storage/innobase/CMakeLists.txt. Since the check for PAUSE is done at top-level configure.cmake we can use the result from there (HAVE_PAUSE_INSTRUCTION) instead of rolling InnoDB's own HAVE_IB_PAUSE_INSTRUCTION (the check is identical anyway). --- storage/innobase/CMakeLists.txt | 2 +- storage/innobase/include/ut0ut.h | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 7da2bc22e39..4bbda0d2477 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -189,7 +189,7 @@ IF(SIZEOF_PTHREAD_T) ENDIF() IF(MSVC) - ADD_DEFINITIONS(-DHAVE_WINDOWS_ATOMICS -DHAVE_IB_PAUSE_INSTRUCTION) + ADD_DEFINITIONS(-DHAVE_WINDOWS_ATOMICS) ENDIF() diff --git a/storage/innobase/include/ut0ut.h b/storage/innobase/include/ut0ut.h index dd59b3eba46..5c7859a94f5 100644 --- a/storage/innobase/include/ut0ut.h +++ b/storage/innobase/include/ut0ut.h @@ -55,24 +55,22 @@ Created 1/20/1994 Heikki Tuuri typedef time_t ib_time_t; #ifndef UNIV_HOTBACKUP -#if defined(HAVE_IB_PAUSE_INSTRUCTION) -# ifdef WIN32 - /* In the Win32 API, the x86 PAUSE instruction is executed by calling - the YieldProcessor macro defined in WinNT.h. It is a CPU architecture- - independent way by using YieldProcessor.*/ -# define UT_RELAX_CPU() YieldProcessor() -# else - /* According to the gcc info page, asm volatile means that the - instruction has important side-effects and must not be removed. - Also asm volatile may trigger a memory barrier (spilling all registers - to memory). */ -# define UT_RELAX_CPU() __asm__ __volatile__ ("pause") -# endif +#if defined(HAVE_PAUSE_INSTRUCTION) + /* According to the gcc info page, asm volatile means that the + instruction has important side-effects and must not be removed. + Also asm volatile may trigger a memory barrier (spilling all registers + to memory). */ +# define UT_RELAX_CPU() __asm__ __volatile__ ("pause") #elif defined(HAVE_ATOMIC_BUILTINS) # define UT_RELAX_CPU() do { \ volatile lint volatile_var; \ os_compare_and_swap_lint(&volatile_var, 0, 1); \ } while (0) +#elif defined(HAVE_WINDOWS_ATOMICS) + /* In the Win32 API, the x86 PAUSE instruction is executed by calling + the YieldProcessor macro defined in WinNT.h. It is a CPU architecture- + independent way by using YieldProcessor. */ +# define UT_RELAX_CPU() YieldProcessor() #else # define UT_RELAX_CPU() ((void)0) /* avoid warning for an empty statement */ #endif From 15f8dc7ad74d1c86ec9d63feeda7d6433a90f7a2 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Sun, 28 Nov 2010 17:43:55 -0800 Subject: [PATCH 08/52] Fix Bug #58461 InnoDB should show aggregated result for multiple buffer pool instance. rb://526 approved by Sunny Bains --- storage/innobase/buf/buf0buf.c | 344 ++++++++++++++++++++++------- storage/innobase/include/buf0buf.h | 70 ++++++ 2 files changed, 336 insertions(+), 78 deletions(-) diff --git a/storage/innobase/buf/buf0buf.c b/storage/innobase/buf/buf0buf.c index b79b5634957..49858406101 100644 --- a/storage/innobase/buf/buf0buf.c +++ b/storage/innobase/buf/buf0buf.c @@ -4793,23 +4793,203 @@ buf_get_modified_ratio_pct(void) return(ratio); } +/*******************************************************************//** +Aggregates a pool stats information with the total buffer pool stats */ +static +void +buf_stats_aggregate_pool_info( +/*==========================*/ + buf_pool_info_t* total_info, /*!< in/out: the buffer pool + info to store aggregated + result */ + const buf_pool_info_t* pool_info) /*!< in: individual buffer pool + stats info */ +{ + ut_a(total_info && pool_info); + + /* Nothing to copy if total_info is the same as pool_info */ + if (total_info == pool_info) { + return; + } + + total_info->pool_size += pool_info->pool_size; + total_info->lru_len += pool_info->lru_len; + total_info->old_lru_len += pool_info->old_lru_len; + total_info->free_list_len += pool_info->free_list_len; + total_info->flush_list_len += pool_info->flush_list_len; + total_info->n_pend_unzip += pool_info->n_pend_unzip; + total_info->n_pend_reads += pool_info->n_pend_reads; + total_info->n_pending_flush_lru += pool_info->n_pending_flush_lru; + total_info->n_pending_flush_list += pool_info->n_pending_flush_list; + total_info->n_pending_flush_single_page += + pool_info->n_pending_flush_single_page; + total_info->n_pages_made_young += pool_info->n_pages_made_young; + total_info->n_pages_not_made_young += pool_info->n_pages_not_made_young; + total_info->n_pages_read += pool_info->n_pages_read; + total_info->n_pages_created += pool_info->n_pages_created; + total_info->n_pages_written += pool_info->n_pages_written; + total_info->n_page_gets += pool_info->n_page_gets; + total_info->n_ra_pages_read += pool_info->n_ra_pages_read; + total_info->n_ra_pages_evicted += pool_info->n_ra_pages_evicted; + total_info->page_made_young_rate += pool_info->page_made_young_rate; + total_info->page_not_made_young_rate += + pool_info->page_not_made_young_rate; + total_info->pages_read_rate += pool_info->pages_read_rate; + total_info->pages_created_rate += pool_info->pages_created_rate; + total_info->pages_written_rate += pool_info->pages_written_rate; + total_info->n_page_get_delta += pool_info->n_page_get_delta; + total_info->page_read_delta += pool_info->page_read_delta; + total_info->young_making_delta += pool_info->young_making_delta; + total_info->not_young_making_delta += pool_info->not_young_making_delta; + total_info->pages_readahead_rate += pool_info->pages_readahead_rate; + total_info->pages_evicted_rate += pool_info->pages_evicted_rate; + total_info->unzip_lru_len += pool_info->unzip_lru_len; + total_info->io_sum += pool_info->io_sum; + total_info->io_cur += pool_info->io_cur; + total_info->unzip_sum += pool_info->unzip_sum; + total_info->unzip_cur += pool_info->unzip_cur; +} +/*******************************************************************//** +Collect buffer pool stats information for a buffer pool. Also +record aggregated stats if there are more than one buffer pool +in the server */ +static +void +buf_stats_get_pool_info( +/*====================*/ + buf_pool_t* buf_pool, /*!< in: buffer pool */ + ulint pool_id, /*!< in: buffer pool ID */ + buf_pool_info_t* all_pool_info) /*!< in/out: buffer pool info + to fill */ +{ + buf_pool_info_t* pool_info; + time_t current_time; + double time_elapsed; + + /* Find appropriate pool_info to store stats for this buffer pool */ + pool_info = &all_pool_info[pool_id]; + + buf_pool_mutex_enter(buf_pool); + buf_flush_list_mutex_enter(buf_pool); + + pool_info->pool_unique_id = pool_id; + + pool_info->pool_size = buf_pool->curr_size; + + pool_info->lru_len = UT_LIST_GET_LEN(buf_pool->LRU); + + pool_info->old_lru_len = buf_pool->LRU_old_len; + + pool_info->free_list_len = UT_LIST_GET_LEN(buf_pool->free); + + pool_info->flush_list_len = UT_LIST_GET_LEN(buf_pool->flush_list); + + pool_info->n_pend_unzip = UT_LIST_GET_LEN(buf_pool->unzip_LRU); + + pool_info->n_pend_reads = buf_pool->n_pend_reads; + + pool_info->n_pending_flush_lru = + (buf_pool->n_flush[BUF_FLUSH_LRU] + + buf_pool->init_flush[BUF_FLUSH_LRU]); + + pool_info->n_pending_flush_list = + (buf_pool->n_flush[BUF_FLUSH_LIST] + + buf_pool->init_flush[BUF_FLUSH_LIST]); + + pool_info->n_pending_flush_single_page = + buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE]; + + buf_flush_list_mutex_exit(buf_pool); + + current_time = time(NULL); + time_elapsed = 0.001 + difftime(current_time, + buf_pool->last_printout_time); + + pool_info->n_pages_made_young = buf_pool->stat.n_pages_made_young; + + pool_info->n_pages_not_made_young = + buf_pool->stat.n_pages_not_made_young; + + pool_info->n_pages_read = buf_pool->stat.n_pages_read; + + pool_info->n_pages_created = buf_pool->stat.n_pages_created; + + pool_info->n_pages_written = buf_pool->stat.n_pages_written; + + pool_info->n_page_gets = buf_pool->stat.n_page_gets; + + pool_info->n_ra_pages_read = buf_pool->stat.n_ra_pages_read; + + pool_info->n_ra_pages_evicted = buf_pool->stat.n_ra_pages_evicted; + + pool_info->page_made_young_rate = + (buf_pool->stat.n_pages_made_young + - buf_pool->old_stat.n_pages_made_young) / time_elapsed; + + pool_info->page_not_made_young_rate = + (buf_pool->stat.n_pages_not_made_young + - buf_pool->old_stat.n_pages_not_made_young) / time_elapsed; + + pool_info->pages_read_rate = + (buf_pool->stat.n_pages_read + - buf_pool->old_stat.n_pages_read) / time_elapsed; + + pool_info->pages_created_rate = + (buf_pool->stat.n_pages_created + - buf_pool->old_stat.n_pages_created) / time_elapsed; + + pool_info->pages_written_rate = + (buf_pool->stat.n_pages_written + - buf_pool->old_stat.n_pages_written) / time_elapsed; + + pool_info->n_page_get_delta = buf_pool->stat.n_page_gets + - buf_pool->old_stat.n_page_gets; + + if (pool_info->n_page_get_delta) { + pool_info->page_read_delta = buf_pool->stat.n_pages_read + - buf_pool->old_stat.n_pages_read; + + pool_info->young_making_delta = + buf_pool->stat.n_pages_made_young + - buf_pool->old_stat.n_pages_made_young; + + pool_info->not_young_making_delta = + buf_pool->stat.n_pages_not_made_young + - buf_pool->old_stat.n_pages_not_made_young; + } + + pool_info->pages_readahead_rate = + (buf_pool->stat.n_ra_pages_read + - buf_pool->old_stat.n_ra_pages_read) / time_elapsed; + + pool_info->pages_evicted_rate = + (buf_pool->stat.n_ra_pages_evicted + - buf_pool->old_stat.n_ra_pages_evicted) / time_elapsed; + + pool_info->unzip_lru_len = UT_LIST_GET_LEN(buf_pool->unzip_LRU); + + pool_info->io_sum = buf_LRU_stat_sum.io; + + pool_info->io_cur = buf_LRU_stat_cur.io; + + pool_info->unzip_sum = buf_LRU_stat_sum.unzip; + + pool_info->unzip_cur = buf_LRU_stat_cur.unzip; + + buf_refresh_io_stats(buf_pool); + buf_pool_mutex_exit(buf_pool); +} + /*********************************************************************//** Prints info of the buffer i/o. */ UNIV_INTERN void buf_print_io_instance( /*==================*/ - buf_pool_t* buf_pool, /*!< in: buffer pool instance */ + buf_pool_info_t*pool_info, /*!< in: buffer pool info */ FILE* file) /*!< in/out: buffer where to print */ { - time_t current_time; - double time_elapsed; - ulint n_gets_diff; - - ut_ad(buf_pool); - - buf_pool_mutex_enter(buf_pool); - buf_flush_list_mutex_enter(buf_pool); + ut_ad(pool_info); fprintf(file, "Buffer pool size %lu\n" @@ -4819,70 +4999,42 @@ buf_print_io_instance( "Modified db pages %lu\n" "Pending reads %lu\n" "Pending writes: LRU %lu, flush list %lu, single page %lu\n", - (ulong) buf_pool->curr_size, - (ulong) UT_LIST_GET_LEN(buf_pool->free), - (ulong) UT_LIST_GET_LEN(buf_pool->LRU), - (ulong) buf_pool->LRU_old_len, - (ulong) UT_LIST_GET_LEN(buf_pool->flush_list), - (ulong) buf_pool->n_pend_reads, - (ulong) buf_pool->n_flush[BUF_FLUSH_LRU] - + buf_pool->init_flush[BUF_FLUSH_LRU], - (ulong) buf_pool->n_flush[BUF_FLUSH_LIST] - + buf_pool->init_flush[BUF_FLUSH_LIST], - (ulong) buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE]); - - buf_flush_list_mutex_exit(buf_pool); - - current_time = time(NULL); - time_elapsed = 0.001 + difftime(current_time, - buf_pool->last_printout_time); + pool_info->pool_size, + pool_info->free_list_len, + pool_info->lru_len, + pool_info->old_lru_len, + pool_info->flush_list_len, + pool_info->n_pend_reads, + pool_info->n_pending_flush_lru, + pool_info->n_pending_flush_list, + pool_info->n_pending_flush_single_page); fprintf(file, "Pages made young %lu, not young %lu\n" "%.2f youngs/s, %.2f non-youngs/s\n" "Pages read %lu, created %lu, written %lu\n" "%.2f reads/s, %.2f creates/s, %.2f writes/s\n", - (ulong) buf_pool->stat.n_pages_made_young, - (ulong) buf_pool->stat.n_pages_not_made_young, - (buf_pool->stat.n_pages_made_young - - buf_pool->old_stat.n_pages_made_young) - / time_elapsed, - (buf_pool->stat.n_pages_not_made_young - - buf_pool->old_stat.n_pages_not_made_young) - / time_elapsed, - (ulong) buf_pool->stat.n_pages_read, - (ulong) buf_pool->stat.n_pages_created, - (ulong) buf_pool->stat.n_pages_written, - (buf_pool->stat.n_pages_read - - buf_pool->old_stat.n_pages_read) - / time_elapsed, - (buf_pool->stat.n_pages_created - - buf_pool->old_stat.n_pages_created) - / time_elapsed, - (buf_pool->stat.n_pages_written - - buf_pool->old_stat.n_pages_written) - / time_elapsed); + pool_info->n_pages_made_young, + pool_info->n_pages_not_made_young, + pool_info->page_made_young_rate, + pool_info->page_not_made_young_rate, + pool_info->n_pages_read, + pool_info->n_pages_created, + pool_info->n_pages_written, + pool_info->pages_read_rate, + pool_info->pages_created_rate, + pool_info->pages_written_rate); - n_gets_diff = buf_pool->stat.n_page_gets - - buf_pool->old_stat.n_page_gets; - - if (n_gets_diff) { + if (pool_info->n_page_get_delta) { fprintf(file, "Buffer pool hit rate %lu / 1000," " young-making rate %lu / 1000 not %lu / 1000\n", - (ulong) - (1000 - ((1000 * (buf_pool->stat.n_pages_read - - buf_pool->old_stat.n_pages_read)) - / (buf_pool->stat.n_page_gets - - buf_pool->old_stat.n_page_gets))), - (ulong) - (1000 * (buf_pool->stat.n_pages_made_young - - buf_pool->old_stat.n_pages_made_young) - / n_gets_diff), - (ulong) - (1000 * (buf_pool->stat.n_pages_not_made_young - - buf_pool->old_stat.n_pages_not_made_young) - / n_gets_diff)); + (ulong) (1000 - (1000 * pool_info->page_read_delta + / pool_info->n_page_get_delta)), + (ulong) (1000 * pool_info->young_making_delta + / pool_info->n_page_get_delta), + (ulong) (1000 * pool_info->not_young_making_delta + / pool_info->n_page_get_delta)); } else { fputs("No buffer pool page gets since the last printout\n", file); @@ -4891,25 +5043,17 @@ buf_print_io_instance( /* Statistics about read ahead algorithm */ fprintf(file, "Pages read ahead %.2f/s," " evicted without access %.2f/s\n", - (buf_pool->stat.n_ra_pages_read - - buf_pool->old_stat.n_ra_pages_read) - / time_elapsed, - (buf_pool->stat.n_ra_pages_evicted - - buf_pool->old_stat.n_ra_pages_evicted) - / time_elapsed); + pool_info->pages_readahead_rate, + pool_info->pages_evicted_rate); /* Print some values to help us with visualizing what is happening with LRU eviction. */ fprintf(file, "LRU len: %lu, unzip_LRU len: %lu\n" "I/O sum[%lu]:cur[%lu], unzip sum[%lu]:cur[%lu]\n", - UT_LIST_GET_LEN(buf_pool->LRU), - UT_LIST_GET_LEN(buf_pool->unzip_LRU), - buf_LRU_stat_sum.io, buf_LRU_stat_cur.io, - buf_LRU_stat_sum.unzip, buf_LRU_stat_cur.unzip); - - buf_refresh_io_stats(buf_pool); - buf_pool_mutex_exit(buf_pool); + pool_info->lru_len, pool_info->unzip_lru_len, + pool_info->io_sum, pool_info->io_cur, + pool_info->unzip_sum, pool_info->unzip_cur); } /*********************************************************************//** @@ -4920,14 +5064,58 @@ buf_print_io( /*=========*/ FILE* file) /*!< in/out: buffer where to print */ { - ulint i; + ulint i; + buf_pool_info_t* pool_info; + buf_pool_info_t* pool_info_total; + + /* If srv_buf_pool_instances is greater than 1, allocate + one extra buf_pool_info_t, the last one stores + aggregated/total values from all pools */ + if (srv_buf_pool_instances > 1) { + pool_info = (buf_pool_info_t*) mem_zalloc(( + srv_buf_pool_instances + 1) * sizeof *pool_info); + + pool_info_total = &pool_info[srv_buf_pool_instances]; + } else { + ut_a(srv_buf_pool_instances == 1); + pool_info_total = pool_info = (buf_pool_info_t*) mem_zalloc( + sizeof *pool_info) + } for (i = 0; i < srv_buf_pool_instances; i++) { buf_pool_t* buf_pool; buf_pool = buf_pool_from_array(i); - buf_print_io_instance(buf_pool, file); + + /* Fetch individual buffer pool info and calculate + aggregated stats along the way */ + buf_stats_get_pool_info(buf_pool, i, pool_info); + + /* If we have more than one buffer pool, store + the aggregated stats */ + if (srv_buf_pool_instances > 1) { + buf_stats_aggregate_pool_info(pool_info_total, + &pool_info[i]); + } } + + /* Print the aggreate buffer pool info */ + buf_print_io_instance(pool_info_total, file); + + /* If there are more than one buffer pool, print each individual pool + info */ + if (srv_buf_pool_instances > 1) { + fputs("----------------------\n" + "INDIVIDUAL BUFFER POOL INFO\n" + "----------------------\n", file); + + for (i = 0; i < srv_buf_pool_instances; i++) { + fprintf(file, "---BUFFER POOL %lu\n", i); + buf_print_io_instance(&pool_info[i], file); + } + } + + mem_free(pool_info); } /**********************************************************************//** diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index a42eba57fd2..46cae25f0f5 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -116,6 +116,76 @@ enum buf_page_state { before putting to the free list */ }; + +/** This structure defines information we will fetch from each buffer pool. It +will be used to print table IO stats */ +struct buf_pool_info_struct{ + /* General buffer pool info */ + ulint pool_unique_id; /*!< Buffer Pool ID */ + ulint pool_size; /*!< Buffer Pool size in pages */ + ulint lru_len; /*!< Length of buf_pool->LRU */ + ulint old_lru_len; /*!< buf_pool->LRU_old_len */ + ulint free_list_len; /*!< Length of buf_pool->free list */ + ulint flush_list_len; /*!< Length of buf_pool->flush_list */ + ulint n_pend_unzip; /*!< buf_pool->n_pend_unzip, pages + pending decompress */ + ulint n_pend_reads; /*!< buf_pool->n_pend_reads, pages + pending read */ + ulint n_pending_flush_lru; /*!< Pages pending flush in LRU */ + ulint n_pending_flush_list; /*!< Pages pending flush in FLUSH + LIST */ + ulint n_pending_flush_single_page;/*!< Pages pending flush in + BUF_FLUSH_SINGLE_PAGE list */ + ulint n_pages_made_young; /*!< number of pages made young */ + ulint n_pages_not_made_young; /*!< number of pages not made young */ + ulint n_pages_read; /*!< buf_pool->n_pages_read */ + ulint n_pages_created; /*!< buf_pool->n_pages_created */ + ulint n_pages_written; /*!< buf_pool->n_pages_written */ + ulint n_page_gets; /*!< buf_pool->n_page_gets */ + ulint n_ra_pages_read; /*!< buf_pool->n_ra_pages_read, number + of pages readahead */ + ulint n_ra_pages_evicted; /*!< buf_pool->n_ra_pages_evicted, + number of readahead pages evicted + without access */ + ulint n_page_get_delta; /*!< num of buffer pool page gets since + last printout */ + + /* Buffer pool access stats */ + double page_made_young_rate; /*!< page made young rate in pages + per second */ + double page_not_made_young_rate;/*!< page not made young rate + in pages per second */ + double pages_read_rate; /*!< num of pages read per second */ + double pages_created_rate; /*!< num of pages create per second */ + double pages_written_rate; /*!< num of pages written per second */ + ulint page_read_delta; /*!< num of pages read since last + printout */ + ulint young_making_delta; /*!< num of pages made young since + last printout */ + ulint not_young_making_delta; /*!< num of pages not make young since + last printout */ + + /* Statistics about read ahead algorithm. */ + double pages_readahead_rate; /*!< readahead rate in pages per + second */ + double pages_evicted_rate; /*!< rate of readahead page evicted + without access, in pages per second */ + + /* Stats about LRU eviction */ + ulint unzip_lru_len; /*!< length of buf_pool->unzip_LRU + list */ + /* Counters for LRU policy */ + ulint io_sum; /*!< buf_LRU_stat_sum.io */ + ulint io_cur; /*!< buf_LRU_stat_cur.io, num of IO + for current interval */ + ulint unzip_sum; /*!< buf_LRU_stat_sum.unzip */ + ulint unzip_cur; /*!< buf_LRU_stat_cur.unzip, num + pages decompressed in current + interval */ +}; + +typedef struct buf_pool_info_struct buf_pool_info_t; + #ifndef UNIV_HOTBACKUP /********************************************************************//** Acquire mutex on all buffer pool instances */ From 715cf980059ae5ad65b10072b881762795cfc169 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 29 Nov 2010 14:50:26 +0200 Subject: [PATCH 09/52] Fix pointers to documentation Do not print pointer to the 5.1 documentation from within MySQL 5.5. Instead of hardcoding the MySQL version, use the MAJOR_VERSION and MINOR_VERSION CMake variables defined at top-level. --- storage/innobase/CMakeLists.txt | 4 ++++ storage/innobase/include/univ.i | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 4bbda0d2477..76967cd06d2 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -19,6 +19,10 @@ INCLUDE(CheckFunctionExists) INCLUDE(CheckCSourceCompiles) INCLUDE(CheckCSourceRuns) +# Make MySQL version available to InnoDB +ADD_DEFINITIONS("-DMYSQL_MAJOR_VERSION=${MAJOR_VERSION}") +ADD_DEFINITIONS("-DMYSQL_MINOR_VERSION=${MINOR_VERSION}") + # OS tests IF(UNIX) IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 9cf823c2ff3..aa208134a51 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -44,6 +44,11 @@ Created 1/20/1994 Heikki Tuuri #include "hb_univ.i" #endif /* UNIV_HOTBACKUP */ +/* aux macros to convert M into "123" (string) if M is defined like +#define M 123 */ +#define _IB_TO_STR(s) #s +#define IB_TO_STR(s) _IB_TO_STR(s) + #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 1 #define INNODB_VERSION_BUGFIX 4 @@ -57,16 +62,14 @@ component, i.e. we show M.N.P as M.N */ #define INNODB_VERSION_SHORT \ (INNODB_VERSION_MAJOR << 8 | INNODB_VERSION_MINOR) -/* auxiliary macros to help creating the version as string */ -#define __INNODB_VERSION(a, b, c) (#a "." #b "." #c) -#define _INNODB_VERSION(a, b, c) __INNODB_VERSION(a, b, c) - #define INNODB_VERSION_STR \ - _INNODB_VERSION(INNODB_VERSION_MAJOR, \ - INNODB_VERSION_MINOR, \ - INNODB_VERSION_BUGFIX) + IB_TO_STR(INNODB_VERSION_MAJOR) "." \ + IB_TO_STR(INNODB_VERSION_MINOR) "." \ + IB_TO_STR(INNODB_VERSION_BUGFIX) -#define REFMAN "http://dev.mysql.com/doc/refman/5.1/en/" +#define REFMAN "http://dev.mysql.com/doc/refman/" \ + IB_TO_STR(MYSQL_MAJOR_VERSION) "." \ + IB_TO_STR(MYSQL_MINOR_VERSION) "/en/" #ifdef MYSQL_DYNAMIC_PLUGIN /* In the dynamic plugin, redefine some externally visible symbols From 0a96975a30446fbd5e5a9c198ccb1bf7819c9ae6 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 29 Nov 2010 14:53:09 +0200 Subject: [PATCH 10/52] Make output from innobase_start_or_create_for_mysql() consistent Prefix all printed lines with a timestamp and write one space between the timestamp and "InnoDB:". --- storage/innobase/srv/srv0start.c | 377 ++++++++++++++++++++----------- storage/innobase/trx/trx0sys.c | 4 +- 2 files changed, 249 insertions(+), 132 deletions(-) diff --git a/storage/innobase/srv/srv0start.c b/storage/innobase/srv/srv0start.c index d7ea3d5d3da..0e121b58d81 100644 --- a/storage/innobase/srv/srv0start.c +++ b/storage/innobase/srv/srv0start.c @@ -1026,26 +1026,35 @@ innobase_start_or_create_for_mysql(void) on Mac OS X 10.3 or later. */ struct utsname utsname; if (uname(&utsname)) { - fputs("InnoDB: cannot determine Mac OS X version!\n", stderr); + ut_print_timestamp(stderr); + fputs(" InnoDB: cannot determine Mac OS X version!\n", stderr); } else { srv_have_fullfsync = strcmp(utsname.release, "7.") >= 0; } if (!srv_have_fullfsync) { - fputs("InnoDB: On Mac OS X, fsync() may be" - " broken on internal drives,\n" - "InnoDB: making transactions unsafe!\n", stderr); + ut_print_timestamp(stderr); + fputs(" InnoDB: On Mac OS X, fsync() may be " + "broken on internal drives,\n", stderr); + ut_print_timestamp(stderr); + fputs(" InnoDB: making transactions unsafe!\n", stderr); } # endif /* F_FULLFSYNC */ #endif /* HAVE_DARWIN_THREADS */ if (sizeof(ulint) != sizeof(void*)) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: size of InnoDB's ulint is %lu," - " but size of void* is %lu.\n" - "InnoDB: The sizes should be the same" - " so that on a 64-bit platform you can\n" - "InnoDB: allocate more than 4 GB of memory.", - (ulong)sizeof(ulint), (ulong)sizeof(void*)); + " InnoDB: Error: size of InnoDB's ulint is %lu, " + "but size of void*\n", (ulong) sizeof(ulint)); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: is %lu. The sizes should be the same " + "so that on a 64-bit\n", + (ulong) sizeof(void*)); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: platforms you can allocate more than 4 GB " + "of memory.\n"); } /* System tables are created in tablespace 0. Thus, we must @@ -1054,53 +1063,68 @@ innobase_start_or_create_for_mysql(void) innodb_file_per_table) until this function has returned. */ srv_file_per_table = FALSE; #ifdef UNIV_DEBUG + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: !!!!!!!! UNIV_DEBUG switched on !!!!!!!!!\n"); + " InnoDB: !!!!!!!! UNIV_DEBUG switched on !!!!!!!!!\n"); #endif #ifdef UNIV_IBUF_DEBUG + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: !!!!!!!! UNIV_IBUF_DEBUG switched on !!!!!!!!!\n" + " InnoDB: !!!!!!!! UNIV_IBUF_DEBUG switched on !!!!!!!!!\n"); # ifdef UNIV_IBUF_COUNT_DEBUG - "InnoDB: !!!!!!!! UNIV_IBUF_COUNT_DEBUG switched on !!!!!!!!!\n" - "InnoDB: Crash recovery will fail with UNIV_IBUF_COUNT_DEBUG\n" + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: !!!!!!!! UNIV_IBUF_COUNT_DEBUG switched on " + "!!!!!!!!!\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Crash recovery will fail with UNIV_IBUF_COUNT_DEBUG\n"); # endif - ); #endif #ifdef UNIV_SYNC_DEBUG + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: !!!!!!!! UNIV_SYNC_DEBUG switched on !!!!!!!!!\n"); + " InnoDB: !!!!!!!! UNIV_SYNC_DEBUG switched on !!!!!!!!!\n"); #endif #ifdef UNIV_SEARCH_DEBUG + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: !!!!!!!! UNIV_SEARCH_DEBUG switched on !!!!!!!!!\n"); + " InnoDB: !!!!!!!! UNIV_SEARCH_DEBUG switched on !!!!!!!!!\n"); #endif #ifdef UNIV_LOG_LSN_DEBUG + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: !!!!!!!! UNIV_LOG_LSN_DEBUG switched on !!!!!!!!!\n"); + " InnoDB: !!!!!!!! UNIV_LOG_LSN_DEBUG switched on !!!!!!!!!\n"); #endif /* UNIV_LOG_LSN_DEBUG */ #ifdef UNIV_MEM_DEBUG + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: !!!!!!!! UNIV_MEM_DEBUG switched on !!!!!!!!!\n"); + " InnoDB: !!!!!!!! UNIV_MEM_DEBUG switched on !!!!!!!!!\n"); #endif if (UNIV_LIKELY(srv_use_sys_malloc)) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: The InnoDB memory heap is disabled\n"); + " InnoDB: The InnoDB memory heap is disabled\n"); } - fputs("InnoDB: " IB_ATOMICS_STARTUP_MSG - "\nInnoDB: Compressed tables use zlib " ZLIB_VERSION + ut_print_timestamp(stderr); + fputs(" InnoDB: " IB_ATOMICS_STARTUP_MSG "\n", stderr); + + ut_print_timestamp(stderr); + fputs(" InnoDB: Compressed tables use zlib " ZLIB_VERSION #ifdef UNIV_ZIP_DEBUG " with validation" #endif /* UNIV_ZIP_DEBUG */ -#ifdef UNIV_ZIP_COPY - " and extra copying" -#endif /* UNIV_ZIP_COPY */ "\n" , stderr); +#ifdef UNIV_ZIP_COPY + ut_print_timestamp(stderr); + fputs(" InnoDB: and extra copying\n", stderr); +#endif /* UNIV_ZIP_COPY */ /* Since InnoDB does not currently clean up all its internal data structures in MySQL Embedded Server Library server_end(), we @@ -1108,13 +1132,17 @@ innobase_start_or_create_for_mysql(void) second time during the process lifetime. */ if (srv_start_has_been_called) { - fprintf(stderr, - "InnoDB: Error: startup called second time" - " during the process lifetime.\n" - "InnoDB: In the MySQL Embedded Server Library" - " you cannot call server_init()\n" - "InnoDB: more than once during" - " the process lifetime.\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Error: startup called second time " + "during the process\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: lifetime. In the MySQL Embedded " + "Server Library you\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: cannot call server_init() more " + "than once during the\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: process lifetime.\n"); } srv_start_has_been_called = TRUE; @@ -1158,7 +1186,7 @@ innobase_start_or_create_for_mysql(void) if (srv_use_native_aio) { ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: Using Linux native AIO\n"); + " InnoDB: Using Linux native AIO\n"); } #else /* Currently native AIO is supported only on windows and linux @@ -1203,8 +1231,9 @@ innobase_start_or_create_for_mysql(void) srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; #endif } else { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Unrecognized value %s for" + " InnoDB: Unrecognized value %s for" " innodb_flush_method\n", srv_file_flush_method_str); return(DB_ERROR); @@ -1285,7 +1314,8 @@ innobase_start_or_create_for_mysql(void) we'll emit a message telling the user that this parameter is now deprecated. */ if (srv_n_file_io_threads != 4) { - fprintf(stderr, "InnoDB: Warning:" + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Warning:" " innodb_file_io_threads is deprecated." " Please use innodb_read_io_threads and" " innodb_write_io_threads instead\n"); @@ -1316,7 +1346,7 @@ innobase_start_or_create_for_mysql(void) /* Print time to initialize the buffer pool */ ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: Initializing buffer pool, size ="); + " InnoDB: Initializing buffer pool, size ="); if (srv_buf_pool_size >= 1024 * 1024 * 1024) { fprintf(stderr, @@ -1332,11 +1362,12 @@ innobase_start_or_create_for_mysql(void) ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: Completed initialization of buffer pool\n"); + " InnoDB: Completed initialization of buffer pool\n"); if (err != DB_SUCCESS) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Fatal error: cannot allocate the memory" + " InnoDB: Fatal error: cannot allocate memory" " for the buffer pool\n"); return(DB_ERROR); @@ -1348,7 +1379,8 @@ innobase_start_or_create_for_mysql(void) if (srv_buf_pool_size <= 5 * 1024 * 1024) { - fprintf(stderr, "InnoDB: Warning: Small buffer pool size " + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Warning: Small buffer pool size " "(%luM), the flst_validate() debug function " "can cause a deadlock if the buffer pool fills up.\n", srv_buf_pool_size / 1024 / 1024); @@ -1370,18 +1402,19 @@ innobase_start_or_create_for_mysql(void) #ifdef UNIV_LOG_ARCHIVE if (0 != ut_strcmp(srv_log_group_home_dirs[0], srv_arch_dir)) { - fprintf(stderr, - "InnoDB: Error: you must set the log group" - " home dir in my.cnf the\n" - "InnoDB: same as log arch dir.\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Error: you must set the log group home dir in my.cnf\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: the same as log arch dir.\n"); return(DB_ERROR); } #endif /* UNIV_LOG_ARCHIVE */ if (srv_n_log_files * srv_log_file_size >= 262144) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: combined size of log files" + " InnoDB: Error: combined size of log files" " must be < 4 GB\n"); return(DB_ERROR); @@ -1392,10 +1425,13 @@ innobase_start_or_create_for_mysql(void) for (i = 0; i < srv_n_data_files; i++) { #ifndef __WIN__ if (sizeof(off_t) < 5 && srv_data_file_sizes[i] >= 262144) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: file size must be < 4 GB" - " with this MySQL binary\n" - "InnoDB: and operating system combination," + " InnoDB: Error: file size must be < 4 GB" + " with this MySQL binary\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: and operating system combination," " in some OS's < 2 GB\n"); return(DB_ERROR); @@ -1405,8 +1441,9 @@ innobase_start_or_create_for_mysql(void) } if (sum_of_new_sizes < 10485760 / UNIV_PAGE_SIZE) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: tablespace size must be" + " InnoDB: Error: tablespace size must be" " at least 10 MB\n"); return(DB_ERROR); @@ -1419,19 +1456,32 @@ innobase_start_or_create_for_mysql(void) &min_flushed_lsn, &max_flushed_lsn, &sum_of_new_sizes); if (err != DB_SUCCESS) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Could not open or create data files.\n" - "InnoDB: If you tried to add new data files," - " and it failed here,\n" - "InnoDB: you should now edit innodb_data_file_path" - " in my.cnf back\n" - "InnoDB: to what it was, and remove the" - " new ibdata files InnoDB created\n" - "InnoDB: in this failed attempt. InnoDB only wrote" - " those files full of\n" - "InnoDB: zeros, but did not yet use them in any way." - " But be careful: do not\n" - "InnoDB: remove old data files" + " InnoDB: Could not open or create data files.\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: If you tried to add new data files," + " and it failed here,\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: you should now edit innodb_data_file_path" + " in my.cnf back\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: to what it was, and remove the" + " new ibdata files InnoDB created\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: in this failed attempt. InnoDB only wrote" + " those files full of\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: zeros, but did not yet use them in any way." + " But be careful: do not\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: remove old data files" " which contain your precious data!\n"); return((int) err); @@ -1457,18 +1507,29 @@ innobase_start_or_create_for_mysql(void) } if ((log_opened && create_new_db) || (log_opened && log_created)) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: all log files must be" - " created at the same time.\n" - "InnoDB: All log files must be" - " created also in database creation.\n" - "InnoDB: If you want bigger or smaller" - " log files, shut down the\n" - "InnoDB: database and make sure there" - " were no errors in shutdown.\n" - "InnoDB: Then delete the existing log files." - " Edit the .cnf file\n" - "InnoDB: and start the database again.\n"); + " InnoDB: Error: all log files must be" + " created at the same time.\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: All log files must be" + " created also in database creation.\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: If you want bigger or smaller" + " log files, shut down the\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: database and make sure there" + " were no errors in shutdown.\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Then delete the existing log files." + " Edit the .cnf file\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: and start the database again.\n"); return(DB_ERROR); } @@ -1489,27 +1550,41 @@ innobase_start_or_create_for_mysql(void) || max_arch_log_no != min_arch_log_no #endif /* UNIV_LOG_ARCHIVE */ ) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Cannot initialize created" - " log files because\n" - "InnoDB: data files were not in sync" - " with each other\n" - "InnoDB: or the data files are corrupt.\n"); + " InnoDB: Cannot initialize created" + " log files because\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: data files were not in sync" + " with each other\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: or the data files are corrupt.\n"); return(DB_ERROR); } if (max_flushed_lsn < (ib_uint64_t) 1000) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Cannot initialize created" - " log files because\n" - "InnoDB: data files are corrupt," - " or new data files were\n" - "InnoDB: created when the database" - " was started previous\n" - "InnoDB: time but the database" - " was not shut down\n" - "InnoDB: normally after that.\n"); + " InnoDB: Cannot initialize created" + " log files because\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: data files are corrupt," + " or new data files were\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: created when the database" + " was started previous\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: time but the database" + " was not shut down\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: normally after that.\n"); return(DB_ERROR); } @@ -1548,8 +1623,9 @@ innobase_start_or_create_for_mysql(void) #ifdef UNIV_LOG_ARCHIVE } else if (srv_archive_recovery) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Starting archive" + " InnoDB: Starting archive" " recovery from a backup...\n"); err = recv_recovery_from_archive_start( min_flushed_lsn, srv_archive_recovery_limit_lsn, @@ -1771,11 +1847,14 @@ innobase_start_or_create_for_mysql(void) if (!srv_auto_extend_last_data_file && sum_of_data_file_sizes != tablespace_size_in_header) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Error: tablespace size" + " stored in header is %lu pages, but\n", + (ulong) tablespace_size_in_header); + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: tablespace size" - " stored in header is %lu pages, but\n" "InnoDB: the sum of data file sizes is %lu pages\n", - (ulong) tablespace_size_in_header, (ulong) sum_of_data_file_sizes); if (srv_force_recovery == 0 @@ -1783,16 +1862,25 @@ innobase_start_or_create_for_mysql(void) /* This is a fatal error, the tail of a tablespace is missing */ + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Cannot start InnoDB." - " The tail of the system tablespace is\n" - "InnoDB: missing. Have you edited" - " innodb_data_file_path in my.cnf in an\n" - "InnoDB: inappropriate way, removing" - " ibdata files from there?\n" - "InnoDB: You can set innodb_force_recovery=1" - " in my.cnf to force\n" - "InnoDB: a startup if you are trying" + " InnoDB: Cannot start InnoDB." + " The tail of the system tablespace is\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: missing. Have you edited" + " innodb_data_file_path in my.cnf in an\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: inappropriate way, removing" + " ibdata files from there?\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: You can set innodb_force_recovery=1" + " in my.cnf to force\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: a startup if you are trying" " to recover a badly corrupt database.\n"); return(DB_ERROR); @@ -1802,26 +1890,38 @@ innobase_start_or_create_for_mysql(void) if (srv_auto_extend_last_data_file && sum_of_data_file_sizes < tablespace_size_in_header) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: tablespace size stored in header" - " is %lu pages, but\n" - "InnoDB: the sum of data file sizes" + " InnoDB: Error: tablespace size stored in header" + " is %lu pages, but\n", + (ulong) tablespace_size_in_header); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: the sum of data file sizes" " is only %lu pages\n", - (ulong) tablespace_size_in_header, (ulong) sum_of_data_file_sizes); if (srv_force_recovery == 0) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Cannot start InnoDB. The tail of" - " the system tablespace is\n" - "InnoDB: missing. Have you edited" - " innodb_data_file_path in my.cnf in an\n" - "InnoDB: inappropriate way, removing" - " ibdata files from there?\n" - "InnoDB: You can set innodb_force_recovery=1" - " in my.cnf to force\n" - "InnoDB: a startup if you are trying to" + " InnoDB: Cannot start InnoDB. The tail of" + " the system tablespace is\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: missing. Have you edited" + " innodb_data_file_path in my.cnf in an\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: inappropriate way, removing" + " ibdata files from there?\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: You can set innodb_force_recovery=1" + " in my.cnf to force\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: a startup if you are trying to" " recover a badly corrupt database.\n"); return(DB_ERROR); @@ -1832,10 +1932,13 @@ innobase_start_or_create_for_mysql(void) os_fast_mutex_init(&srv_os_test_mutex); if (0 != os_fast_mutex_trylock(&srv_os_test_mutex)) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Error: pthread_mutex_trylock returns" - " an unexpected value on\n" - "InnoDB: success! Cannot continue.\n"); + " InnoDB: Error: pthread_mutex_trylock returns" + " an unexpected value on\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: success! Cannot continue.\n"); exit(1); } @@ -1850,14 +1953,15 @@ innobase_start_or_create_for_mysql(void) if (srv_print_verbose_log) { ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: %s started; " + " InnoDB: %s started; " "log sequence number %llu\n", INNODB_VERSION_STR, srv_start_lsn); } if (srv_force_recovery > 0) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: !!! innodb_force_recovery" + " InnoDB: !!! innodb_force_recovery" " is set to %lu !!!\n", (ulong) srv_force_recovery); } @@ -1878,12 +1982,17 @@ innobase_start_or_create_for_mysql(void) 4.1.1. It is essential that the insert buffer is emptied here! */ + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: You are upgrading to an" - " InnoDB version which allows multiple\n" - "InnoDB: tablespaces. Wait that purge" - " and insert buffer merge run to\n" - "InnoDB: completion...\n"); + " InnoDB: You are upgrading to an" + " InnoDB version which allows multiple\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: tablespaces. Wait that purge" + " and insert buffer merge run to\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: completion...\n"); for (;;) { os_thread_sleep(1000000); @@ -1895,21 +2004,29 @@ innobase_start_or_create_for_mysql(void) break; } } + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Full purge and insert buffer merge" + " InnoDB: Full purge and insert buffer merge" " completed.\n"); trx_sys_mark_upgraded_to_multiple_tablespaces(); + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: You have now successfully upgraded" - " to the multiple tablespaces\n" - "InnoDB: format. You should NOT DOWNGRADE" - " to an earlier version of\n" - "InnoDB: InnoDB! But if you absolutely need to" - " downgrade, see\n" - "InnoDB: " REFMAN "multiple-tablespaces.html\n" - "InnoDB: for instructions.\n"); + " InnoDB: You have now successfully upgraded" + " to the multiple tablespaces\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: format. You should NOT DOWNGRADE" + " to an earlier version of\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: InnoDB! But if you absolutely need to" + " downgrade, see\n"); + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: " REFMAN "multiple-tablespaces.html\n" + " InnoDB: for instructions.\n"); } if (srv_force_recovery == 0) { diff --git a/storage/innobase/trx/trx0sys.c b/storage/innobase/trx/trx0sys.c index 26498a1b712..101f225a06f 100644 --- a/storage/innobase/trx/trx0sys.c +++ b/storage/innobase/trx/trx0sys.c @@ -1160,7 +1160,7 @@ trx_sys_file_format_max_check( ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: highest supported file format is %s.\n", + " InnoDB: highest supported file format is %s.\n", trx_sys_file_format_id_to_name(DICT_TF_FORMAT_MAX)); if (format_id > DICT_TF_FORMAT_MAX) { @@ -1169,7 +1169,7 @@ trx_sys_file_format_max_check( ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: %s: the system tablespace is in a file " + " InnoDB: %s: the system tablespace is in a file " "format that this version doesn't support - %s\n", ((max_format_id <= DICT_TF_FORMAT_MAX) ? "Error" : "Warning"), From 6d445ad8540499f600a47c3565e36c6f3855ec55 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Tue, 30 Nov 2010 16:51:40 +1100 Subject: [PATCH 11/52] Fix Bug# 58459 - assert slot->in_use == FALSE while starting purge thread. Fix a race condition in srv_master_thread(). We need to acquire the kernel mutex before calling srv_table_reserve_slot(). Add a mutex_own() assertion in srv_table_reserve_slot(). --- storage/innobase/srv/srv0srv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 2ac5c8ce4fc..0f6d6c95739 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -831,6 +831,7 @@ srv_table_reserve_slot( ut_a(type > 0); ut_a(type <= SRV_MASTER); + ut_ad(mutex_own(&kernel_mutex)); i = 0; slot = srv_table_get_nth_slot(i); @@ -2627,10 +2628,10 @@ srv_master_thread( srv_main_thread_process_no = os_proc_get_number(); srv_main_thread_id = os_thread_pf(os_thread_get_curr_id()); - srv_table_reserve_slot(SRV_MASTER); - mutex_enter(&kernel_mutex); + srv_table_reserve_slot(SRV_MASTER); + srv_n_threads_active[SRV_MASTER]++; mutex_exit(&kernel_mutex); From f6c03ab820e502b53b47665b1907ece373c8acbe Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 Nov 2010 10:03:55 -0600 Subject: [PATCH 12/52] RB://518 approved by Jimmy Yang and Sunny bains Code cleanup after changes for Bug 56628. The general approach for InnoDB is to make a reference to each enum value whenever it is used in a switch statement. In addition, no default case should be used for switch statements on enum types. This assures that if there is ever any change in the enum values, the switch will need to change to reflect it since a compiler warning will occur. In this case, the enum row_type is declared in handler.h and could be changed for another storage engine. If so, a warning will occur in the InnoDB build. Other changes; * This patch uses 2 macros to help consolidate warning messages that need to occur twice in the single switch for row_format. * Using row_format as the variable name to distinguish it from the enum type. * Function declaration format correction. --- storage/innodb_plugin/handler/ha_innodb.cc | 182 ++++++++++----------- 1 file changed, 89 insertions(+), 93 deletions(-) diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index cffe1e8459e..86168e2bc9b 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -6293,10 +6293,11 @@ create_clustered_index_when_no_primary( /*****************************************************************//** Return a display name for the row format @return row format name */ - -const char *get_row_format_name( -/*============================*/ -enum row_type row_format) /*!< in: Row Format */ +UNIV_INTERN +const char* +get_row_format_name( +/*================*/ + enum row_type row_format) /*!< in: Row Format */ { switch (row_format) { case ROW_TYPE_COMPACT: @@ -6311,12 +6312,38 @@ enum row_type row_format) /*!< in: Row Format */ return("DEFAULT"); case ROW_TYPE_FIXED: return("FIXED"); - default: + case ROW_TYPE_PAGE: + case ROW_TYPE_NOT_USED: break; } return("NOT USED"); } +/** If file-per-table is missing, issue warning and set ret false */ +#define CHECK_ERROR_ROW_TYPE_NEEDS_FILE_PER_TABLE \ + if (!srv_file_per_table) { \ + push_warning_printf( \ + thd, MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_ILLEGAL_HA_CREATE_OPTION, \ + "InnoDB: ROW_FORMAT=%s requires" \ + " innodb_file_per_table.", \ + get_row_format_name(row_format)); \ + ret = FALSE; \ + } + +/** If file-format is Antelope, issue warning and set ret false */ +#define CHECK_ERROR_ROW_TYPE_NEEDS_GT_ANTELOPE \ + if (srv_file_format < DICT_TF_FORMAT_ZIP) { \ + push_warning_printf( \ + thd, MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_ILLEGAL_HA_CREATE_OPTION, \ + "InnoDB: ROW_FORMAT=%s requires" \ + " innodb_file_format > Antelope.", \ + get_row_format_name(row_format)); \ + ret = FALSE; \ + } + + /*****************************************************************//** Validates the create options. We may build on this function in future. For now, it checks two specifiers: @@ -6334,7 +6361,7 @@ create_options_are_valid( { ibool kbs_specified = FALSE; ibool ret = TRUE; - enum row_type row_type = form->s->row_type; + enum row_type row_format = form->s->row_type; ut_ad(thd != NULL); @@ -6343,23 +6370,6 @@ create_options_are_valid( return(TRUE); } - /* Check for a valid Innodb ROW_FORMAT specifier. For example, - ROW_TYPE_FIXED can be sent to Innodb */ - switch (row_type) { - case ROW_TYPE_COMPACT: - case ROW_TYPE_COMPRESSED: - case ROW_TYPE_DYNAMIC: - case ROW_TYPE_REDUNDANT: - case ROW_TYPE_DEFAULT: - break; - default: - push_warning( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: invalid ROW_FORMAT specifier."); - ret = FALSE; - } - ut_ad(form != NULL); ut_ad(create_info != NULL); @@ -6372,7 +6382,23 @@ create_options_are_valid( case 4: case 8: case 16: - /* Valid value. */ + /* Valid KEY_BLOCK_SIZE, check its dependencies. */ + if (!srv_file_per_table) { + push_warning( + thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: KEY_BLOCK_SIZE requires" + " innodb_file_per_table."); + ret = FALSE; + } + if (srv_file_format < DICT_TF_FORMAT_ZIP) { + push_warning( + thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: KEY_BLOCK_SIZE requires" + " innodb_file_format > Antelope."); + ret = FALSE; + } break; default: push_warning_printf( @@ -6382,72 +6408,43 @@ create_options_are_valid( " Valid values are [1, 2, 4, 8, 16]", create_info->key_block_size); ret = FALSE; + break; } } - /* If KEY_BLOCK_SIZE was specified, check for its - dependencies. */ - if (kbs_specified && !srv_file_per_table) { - push_warning( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: KEY_BLOCK_SIZE" - " requires innodb_file_per_table."); - ret = FALSE; - } - - if (kbs_specified && srv_file_format < DICT_TF_FORMAT_ZIP) { - push_warning( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: KEY_BLOCK_SIZE requires" - " innodb_file_format > Antelope."); - ret = FALSE; - } - - switch (row_type) { + /* Check for a valid Innodb ROW_FORMAT specifier and + other incompatibilities. */ + switch (row_format) { case ROW_TYPE_COMPRESSED: - case ROW_TYPE_DYNAMIC: - /* These two ROW_FORMATs require srv_file_per_table - and srv_file_format > Antelope */ - if (!srv_file_per_table) { - push_warning_printf( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ROW_FORMAT=%s" - " requires innodb_file_per_table.", - get_row_format_name(row_type)); - ret = FALSE; - } - - if (srv_file_format < DICT_TF_FORMAT_ZIP) { - push_warning_printf( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ROW_FORMAT=%s requires" - " innodb_file_format > Antelope.", - get_row_format_name(row_type)); - ret = FALSE; - } - default: + CHECK_ERROR_ROW_TYPE_NEEDS_FILE_PER_TABLE; + CHECK_ERROR_ROW_TYPE_NEEDS_GT_ANTELOPE; break; - } - - switch (row_type) { - case ROW_TYPE_REDUNDANT: - case ROW_TYPE_COMPACT: case ROW_TYPE_DYNAMIC: - /* KEY_BLOCK_SIZE is only allowed with Compressed or Default */ + CHECK_ERROR_ROW_TYPE_NEEDS_FILE_PER_TABLE; + CHECK_ERROR_ROW_TYPE_NEEDS_GT_ANTELOPE; + /* fall through since dynamic also shuns KBS */ + case ROW_TYPE_COMPACT: + case ROW_TYPE_REDUNDANT: if (kbs_specified) { push_warning_printf( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, "InnoDB: cannot specify ROW_FORMAT = %s" " with KEY_BLOCK_SIZE.", - get_row_format_name(row_type)); - ret = FALSE; + get_row_format_name(row_format)); + ret = FALSE; } - default: + break; + case ROW_TYPE_DEFAULT: + break; + case ROW_TYPE_FIXED: + case ROW_TYPE_PAGE: + case ROW_TYPE_NOT_USED: + push_warning( + thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, \ + "InnoDB: invalid ROW_FORMAT specifier."); + ret = FALSE; break; } @@ -6498,7 +6495,7 @@ ha_innobase::create( const ulint file_format = srv_file_format; const char* stmt; size_t stmt_len; - enum row_type row_type; + enum row_type row_format; DBUG_ENTER("ha_innobase::create"); @@ -6598,8 +6595,8 @@ ha_innobase::create( push_warning( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: KEY_BLOCK_SIZE" - " requires innodb_file_per_table."); + "InnoDB: KEY_BLOCK_SIZE requires" + " innodb_file_per_table."); flags = 0; } @@ -6616,20 +6613,19 @@ ha_innobase::create( push_warning_printf( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ignoring" - " KEY_BLOCK_SIZE=%lu.", + "InnoDB: ignoring KEY_BLOCK_SIZE=%lu.", create_info->key_block_size); } } - row_type = form->s->row_type; + row_format = form->s->row_type; if (flags) { /* if ROW_FORMAT is set to default, automatically change it to COMPRESSED.*/ - if (row_type == ROW_TYPE_DEFAULT) { - row_type = ROW_TYPE_COMPRESSED; - } else if (row_type != ROW_TYPE_COMPRESSED) { + if (row_format == ROW_TYPE_DEFAULT) { + row_format = ROW_TYPE_COMPRESSED; + } else if (row_format != ROW_TYPE_COMPRESSED) { /* ROW_FORMAT other than COMPRESSED ignores KEY_BLOCK_SIZE. It does not make sense to reject conflicting @@ -6646,7 +6642,7 @@ ha_innobase::create( } } else { /* flags == 0 means no KEY_BLOCK_SIZE.*/ - if (row_type == ROW_TYPE_COMPRESSED) { + if (row_format == ROW_TYPE_COMPRESSED) { /* ROW_FORMAT=COMPRESSED without KEY_BLOCK_SIZE implies half the maximum KEY_BLOCK_SIZE. */ @@ -6661,7 +6657,7 @@ ha_innobase::create( } } - switch (row_type) { + switch (row_format) { case ROW_TYPE_REDUNDANT: break; case ROW_TYPE_COMPRESSED: @@ -6672,25 +6668,25 @@ ha_innobase::create( ER_ILLEGAL_HA_CREATE_OPTION, "InnoDB: ROW_FORMAT=%s requires" " innodb_file_per_table.", - get_row_format_name(row_type)); + get_row_format_name(row_format)); } else if (file_format < DICT_TF_FORMAT_ZIP) { push_warning_printf( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, "InnoDB: ROW_FORMAT=%s requires" " innodb_file_format > Antelope.", - get_row_format_name(row_type)); + get_row_format_name(row_format)); } else { flags |= DICT_TF_COMPACT - | (DICT_TF_FORMAT_ZIP - << DICT_TF_FORMAT_SHIFT); + | (DICT_TF_FORMAT_ZIP + << DICT_TF_FORMAT_SHIFT); break; } /* fall through */ case ROW_TYPE_NOT_USED: case ROW_TYPE_FIXED: - default: + case ROW_TYPE_PAGE: push_warning( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, From 4018c393611ce9e0b5e4d563dc1264423354bac1 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 Nov 2010 10:06:11 -0600 Subject: [PATCH 13/52] RB://518 approved by Jimmy Yang and Sunny bains Code cleanup after changes for Bug 56628. The general approach for InnoDB is to make a reference to each enum value whenever it is used in a switch statement. In addition, no default case should be used for switch statements on enum types. This assures that if there is ever any change in the enum values, the switch will need to change to reflect it since a compiler warning will occur. In this case, the enum row_type is declared in handler.h and could be changed for another storage engine. If so, a warning will occur in the InnoDB build. Other changes; * This patch uses 2 macros to help consolidate warning messages that need to occur twice in the single switch for row_format. * Using row_format as the variable name to distinguish it from the enum type. * Function declaration format correction. --- storage/innobase/handler/ha_innodb.cc | 178 +++++++++++++------------- 1 file changed, 87 insertions(+), 91 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 61fa3102dc7..705ab6c8df3 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6498,10 +6498,11 @@ create_clustered_index_when_no_primary( /*****************************************************************//** Return a display name for the row format @return row format name */ - -const char *get_row_format_name( -/*============================*/ -enum row_type row_format) /*!< in: Row Format */ +UNIV_INTERN +const char* +get_row_format_name( +/*================*/ + enum row_type row_format) /*!< in: Row Format */ { switch (row_format) { case ROW_TYPE_COMPACT: @@ -6516,12 +6517,38 @@ enum row_type row_format) /*!< in: Row Format */ return("DEFAULT"); case ROW_TYPE_FIXED: return("FIXED"); - default: + case ROW_TYPE_PAGE: + case ROW_TYPE_NOT_USED: break; } return("NOT USED"); } +/** If file-per-table is missing, issue warning and set ret false */ +#define CHECK_ERROR_ROW_TYPE_NEEDS_FILE_PER_TABLE \ + if (!srv_file_per_table) { \ + push_warning_printf( \ + thd, MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_ILLEGAL_HA_CREATE_OPTION, \ + "InnoDB: ROW_FORMAT=%s requires" \ + " innodb_file_per_table.", \ + get_row_format_name(row_format)); \ + ret = FALSE; \ + } + +/** If file-format is Antelope, issue warning and set ret false */ +#define CHECK_ERROR_ROW_TYPE_NEEDS_GT_ANTELOPE \ + if (srv_file_format < DICT_TF_FORMAT_ZIP) { \ + push_warning_printf( \ + thd, MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_ILLEGAL_HA_CREATE_OPTION, \ + "InnoDB: ROW_FORMAT=%s requires" \ + " innodb_file_format > Antelope.", \ + get_row_format_name(row_format)); \ + ret = FALSE; \ + } + + /*****************************************************************//** Validates the create options. We may build on this function in future. For now, it checks two specifiers: @@ -6539,7 +6566,7 @@ create_options_are_valid( { ibool kbs_specified = FALSE; ibool ret = TRUE; - enum row_type row_type = form->s->row_type; + enum row_type row_format = form->s->row_type; ut_ad(thd != NULL); @@ -6548,23 +6575,6 @@ create_options_are_valid( return(TRUE); } - /* Check for a valid Innodb ROW_FORMAT specifier. For example, - ROW_TYPE_FIXED can be sent to Innodb */ - switch (row_type) { - case ROW_TYPE_COMPACT: - case ROW_TYPE_COMPRESSED: - case ROW_TYPE_DYNAMIC: - case ROW_TYPE_REDUNDANT: - case ROW_TYPE_DEFAULT: - break; - default: - push_warning( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: invalid ROW_FORMAT specifier."); - ret = FALSE; - } - ut_ad(form != NULL); ut_ad(create_info != NULL); @@ -6577,7 +6587,23 @@ create_options_are_valid( case 4: case 8: case 16: - /* Valid value. */ + /* Valid KEY_BLOCK_SIZE, check its dependencies. */ + if (!srv_file_per_table) { + push_warning( + thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: KEY_BLOCK_SIZE requires" + " innodb_file_per_table."); + ret = FALSE; + } + if (srv_file_format < DICT_TF_FORMAT_ZIP) { + push_warning( + thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: KEY_BLOCK_SIZE requires" + " innodb_file_format > Antelope."); + ret = FALSE; + } break; default: push_warning_printf( @@ -6587,72 +6613,43 @@ create_options_are_valid( " Valid values are [1, 2, 4, 8, 16]", create_info->key_block_size); ret = FALSE; + break; } } - /* If KEY_BLOCK_SIZE was specified, check for its - dependencies. */ - if (kbs_specified && !srv_file_per_table) { - push_warning( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: KEY_BLOCK_SIZE requires" - " innodb_file_per_table."); - ret = FALSE; - } - - if (kbs_specified && srv_file_format < DICT_TF_FORMAT_ZIP) { - push_warning( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: KEY_BLOCK_SIZE requires" - " innodb_file_format > Antelope."); - ret = FALSE; - } - - switch (row_type) { + /* Check for a valid Innodb ROW_FORMAT specifier and + other incompatibilities. */ + switch (row_format) { case ROW_TYPE_COMPRESSED: - case ROW_TYPE_DYNAMIC: - /* These two ROW_FORMATs require srv_file_per_table - and srv_file_format > Antelope */ - if (!srv_file_per_table) { - push_warning_printf( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ROW_FORMAT=%s requires" - " innodb_file_per_table.", - get_row_format_name(row_type)); - ret = FALSE; - } - - if (srv_file_format < DICT_TF_FORMAT_ZIP) { - push_warning_printf( - thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ROW_FORMAT=%s requires" - " innodb_file_format > Antelope.", - get_row_format_name(row_type)); - ret = FALSE; - } - default: + CHECK_ERROR_ROW_TYPE_NEEDS_FILE_PER_TABLE; + CHECK_ERROR_ROW_TYPE_NEEDS_GT_ANTELOPE; break; - } - - switch (row_type) { - case ROW_TYPE_REDUNDANT: - case ROW_TYPE_COMPACT: case ROW_TYPE_DYNAMIC: - /* KEY_BLOCK_SIZE is only allowed with Compressed or Default */ + CHECK_ERROR_ROW_TYPE_NEEDS_FILE_PER_TABLE; + CHECK_ERROR_ROW_TYPE_NEEDS_GT_ANTELOPE; + /* fall through since dynamic also shuns KBS */ + case ROW_TYPE_COMPACT: + case ROW_TYPE_REDUNDANT: if (kbs_specified) { push_warning_printf( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, "InnoDB: cannot specify ROW_FORMAT = %s" " with KEY_BLOCK_SIZE.", - get_row_format_name(row_type)); - ret = FALSE; + get_row_format_name(row_format)); + ret = FALSE; } - default: + break; + case ROW_TYPE_DEFAULT: + break; + case ROW_TYPE_FIXED: + case ROW_TYPE_PAGE: + case ROW_TYPE_NOT_USED: + push_warning( + thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, \ + "InnoDB: invalid ROW_FORMAT specifier."); + ret = FALSE; break; } @@ -6703,7 +6700,7 @@ ha_innobase::create( const ulint file_format = srv_file_format; const char* stmt; size_t stmt_len; - enum row_type row_type; + enum row_type row_format; DBUG_ENTER("ha_innobase::create"); @@ -6821,20 +6818,19 @@ ha_innobase::create( push_warning_printf( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ignoring" - " KEY_BLOCK_SIZE=%lu.", + "InnoDB: ignoring KEY_BLOCK_SIZE=%lu.", create_info->key_block_size); } } - row_type = form->s->row_type; + row_format = form->s->row_type; if (flags) { /* if ROW_FORMAT is set to default, automatically change it to COMPRESSED.*/ - if (row_type == ROW_TYPE_DEFAULT) { - row_type = ROW_TYPE_COMPRESSED; - } else if (row_type != ROW_TYPE_COMPRESSED) { + if (row_format == ROW_TYPE_DEFAULT) { + row_format = ROW_TYPE_COMPRESSED; + } else if (row_format != ROW_TYPE_COMPRESSED) { /* ROW_FORMAT other than COMPRESSED ignores KEY_BLOCK_SIZE. It does not make sense to reject conflicting @@ -6851,7 +6847,7 @@ ha_innobase::create( } } else { /* flags == 0 means no KEY_BLOCK_SIZE.*/ - if (row_type == ROW_TYPE_COMPRESSED) { + if (row_format == ROW_TYPE_COMPRESSED) { /* ROW_FORMAT=COMPRESSED without KEY_BLOCK_SIZE implies half the maximum KEY_BLOCK_SIZE. */ @@ -6866,7 +6862,7 @@ ha_innobase::create( } } - switch (row_type) { + switch (row_format) { case ROW_TYPE_REDUNDANT: break; case ROW_TYPE_COMPRESSED: @@ -6877,25 +6873,25 @@ ha_innobase::create( ER_ILLEGAL_HA_CREATE_OPTION, "InnoDB: ROW_FORMAT=%s requires" " innodb_file_per_table.", - get_row_format_name(row_type)); + get_row_format_name(row_format)); } else if (file_format < DICT_TF_FORMAT_ZIP) { push_warning_printf( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, "InnoDB: ROW_FORMAT=%s requires" " innodb_file_format > Antelope.", - get_row_format_name(row_type)); + get_row_format_name(row_format)); } else { flags |= DICT_TF_COMPACT - | (DICT_TF_FORMAT_ZIP - << DICT_TF_FORMAT_SHIFT); + | (DICT_TF_FORMAT_ZIP + << DICT_TF_FORMAT_SHIFT); break; } /* fall through */ case ROW_TYPE_NOT_USED: case ROW_TYPE_FIXED: - default: + case ROW_TYPE_PAGE: push_warning( thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, From 2dde698e0e4aa5a4264be6f34742f1f0702c0919 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 Nov 2010 12:25:52 -0600 Subject: [PATCH 14/52] Bug#55222 - RB://517 - Approved by Sunny InnoDB does not attempt to handle lower_case_table_names == 2 when looking up foreign table names and referenced table name. It turned that server variable into a boolean and ignored the possibility of it being '2'. The setting lower_case_table_names == 2 means that it should be stored and displayed in mixed case as given, but compared internally in lower case. Normally the server deals with this since it stores table names. But InnoDB stores referential constraints for the server, so it needs to keep track of both lower case and given names. This solution creates two table name pointers for each foreign and referenced table name. One to display the name, and one to look it up. Both pointers point to the same allocated string unless this setting is 2. So the overhead added is not too much. Two functions are created in dict0mem.c to populate the ..._lookup versions of these pointers. Both dict_mem_foreign_table_name_lookup_set() and dict_mem_referenced_table_name_lookup_set() are called 5 times each. --- mysql-test/r/lowercase_table4.result | 108 ++++++++++++++++++ .../suite/innodb/r/innodb_bug57904.result | 22 ++-- .../suite/innodb/t/innodb_bug57904.test | 22 ++-- mysql-test/t/lowercase_table4.test | 52 +++++++++ storage/innobase/dict/dict0dict.c | 68 ++++++----- storage/innobase/dict/dict0load.c | 18 ++- storage/innobase/dict/dict0mem.c | 55 +++++++++ storage/innobase/handler/ha_innodb.cc | 19 +-- storage/innobase/include/dict0mem.h | 24 ++++ storage/innobase/include/srv0srv.h | 4 +- storage/innobase/row/row0ins.c | 2 +- storage/innobase/row/row0mysql.c | 2 +- storage/innobase/row/row0upd.c | 2 +- storage/innobase/srv/srv0srv.c | 8 +- 14 files changed, 331 insertions(+), 75 deletions(-) diff --git a/mysql-test/r/lowercase_table4.result b/mysql-test/r/lowercase_table4.result index e3f861f8884..f896b9008e3 100755 --- a/mysql-test/r/lowercase_table4.result +++ b/mysql-test/r/lowercase_table4.result @@ -5,3 +5,111 @@ CREATE DATABASE XY; USE XY; DROP DATABASE XY; +USE TEST; +# +# Bug55222 Mysqldump table names case bug in REFERENCES clause +# InnoDB did not handle lower_case_table_names=2 for +# foreign_table_names and referenced_table_names. +# +SHOW VARIABLES LIKE 'lower_case_table_names'; +Variable_name Value +lower_case_table_names 2 +DROP TABLE IF EXISTS `Table2`; +DROP TABLE IF EXISTS `Table1`; +CREATE TABLE `Table1`(c1 INT PRIMARY KEY) ENGINE=InnoDB; +CREATE TABLE `Table2`(c1 INT PRIMARY KEY, c2 INT) ENGINE=InnoDB; +ALTER TABLE `Table2` ADD CONSTRAINT fk1 FOREIGN KEY(c2) REFERENCES `Table1`(c1); +SHOW CREATE TABLE `Table2`; +Table Table2 +Create Table CREATE TABLE `Table2` ( + `c1` int(11) NOT NULL, + `c2` int(11) DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `fk1` (`c2`), + CONSTRAINT `fk1` FOREIGN KEY (`c2`) REFERENCES `Table1` (`c1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS; +CONSTRAINT_CATALOG def +CONSTRAINT_SCHEMA test +CONSTRAINT_NAME fk1 +UNIQUE_CONSTRAINT_CATALOG def +UNIQUE_CONSTRAINT_SCHEMA test +UNIQUE_CONSTRAINT_NAME PRIMARY +MATCH_OPTION NONE +UPDATE_RULE RESTRICT +DELETE_RULE RESTRICT +TABLE_NAME Table2 +REFERENCED_TABLE_NAME Table1 +DROP TABLE `Table2`; +DROP TABLE `Table1`; +DROP TABLE IF EXISTS Product_Order; +DROP TABLE IF EXISTS Product; +DROP TABLE IF EXISTS Customer; +CREATE TABLE Product (Category INT NOT NULL, Id INT NOT NULL, +Price DECIMAL, PRIMARY KEY(Category, Id)) ENGINE=InnoDB; +CREATE TABLE Customer (Id INT NOT NULL, PRIMARY KEY (Id)) ENGINE=InnoDB; +CREATE TABLE Product_Order (No INT NOT NULL AUTO_INCREMENT, +Product_Category INT NOT NULL, +Product_Id INT NOT NULL, +Customer_Id INT NOT NULL, +PRIMARY KEY(No), +INDEX (Product_Category, Product_Id), +FOREIGN KEY (Product_Category, Product_Id) +REFERENCES Product(Category, Id) ON UPDATE CASCADE ON DELETE RESTRICT, +INDEX (Customer_Id), +FOREIGN KEY (Customer_Id) +REFERENCES Customer(Id) +) ENGINE=INNODB; +SHOW CREATE TABLE Product_Order; +Table Product_Order +Create Table CREATE TABLE `Product_Order` ( + `No` int(11) NOT NULL AUTO_INCREMENT, + `Product_Category` int(11) NOT NULL, + `Product_Id` int(11) NOT NULL, + `Customer_Id` int(11) NOT NULL, + PRIMARY KEY (`No`), + KEY `Product_Category` (`Product_Category`,`Product_Id`), + KEY `Customer_Id` (`Customer_Id`), + CONSTRAINT `product_order_ibfk_1` FOREIGN KEY (`Product_Category`, `Product_Id`) REFERENCES `Product` (`Category`, `Id`) ON UPDATE CASCADE, + CONSTRAINT `product_order_ibfk_2` FOREIGN KEY (`Customer_Id`) REFERENCES `Customer` (`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW CREATE TABLE Product; +Table Product +Create Table CREATE TABLE `Product` ( + `Category` int(11) NOT NULL, + `Id` int(11) NOT NULL, + `Price` decimal(10,0) DEFAULT NULL, + PRIMARY KEY (`Category`,`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW CREATE TABLE Customer; +Table Customer +Create Table CREATE TABLE `Customer` ( + `Id` int(11) NOT NULL, + PRIMARY KEY (`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS; +CONSTRAINT_CATALOG def +CONSTRAINT_SCHEMA test +CONSTRAINT_NAME product_order_ibfk_1 +UNIQUE_CONSTRAINT_CATALOG def +UNIQUE_CONSTRAINT_SCHEMA test +UNIQUE_CONSTRAINT_NAME PRIMARY +MATCH_OPTION NONE +UPDATE_RULE CASCADE +DELETE_RULE RESTRICT +TABLE_NAME Product_Order +REFERENCED_TABLE_NAME Product +CONSTRAINT_CATALOG def +CONSTRAINT_SCHEMA test +CONSTRAINT_NAME product_order_ibfk_2 +UNIQUE_CONSTRAINT_CATALOG def +UNIQUE_CONSTRAINT_SCHEMA test +UNIQUE_CONSTRAINT_NAME PRIMARY +MATCH_OPTION NONE +UPDATE_RULE RESTRICT +DELETE_RULE RESTRICT +TABLE_NAME Product_Order +REFERENCED_TABLE_NAME Customer +DROP TABLE Product_Order; +DROP TABLE Product; +DROP TABLE Customer; diff --git a/mysql-test/suite/innodb/r/innodb_bug57904.result b/mysql-test/suite/innodb/r/innodb_bug57904.result index 84868dcf46b..d265adc385f 100755 --- a/mysql-test/suite/innodb/r/innodb_bug57904.result +++ b/mysql-test/suite/innodb/r/innodb_bug57904.result @@ -1,16 +1,16 @@ -CREATE TABLE product (category INT NOT NULL, id INT NOT NULL, +CREATE TABLE product (category INT NOT NULL, id INT NOT NULL, price DECIMAL, PRIMARY KEY(category, id)) ENGINE=INNODB; CREATE TABLE customer (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB; -CREATE TABLE product_order (no INT NOT NULL AUTO_INCREMENT, -product_category INT NOT NULL, -product_id INT NOT NULL, -customer_id INT NOT NULL, -PRIMARY KEY(no), -INDEX (product_category, product_id), -FOREIGN KEY (product_category, product_id) -REFERENCES product(category, id) ON UPDATE CASCADE ON DELETE RESTRICT, -INDEX (customer_id), -FOREIGN KEY (customer_id) +CREATE TABLE product_order (no INT NOT NULL AUTO_INCREMENT, +product_category INT NOT NULL, +product_id INT NOT NULL, +customer_id INT NOT NULL, +PRIMARY KEY(no), +INDEX (product_category, product_id), +FOREIGN KEY (product_category, product_id) +REFERENCES product(category, id) ON UPDATE CASCADE ON DELETE RESTRICT, +INDEX (customer_id), +FOREIGN KEY (customer_id) REFERENCES customer(id) ) ENGINE=INNODB; SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS; diff --git a/mysql-test/suite/innodb/t/innodb_bug57904.test b/mysql-test/suite/innodb/t/innodb_bug57904.test index d283ad11c3a..1131e24844d 100755 --- a/mysql-test/suite/innodb/t/innodb_bug57904.test +++ b/mysql-test/suite/innodb/t/innodb_bug57904.test @@ -3,19 +3,19 @@ # -- source include/have_innodb.inc -CREATE TABLE product (category INT NOT NULL, id INT NOT NULL, +CREATE TABLE product (category INT NOT NULL, id INT NOT NULL, price DECIMAL, PRIMARY KEY(category, id)) ENGINE=INNODB; CREATE TABLE customer (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB; -CREATE TABLE product_order (no INT NOT NULL AUTO_INCREMENT, - product_category INT NOT NULL, - product_id INT NOT NULL, - customer_id INT NOT NULL, - PRIMARY KEY(no), - INDEX (product_category, product_id), - FOREIGN KEY (product_category, product_id) - REFERENCES product(category, id) ON UPDATE CASCADE ON DELETE RESTRICT, - INDEX (customer_id), - FOREIGN KEY (customer_id) +CREATE TABLE product_order (no INT NOT NULL AUTO_INCREMENT, + product_category INT NOT NULL, + product_id INT NOT NULL, + customer_id INT NOT NULL, + PRIMARY KEY(no), + INDEX (product_category, product_id), + FOREIGN KEY (product_category, product_id) + REFERENCES product(category, id) ON UPDATE CASCADE ON DELETE RESTRICT, + INDEX (customer_id), + FOREIGN KEY (customer_id) REFERENCES customer(id) ) ENGINE=INNODB; diff --git a/mysql-test/t/lowercase_table4.test b/mysql-test/t/lowercase_table4.test index 93956047145..783a4fcae51 100755 --- a/mysql-test/t/lowercase_table4.test +++ b/mysql-test/t/lowercase_table4.test @@ -53,4 +53,56 @@ eval SELECT * FROM XY.T_$tcs LIMIT 1; --enable_query_log --enable_result_log DROP DATABASE XY; +USE TEST; + +--echo # +--echo # Bug55222 Mysqldump table names case bug in REFERENCES clause +--echo # InnoDB did not handle lower_case_table_names=2 for +--echo # foreign_table_names and referenced_table_names. +--echo # + +SHOW VARIABLES LIKE 'lower_case_table_names'; + +--disable_warnings +DROP TABLE IF EXISTS `Table2`; +DROP TABLE IF EXISTS `Table1`; +--disable_warnings + +CREATE TABLE `Table1`(c1 INT PRIMARY KEY) ENGINE=InnoDB; +CREATE TABLE `Table2`(c1 INT PRIMARY KEY, c2 INT) ENGINE=InnoDB; +ALTER TABLE `Table2` ADD CONSTRAINT fk1 FOREIGN KEY(c2) REFERENCES `Table1`(c1); +query_vertical SHOW CREATE TABLE `Table2`; +query_vertical SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS; +DROP TABLE `Table2`; +DROP TABLE `Table1`; + +--disable_warnings +DROP TABLE IF EXISTS Product_Order; +DROP TABLE IF EXISTS Product; +DROP TABLE IF EXISTS Customer; +--enable_warnings + +CREATE TABLE Product (Category INT NOT NULL, Id INT NOT NULL, + Price DECIMAL, PRIMARY KEY(Category, Id)) ENGINE=InnoDB; +CREATE TABLE Customer (Id INT NOT NULL, PRIMARY KEY (Id)) ENGINE=InnoDB; +CREATE TABLE Product_Order (No INT NOT NULL AUTO_INCREMENT, + Product_Category INT NOT NULL, + Product_Id INT NOT NULL, + Customer_Id INT NOT NULL, + PRIMARY KEY(No), + INDEX (Product_Category, Product_Id), + FOREIGN KEY (Product_Category, Product_Id) + REFERENCES Product(Category, Id) ON UPDATE CASCADE ON DELETE RESTRICT, + INDEX (Customer_Id), + FOREIGN KEY (Customer_Id) + REFERENCES Customer(Id) + ) ENGINE=INNODB; + +query_vertical SHOW CREATE TABLE Product_Order; +query_vertical SHOW CREATE TABLE Product; +query_vertical SHOW CREATE TABLE Customer; +query_vertical SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS; +DROP TABLE Product_Order; +DROP TABLE Product; +DROP TABLE Customer; diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index 0d15ad8b716..f3660cef611 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -52,8 +52,9 @@ UNIV_INTERN dict_index_t* dict_ind_compact; #include "que0que.h" #include "rem0cmp.h" #include "row0merge.h" +#include "srv0srv.h" /* srv_lower_case_table_names */ #include "m_ctype.h" /* my_isspace() */ -#include "ha_prototypes.h" /* innobase_strcasecmp() */ +#include "ha_prototypes.h" /* innobase_strcasecmp(), innobase_casedn_str()*/ #include @@ -1080,13 +1081,13 @@ dict_table_rename_in_cache( /* Allocate a longer name buffer; TODO: store buf len to save memory */ - foreign->foreign_table_name - = mem_heap_alloc(foreign->heap, - ut_strlen(table->name) + 1); + foreign->foreign_table_name = mem_heap_strdup( + foreign->heap, table->name); + dict_mem_foreign_table_name_lookup_set(foreign, TRUE); + } else { + strcpy(foreign->foreign_table_name, table->name); + dict_mem_foreign_table_name_lookup_set(foreign, FALSE); } - - strcpy(foreign->foreign_table_name, table->name); - if (strchr(foreign->id, '/')) { ulint db_len; char* old_id; @@ -1152,12 +1153,14 @@ dict_table_rename_in_cache( /* Allocate a longer name buffer; TODO: store buf len to save memory */ - foreign->referenced_table_name = mem_heap_alloc( - foreign->heap, strlen(table->name) + 1); + foreign->referenced_table_name = mem_heap_strdup( + foreign->heap, table->name); + dict_mem_referenced_table_name_lookup_set(foreign, TRUE); + } else { + /* Use the same buffer */ + strcpy(foreign->referenced_table_name, table->name); + dict_mem_referenced_table_name_lookup_set(foreign, FALSE); } - - strcpy(foreign->referenced_table_name, table->name); - foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } @@ -2583,10 +2586,10 @@ dict_foreign_add_to_cache( ut_ad(mutex_own(&(dict_sys->mutex))); for_table = dict_table_check_if_in_cache_low( - foreign->foreign_table_name); + foreign->foreign_table_name_lookup); ref_table = dict_table_check_if_in_cache_low( - foreign->referenced_table_name); + foreign->referenced_table_name_lookup); ut_a(for_table || ref_table); if (for_table) { @@ -3015,19 +3018,25 @@ dict_scan_table_name( memcpy(ref, database_name, database_name_len); ref[database_name_len] = '/'; memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); -#ifndef __WIN__ - if (srv_lower_case_table_names) { -#endif /* !__WIN__ */ - /* The table name is always put to lower case on Windows. */ + + /* Values; 0 = Store and compare as given; case sensitive + 1 = Store and compare in lower; case insensitive + 2 = Store as given, compare in lower; case semi-sensitive */ + if (srv_lower_case_table_names == 2) { innobase_casedn_str(ref); -#ifndef __WIN__ + *table = dict_table_get_low(ref); + memcpy(ref, database_name, database_name_len); + ref[database_name_len] = '/'; + memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); + } else { + if (srv_lower_case_table_names == 1) { + innobase_casedn_str(ref); + } + *table = dict_table_get_low(ref); } -#endif /* !__WIN__ */ *success = TRUE; *ref_name = ref; - *table = dict_table_get_low(ref); - return(ptr); } @@ -3516,8 +3525,10 @@ col_loop1: } foreign->foreign_table = table; - foreign->foreign_table_name = mem_heap_strdup(foreign->heap, - table->name); + foreign->foreign_table_name = mem_heap_strdup( + foreign->heap, table->name); + dict_mem_foreign_table_name_lookup_set(foreign, TRUE); + foreign->foreign_index = index; foreign->n_fields = (unsigned int) i; foreign->foreign_col_names = mem_heap_alloc(foreign->heap, @@ -3774,8 +3785,9 @@ try_find_index: foreign->referenced_index = index; foreign->referenced_table = referenced_table; - foreign->referenced_table_name - = mem_heap_strdup(foreign->heap, referenced_table_name); + foreign->referenced_table_name = mem_heap_strdup( + foreign->heap, referenced_table_name); + dict_mem_referenced_table_name_lookup_set(foreign, TRUE); foreign->referenced_col_names = mem_heap_alloc(foreign->heap, i * sizeof(void*)); @@ -4586,8 +4598,8 @@ dict_print_info_on_foreign_key_in_create_format( fputs(") REFERENCES ", file); - if (dict_tables_have_same_db(foreign->foreign_table_name, - foreign->referenced_table_name)) { + if (dict_tables_have_same_db(foreign->foreign_table_name_lookup, + foreign->referenced_table_name_lookup)) { /* Do not print the database name of the referenced table */ ut_print_name(file, trx, TRUE, dict_remove_db_name( diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index 5b01669af29..a5f60d8a2f7 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -40,6 +40,7 @@ Created 4/24/1996 Heikki Tuuri #include "rem0cmp.h" #include "srv0start.h" #include "srv0srv.h" +#include "ha_prototypes.h" /* innobase_casedn_str() */ /** Following are six InnoDB system tables */ @@ -435,6 +436,8 @@ dict_process_sys_fields_rec( return(err_msg); } + +#ifdef FOREIGN_NOT_USED /********************************************************************//** This function parses a SYS_FOREIGN record and populate a dict_foreign_t structure with the information from the record. For detail information @@ -483,13 +486,16 @@ err_len: } foreign->foreign_table_name = mem_heap_strdupl( heap, (const char*) field, len); + dict_mem_foreign_table_name_lookup_set(foreign, TRUE); field = rec_get_nth_field_old(rec, 4/*REF_NAME*/, &len); if (UNIV_UNLIKELY(len < 1 || len == UNIV_SQL_NULL)) { goto err_len; } + foreign->referenced_table_name = mem_heap_strdupl( heap, (const char*) field, len); + dict_mem_referenced_table_name_lookup_set(foreign, TRUE); field = rec_get_nth_field_old(rec, 5/*N_COLS*/, &len); if (UNIV_UNLIKELY(len != 4)) { @@ -502,6 +508,9 @@ err_len: return(NULL); } +#endif /* FOREIGN_NOT_USED */ + +#ifdef FOREIGN_NOT_USED /********************************************************************//** This function parses a SYS_FOREIGN_COLS record and extract necessary information from the record and return to caller. @@ -565,6 +574,8 @@ err_len: return(NULL); } +#endif /* FOREIGN_NOT_USED */ + /********************************************************************//** Determine the flags of a table described in SYS_TABLES. @return compressed page size in kilobytes; or 0 if the tablespace is @@ -2057,12 +2068,15 @@ dict_load_foreign( foreign->id = mem_heap_strdup(foreign->heap, id); field = rec_get_nth_field_old(rec, 3, &len); + foreign->foreign_table_name = mem_heap_strdupl( foreign->heap, (char*) field, len); + dict_mem_foreign_table_name_lookup_set(foreign, TRUE); field = rec_get_nth_field_old(rec, 4, &len); foreign->referenced_table_name = mem_heap_strdupl( foreign->heap, (char*) field, len); + dict_mem_referenced_table_name_lookup_set(foreign, TRUE); btr_pcur_close(&pcur); mtr_commit(&mtr); @@ -2070,7 +2084,7 @@ dict_load_foreign( dict_load_foreign_cols(id, foreign); ref_table = dict_table_check_if_in_cache_low( - foreign->referenced_table_name); + foreign->referenced_table_name_lookup); /* We could possibly wind up in a deep recursive calls if we call dict_table_get_low() again here if there @@ -2103,7 +2117,7 @@ dict_load_foreign( have to load it so that we are able to make type comparisons in the next function call. */ - for_table = dict_table_get_low(foreign->foreign_table_name); + for_table = dict_table_get_low(foreign->foreign_table_name_lookup); if (for_table && ref_table && check_recursive) { /* This is to record the longest chain of ancesters diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index bbb8f810f44..32b02e3180f 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -33,6 +33,7 @@ Created 1/8/1996 Heikki Tuuri #include "data0type.h" #include "mach0data.h" #include "dict0dict.h" +#include "srv0srv.h" /* srv_lower_case_table_names */ #ifndef UNIV_HOTBACKUP # include "lock0lock.h" #endif /* !UNIV_HOTBACKUP */ @@ -287,6 +288,60 @@ dict_mem_foreign_create(void) return(foreign); } +/**********************************************************************//** +Sets the foreign_table_name_lookup pointer based on the value of +srv_lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup +will point to foreign_table_name. If 2, then another string is allocated +of the heap and set to lower case. */ +UNIV_INLINE +void +dict_mem_foreign_table_name_lookup_set( +/*===================================*/ + dict_foreign_t* foreign, /*!< in/out: foreign struct */ + ibool do_alloc) /*!< in: is an alloc needed */ +{ + if (srv_lower_case_table_names == 2) { + if (do_alloc) { + foreign->foreign_table_name_lookup = mem_heap_alloc( + foreign->heap, + strlen(foreign->foreign_table_name) + 1); + } + strcpy(foreign->foreign_table_name_lookup, + foreign->foreign_table_name); + innobase_casedn_str(foreign->foreign_table_name_lookup); + } else { + foreign->foreign_table_name_lookup + = foreign->foreign_table_name; + } +} + +/**********************************************************************//** +Sets the referenced_table_name_lookup pointer based on the value of +srv_lower_case_table_names. If that is 0 or 1, +referenced_table_name_lookup will point to referenced_table_name. If 2, +then another string is allocated of the heap and set to lower case. */ +UNIV_INLINE +void +dict_mem_referenced_table_name_lookup_set( +/*======================================*/ + dict_foreign_t* foreign, /*!< in/out: foreign struct */ + ibool do_alloc) /*!< in: is an alloc needed */ +{ + if (srv_lower_case_table_names == 2) { + if (do_alloc) { + foreign->referenced_table_name_lookup = mem_heap_alloc( + foreign->heap, + strlen(foreign->referenced_table_name) + 1); + } + strcpy(foreign->referenced_table_name_lookup, + foreign->referenced_table_name); + innobase_casedn_str(foreign->referenced_table_name_lookup); + } else { + foreign->referenced_table_name_lookup + = foreign->referenced_table_name; + } +} + /**********************************************************************//** Adds a field definition to an index. NOTE: does not take a copy of the column name if the field is a column. The memory occupied diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 705ab6c8df3..5c7301d453c 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3639,6 +3639,7 @@ ha_innobase::open( UT_NOT_USED(test_if_locked); thd = ha_thd(); + srv_lower_case_table_names = lower_case_table_names; /* Under some cases MySQL seems to call this function while holding btr_search_latch. This breaks the latching order as @@ -6750,11 +6751,7 @@ ha_innobase::create( trx = innobase_trx_allocate(thd); - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } + srv_lower_case_table_names = lower_case_table_names; strcpy(name2, name); @@ -7179,11 +7176,7 @@ ha_innobase::delete_table( trx = innobase_trx_allocate(thd); - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } + srv_lower_case_table_names = lower_case_table_names; name_len = strlen(name); @@ -7306,11 +7299,7 @@ innobase_rename_table( char* norm_to; char* norm_from; - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } + srv_lower_case_table_names = lower_case_table_names; // Magic number 64 arbitrary norm_to = (char*) my_malloc(strlen(to) + 64, MYF(0)); diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index d448e57e395..7526e4dc8dc 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -238,6 +238,26 @@ dict_foreign_t* dict_mem_foreign_create(void); /*=========================*/ +/**********************************************************************//** +Sets the foreign_table_name_lookup pointer based on the value of +srv_lower_case_table_names. */ +UNIV_INTERN +void +dict_mem_foreign_table_name_lookup_set( +/*===================================*/ + dict_foreign_t* foreign, /*!< in/out: foreign struct */ + ibool do_alloc); /*!< in: is an alloc needed */ + +/**********************************************************************//** +Sets the reference_table_name_lookup pointer based on the value of +srv_lower_case_table_names. */ +UNIV_INTERN +void +dict_mem_referenced_table_name_lookup_set( +/*======================================*/ + dict_foreign_t* foreign, /*!< in/out: foreign struct */ + ibool do_alloc); /*!< in: is an alloc needed */ + /** Data structure for a column in a table */ struct dict_col_struct{ /*----------------------*/ @@ -393,10 +413,14 @@ struct dict_foreign_struct{ unsigned type:6; /*!< 0 or DICT_FOREIGN_ON_DELETE_CASCADE or DICT_FOREIGN_ON_DELETE_SET_NULL */ char* foreign_table_name;/*!< foreign table name */ + char* foreign_table_name_lookup; + /*!< foreign table name used for dict lookup */ dict_table_t* foreign_table; /*!< table where the foreign key is */ const char** foreign_col_names;/*!< names of the columns in the foreign key */ char* referenced_table_name;/*!< referenced table name */ + char* referenced_table_name_lookup; + /*!< referenced table name for dict lookup*/ dict_table_t* referenced_table;/*!< table where the referenced key is */ const char** referenced_col_names;/*!< names of the referenced diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 98b07f5e893..30e3648576a 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -71,8 +71,8 @@ at a time */ #define SRV_AUTO_EXTEND_INCREMENT \ (srv_auto_extend_increment * ((1024 * 1024) / UNIV_PAGE_SIZE)) -/* This is set to TRUE if the MySQL user has set it in MySQL */ -extern ibool srv_lower_case_table_names; +/* This is set to the MySQL server value for this variable. */ +extern uint srv_lower_case_table_names; /* Mutex for locking srv_monitor_file */ extern mutex_t srv_monitor_file_mutex; diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index 09ae80adff4..dbfba358f9f 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -1525,7 +1525,7 @@ row_ins_check_foreign_constraints( if (foreign->foreign_index == index) { if (foreign->referenced_table == NULL) { - dict_table_get(foreign->referenced_table_name, + dict_table_get(foreign->referenced_table_name_lookup, FALSE); } diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 709750fc4e7..69d93367297 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -3163,7 +3163,7 @@ check_next_foreign: if (foreign && trx->check_foreigns && !(drop_db && dict_tables_have_same_db( - name, foreign->foreign_table_name))) { + name, foreign->foreign_table_name_lookup))) { FILE* ef = dict_foreign_err_file; /* We only allow dropping a referenced table if diff --git a/storage/innobase/row/row0upd.c b/storage/innobase/row/row0upd.c index 81750938f58..45fa8538a87 100644 --- a/storage/innobase/row/row0upd.c +++ b/storage/innobase/row/row0upd.c @@ -238,7 +238,7 @@ row_upd_check_references_constraints( foreign->n_fields))) { if (foreign->foreign_table == NULL) { - dict_table_get(foreign->foreign_table_name, + dict_table_get(foreign->foreign_table_name_lookup, FALSE); } diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 0f6d6c95739..6e9fc6ab1c7 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -87,9 +87,11 @@ Created 10/8/1995 Heikki Tuuri #include "mysql/plugin.h" #include "mysql/service_thd_wait.h" -/* This is set to TRUE if the MySQL user has set it in MySQL; currently -affects only FOREIGN KEY definition parsing */ -UNIV_INTERN ibool srv_lower_case_table_names = FALSE; +/* This is set to the MySQL server value for this variable. It is only +needed for FOREIGN KEY definition parsing since FOREIGN KEY names are not +stored in the server metadata. The server stores and enforces it for +regular database and table names.*/ +UNIV_INTERN uint srv_lower_case_table_names = 0; /* The following counter is incremented whenever there is some user activity in the server */ From 6d1760be87285b16c5676cdd47c39d4d2abc7057 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 Nov 2010 20:13:02 -0600 Subject: [PATCH 15/52] Fix compiler warning for Bug#55222 patch. --- storage/innobase/dict/dict0mem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index 32b02e3180f..ab6285648a4 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -34,6 +34,7 @@ Created 1/8/1996 Heikki Tuuri #include "mach0data.h" #include "dict0dict.h" #include "srv0srv.h" /* srv_lower_case_table_names */ +#include "ha_prototypes.h" /* innobase_casedn_str()*/ #ifndef UNIV_HOTBACKUP # include "lock0lock.h" #endif /* !UNIV_HOTBACKUP */ From 469a81129959643050effb82f70fa71ff8f5bba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 1 Dec 2010 10:03:53 +0200 Subject: [PATCH 16/52] Bug#58623: Bogus debug assertion failure in i_s_locks_row_validate() This bogus assertion was introduced in the fix of Bug #57802: Empty ASSERTION parameter passed to the HASH_SEARCH macro. --- storage/innodb_plugin/trx/trx0i_s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innodb_plugin/trx/trx0i_s.c b/storage/innodb_plugin/trx/trx0i_s.c index a3ffc4ec015..3bf5ece9b9c 100644 --- a/storage/innodb_plugin/trx/trx0i_s.c +++ b/storage/innodb_plugin/trx/trx0i_s.c @@ -435,7 +435,7 @@ i_s_locks_row_validate( /* record lock */ ut_ad(!strcmp("RECORD", row->lock_type)); ut_ad(row->lock_index != NULL); - ut_ad(row->lock_data != NULL); + /* row->lock_data == NULL if buf_page_try_get() == NULL */ ut_ad(row->lock_page != ULINT_UNDEFINED); ut_ad(row->lock_rec != ULINT_UNDEFINED); } From 2a4aca3e6869d3d03538c0013832b70ebc093aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 1 Dec 2010 10:36:54 +0200 Subject: [PATCH 17/52] Fix a compilation error that was introduced in the Bug #55222 fix (bzr revision id kevin.lewis@oracle.com-20101130182552-hfydggaeeys3tjqx). --- storage/innobase/dict/dict0mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index ab6285648a4..8d3d78f3900 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -294,7 +294,7 @@ Sets the foreign_table_name_lookup pointer based on the value of srv_lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup will point to foreign_table_name. If 2, then another string is allocated of the heap and set to lower case. */ -UNIV_INLINE +UNIV_INTERN void dict_mem_foreign_table_name_lookup_set( /*===================================*/ @@ -321,7 +321,7 @@ Sets the referenced_table_name_lookup pointer based on the value of srv_lower_case_table_names. If that is 0 or 1, referenced_table_name_lookup will point to referenced_table_name. If 2, then another string is allocated of the heap and set to lower case. */ -UNIV_INLINE +UNIV_INTERN void dict_mem_referenced_table_name_lookup_set( /*======================================*/ From d6bac7db8e586b6c2f3224a8271ebbabc8e5524e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 1 Dec 2010 10:43:33 +0200 Subject: [PATCH 18/52] Bug#58226 Some InnoDB debug checks consume too much CPU time Do not disable InnoDB inlining when UNIV_DEBUG is defined. The inlining is now solely controlled by the preprocessor symbol UNIV_MUST_NOT_INLINE and by any compiler options. mtr_memo_contains(): Add an explicit type conversion from void*, so that the function can be compiled by a C++ compiler. Previously, this function was never seen by the C++ compiler, because it is only present in UNIV_DEBUG builds and InnoDB inlining used to be disabled. buf_flush_validate_skip(): A wrapper that skips most calls of buf_flush_validate_low(). Invoked by debug assertions in buf_flush_insert_into_flush_list() and buf_flush_remove(). fil_validate_skip(): A wrapper that skips most calls of fil_validate(). Invoked by debug assertions in fil_io() and fil_io_wait(). os_aio_validate_skip(): A wrapper that skips most calls of os_aio_validate(). Invoked by debug assertions in os_aio_func(), os_aio_windows_handle() and os_aio_simulated_handle. os_get_os_version(): Only include this function if __WIN__ is defined. sync_array_deadlock_step(): Slight optimizations. This function is a major CPU consumer in UNIV_SYNC_DEBUG builds. --- storage/innobase/buf/buf0flu.c | 32 +++++++++++++++++++-- storage/innobase/fil/fil0fil.c | 36 +++++++++++++++++++++--- storage/innobase/include/mtr0mtr.ic | 2 +- storage/innobase/include/os0file.h | 2 ++ storage/innobase/include/univ.i | 2 +- storage/innobase/os/os0file.c | 43 +++++++++++++++++++++++------ storage/innobase/sync/sync0arr.c | 11 ++------ 7 files changed, 102 insertions(+), 26 deletions(-) diff --git a/storage/innobase/buf/buf0flu.c b/storage/innobase/buf/buf0flu.c index 5b0617f17b1..07a32e55f97 100644 --- a/storage/innobase/buf/buf0flu.c +++ b/storage/innobase/buf/buf0flu.c @@ -88,6 +88,34 @@ ibool buf_flush_validate_low( /*===================*/ buf_pool_t* buf_pool); /*!< in: Buffer pool instance */ + +/******************************************************************//** +Validates the flush list some of the time. +@return TRUE if ok or the check was skipped */ +static +ibool +buf_flush_validate_skip( +/*====================*/ + buf_pool_t* buf_pool) /*!< in: Buffer pool instance */ +{ +/** Try buf_flush_validate_low() every this many times */ +# define BUF_FLUSH_VALIDATE_SKIP 23 + + /** The buf_flush_validate_low() call skip counter. + Use a signed type because of the race condition below. */ + static int buf_flush_validate_count = BUF_FLUSH_VALIDATE_SKIP; + + /* There is a race condition below, but it does not matter, + because this call is only for heuristic purposes. We want to + reduce the call frequency of the costly buf_flush_validate_low() + check in debug builds. */ + if (--buf_flush_validate_count > 0) { + return(TRUE); + } + + buf_flush_validate_count = BUF_FLUSH_VALIDATE_SKIP; + return(buf_flush_validate_low(buf_pool)); +} #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ /******************************************************************//** @@ -293,7 +321,7 @@ buf_flush_insert_into_flush_list( } #endif /* UNIV_DEBUG_VALGRIND */ #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG - ut_a(buf_flush_validate_low(buf_pool)); + ut_a(buf_flush_validate_skip(buf_pool)); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ buf_flush_list_mutex_exit(buf_pool); @@ -515,7 +543,7 @@ buf_flush_remove( bpage->oldest_modification = 0; #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG - ut_a(buf_flush_validate_low(buf_pool)); + ut_a(buf_flush_validate_skip(buf_pool)); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ buf_flush_list_mutex_exit(buf_pool); diff --git a/storage/innobase/fil/fil0fil.c b/storage/innobase/fil/fil0fil.c index cb4ccc005b5..d940bc70609 100644 --- a/storage/innobase/fil/fil0fil.c +++ b/storage/innobase/fil/fil0fil.c @@ -299,6 +299,34 @@ struct fil_system_struct { initialized. */ static fil_system_t* fil_system = NULL; +#ifdef UNIV_DEBUG +/** Try fil_validate() every this many times */ +# define FIL_VALIDATE_SKIP 17 + +/******************************************************************//** +Checks the consistency of the tablespace cache some of the time. +@return TRUE if ok or the check was skipped */ +static +ibool +fil_validate_skip(void) +/*===================*/ +{ + /** The fil_validate() call skip counter. Use a signed type + because of the race condition below. */ + static int fil_validate_count = FIL_VALIDATE_SKIP; + + /* There is a race condition below, but it does not matter, + because this call is only for heuristic purposes. We want to + reduce the call frequency of the costly fil_validate() check + in debug builds. */ + if (--fil_validate_count > 0) { + return(TRUE); + } + + fil_validate_count = FIL_VALIDATE_SKIP; + return(fil_validate()); +} +#endif /* UNIV_DEBUG */ /********************************************************************//** NOTE: you must call fil_mutex_enter_and_prepare_for_io() first! @@ -4307,7 +4335,7 @@ fil_io( #if (1 << UNIV_PAGE_SIZE_SHIFT) != UNIV_PAGE_SIZE # error "(1 << UNIV_PAGE_SIZE_SHIFT) != UNIV_PAGE_SIZE" #endif - ut_ad(fil_validate()); + ut_ad(fil_validate_skip()); #ifndef UNIV_HOTBACKUP # ifndef UNIV_LOG_DEBUG /* ibuf bitmap pages must be read in the sync aio mode: */ @@ -4466,7 +4494,7 @@ fil_io( mutex_exit(&fil_system->mutex); - ut_ad(fil_validate()); + ut_ad(fil_validate_skip()); } return(DB_SUCCESS); @@ -4490,7 +4518,7 @@ fil_aio_wait( void* message; ulint type; - ut_ad(fil_validate()); + ut_ad(fil_validate_skip()); if (srv_use_native_aio) { srv_set_io_thread_op_info(segment, "native aio handle"); @@ -4521,7 +4549,7 @@ fil_aio_wait( mutex_exit(&fil_system->mutex); - ut_ad(fil_validate()); + ut_ad(fil_validate_skip()); /* Do the i/o handling */ /* IMPORTANT: since i/o handling for reads will read also the insert diff --git a/storage/innobase/include/mtr0mtr.ic b/storage/innobase/include/mtr0mtr.ic index 18f8e87b3cf..55f3cf7f147 100644 --- a/storage/innobase/include/mtr0mtr.ic +++ b/storage/innobase/include/mtr0mtr.ic @@ -160,7 +160,7 @@ mtr_memo_contains( while (offset > 0) { offset -= sizeof(mtr_memo_slot_t); - slot = dyn_array_get_element(memo, offset); + slot = (mtr_memo_slot_t*) dyn_array_get_element(memo, offset); if ((object == slot->object) && (type == slot->type)) { diff --git a/storage/innobase/include/os0file.h b/storage/innobase/include/os0file.h index 6d95b280330..fb13120a481 100644 --- a/storage/innobase/include/os0file.h +++ b/storage/innobase/include/os0file.h @@ -373,6 +373,7 @@ typedef HANDLE os_file_dir_t; /*!< directory stream */ typedef DIR* os_file_dir_t; /*!< directory stream */ #endif +#ifdef __WIN__ /***********************************************************************//** Gets the operating system version. Currently works only on Windows. @return OS_WIN95, OS_WIN31, OS_WINNT, OS_WIN2000, OS_WINXP, OS_WINVISTA, @@ -381,6 +382,7 @@ UNIV_INTERN ulint os_get_os_version(void); /*===================*/ +#endif /* __WIN__ */ #ifndef UNIV_HOTBACKUP /****************************************************************//** Creates the seek mutexes used in positioned reads and writes. */ diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index aa208134a51..84d81d51ec0 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -253,7 +253,7 @@ easy way to get it to work. See http://bugs.mysql.com/bug.php?id=52263. */ # define UNIV_INTERN #endif -#if (!defined(UNIV_DEBUG) && !defined(UNIV_MUST_NOT_INLINE)) +#ifndef UNIV_MUST_NOT_INLINE /* Definition for inline version */ #ifdef __WIN__ diff --git a/storage/innobase/os/os0file.c b/storage/innobase/os/os0file.c index 93d2f72746d..74dbac3bc96 100644 --- a/storage/innobase/os/os0file.c +++ b/storage/innobase/os/os0file.c @@ -302,6 +302,36 @@ UNIV_INTERN ulint os_n_pending_writes = 0; /** Number of pending read operations */ UNIV_INTERN ulint os_n_pending_reads = 0; +#ifdef UNIV_DEBUG +/**********************************************************************//** +Validates the consistency the aio system some of the time. +@return TRUE if ok or the check was skipped */ +UNIV_INTERN +ibool +os_aio_validate_skip(void) +/*======================*/ +{ +/** Try os_aio_validate() every this many times */ +# define OS_AIO_VALIDATE_SKIP 13 + + /** The os_aio_validate() call skip counter. + Use a signed type because of the race condition below. */ + static int os_aio_validate_count = OS_AIO_VALIDATE_SKIP; + + /* There is a race condition below, but it does not matter, + because this call is only for heuristic purposes. We want to + reduce the call frequency of the costly os_aio_validate() + check in debug builds. */ + if (--os_aio_validate_count > 0) { + return(TRUE); + } + + os_aio_validate_count = OS_AIO_VALIDATE_SKIP; + return(os_aio_validate()); +} +#endif /* UNIV_DEBUG */ + +#ifdef __WIN__ /***********************************************************************//** Gets the operating system version. Currently works only on Windows. @return OS_WIN95, OS_WIN31, OS_WINNT, OS_WIN2000, OS_WINXP, OS_WINVISTA, @@ -311,7 +341,6 @@ ulint os_get_os_version(void) /*===================*/ { -#ifdef __WIN__ OSVERSIONINFO os_info; os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); @@ -340,12 +369,8 @@ os_get_os_version(void) ut_error; return(0); } -#else - ut_error; - - return(0); -#endif } +#endif /* __WIN__ */ /***********************************************************************//** Retrieves the last error number if an error occurs in a file io function. @@ -4008,7 +4033,7 @@ os_aio_func( ut_ad(n > 0); ut_ad(n % OS_FILE_LOG_BLOCK_SIZE == 0); ut_ad(offset % OS_FILE_LOG_BLOCK_SIZE == 0); - ut_ad(os_aio_validate()); + ut_ad(os_aio_validate_skip()); #ifdef WIN_ASYNC_IO ut_ad((n & 0xFFFFFFFFUL) == n); #endif @@ -4210,7 +4235,7 @@ os_aio_windows_handle( /* NOTE! We only access constant fields in os_aio_array. Therefore we do not have to acquire the protecting mutex yet */ - ut_ad(os_aio_validate()); + ut_ad(os_aio_validate_skip()); ut_ad(segment < array->n_segments); n = array->n_slots / array->n_segments; @@ -4630,7 +4655,7 @@ restart: srv_set_io_thread_op_info(global_segment, "looking for i/o requests (a)"); - ut_ad(os_aio_validate()); + ut_ad(os_aio_validate_skip()); ut_ad(segment < array->n_segments); n = array->n_slots / array->n_segments; diff --git a/storage/innobase/sync/sync0arr.c b/storage/innobase/sync/sync0arr.c index 753ebd958ac..b7e8206b575 100644 --- a/storage/innobase/sync/sync0arr.c +++ b/storage/innobase/sync/sync0arr.c @@ -590,9 +590,6 @@ sync_array_deadlock_step( ulint depth) /*!< in: recursion depth */ { sync_cell_t* new; - ibool ret; - - depth++; if (pass != 0) { /* If pass != 0, then we do not know which threads are @@ -604,7 +601,7 @@ sync_array_deadlock_step( new = sync_array_find_thread(arr, thread); - if (new == start) { + if (UNIV_UNLIKELY(new == start)) { /* Stop running of other threads */ ut_dbg_stop_threads = TRUE; @@ -616,11 +613,7 @@ sync_array_deadlock_step( return(TRUE); } else if (new) { - ret = sync_array_detect_deadlock(arr, start, new, depth); - - if (ret) { - return(TRUE); - } + return(sync_array_detect_deadlock(arr, start, new, depth + 1)); } return(FALSE); } From 9bfb5ece4bd9701a542c0ac5dd61df41d664dc8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 1 Dec 2010 15:09:02 +0200 Subject: [PATCH 19/52] Bug#58212 Possible deadlock in change buffer in debug builds ibuf_page(): Renamed to ibuf_page_low(). Add the parameters file, line so that the latch diagnostics will be more meaningful. In debug builds, add the parameter ibool x_latch. When x_latch=FALSE, do not x-latch the page, but only buffer-fix it for reading the bit. In UNIV_SYNC_DEBUG, display a message if an insert buffer bitmap page was already latched. (The message should be displayed in those cases where the code would have previously failed.) ibuf_page(): A wrapper macro for ibuf_page_low(). Pass x_latch=TRUE. ibuf_bitmap_page_get_bits(): Renamed to ibuf_bitmap_page_get_bits_low(). In UNIV_DEBUG, add the parameter latch_mode. Remove the parameter mtr unless UNIV_DEBUG is defined. ibuf_bitmap_page_get_bits(): A wrapper macro for ibuf_bitmap_page_get_bits_low(). Pass latch_type=MTR_MEMO_PAGE_X_FIX. buf_page_get_gen(): Use ibuf_page_low(x_latch=FALSE) in the debug assertion. This avoids the possible deadlock. --- storage/innobase/buf/buf0buf.c | 3 +- storage/innobase/ibuf/ibuf0ibuf.c | 112 +++++++++++++++++++++++---- storage/innobase/include/ibuf0ibuf.h | 47 ++++++++--- 3 files changed, 135 insertions(+), 27 deletions(-) diff --git a/storage/innobase/buf/buf0buf.c b/storage/innobase/buf/buf0buf.c index 49858406101..a05c03a9f67 100644 --- a/storage/innobase/buf/buf0buf.c +++ b/storage/innobase/buf/buf0buf.c @@ -2745,7 +2745,8 @@ buf_page_get_gen( ut_ad(zip_size == fil_space_get_zip_size(space)); ut_ad(ut_is_2pow(zip_size)); #ifndef UNIV_LOG_DEBUG - ut_ad(!ibuf_inside() || ibuf_page(space, zip_size, offset, NULL)); + ut_ad(!ibuf_inside() || ibuf_page_low(space, zip_size, offset, + FALSE, file, line, NULL)); #endif buf_pool->stat.n_page_gets++; fold = buf_page_address_fold(space, offset); diff --git a/storage/innobase/ibuf/ibuf0ibuf.c b/storage/innobase/ibuf/ibuf0ibuf.c index ab42f1ad4f3..0cec0318bf4 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.c +++ b/storage/innobase/ibuf/ibuf0ibuf.c @@ -656,22 +656,49 @@ ibuf_parse_bitmap_init( return(ptr); } #ifndef UNIV_HOTBACKUP +# ifdef UNIV_DEBUG +/** Gets the desired bits for a given page from a bitmap page. +@param page in: bitmap page +@param offset in: page whose bits to get +@param zs in: compressed page size in bytes; 0 for uncompressed pages +@param bit in: IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ... +@param mtr in: mini-transaction holding an x-latch on the bitmap page +@return value of bits */ +# define ibuf_bitmap_page_get_bits(page, offset, zs, bit, mtr) \ + ibuf_bitmap_page_get_bits_low(page, offset, zs, \ + MTR_MEMO_PAGE_X_FIX, mtr, bit) +# else /* UNIV_DEBUG */ +/** Gets the desired bits for a given page from a bitmap page. +@param page in: bitmap page +@param offset in: page whose bits to get +@param zs in: compressed page size in bytes; 0 for uncompressed pages +@param bit in: IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ... +@param mtr in: mini-transaction holding an x-latch on the bitmap page +@return value of bits */ +# define ibuf_bitmap_page_get_bits(page, offset, zs, bit, mtr) \ + ibuf_bitmap_page_get_bits_low(page, offset, zs, bit) +# endif /* UNIV_DEBUG */ + /********************************************************************//** Gets the desired bits for a given page from a bitmap page. @return value of bits */ UNIV_INLINE ulint -ibuf_bitmap_page_get_bits( -/*======================*/ +ibuf_bitmap_page_get_bits_low( +/*==========================*/ const page_t* page, /*!< in: bitmap page */ ulint page_no,/*!< in: page whose bits to get */ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ - ulint bit, /*!< in: IBUF_BITMAP_FREE, +#ifdef UNIV_DEBUG + ulint latch_type, + /*!< in: MTR_MEMO_PAGE_X_FIX, + MTR_MEMO_BUF_FIX, ... */ + mtr_t* mtr, /*!< in: mini-transaction holding latch_type + on the bitmap page */ +#endif /* UNIV_DEBUG */ + ulint bit) /*!< in: IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ... */ - mtr_t* mtr __attribute__((unused))) - /*!< in: mtr containing an - x-latch to the bitmap page */ { ulint byte_offset; ulint bit_offset; @@ -683,7 +710,7 @@ ibuf_bitmap_page_get_bits( # error "IBUF_BITS_PER_PAGE % 2 != 0" #endif ut_ad(ut_is_2pow(zip_size)); - ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); + ut_ad(mtr_memo_contains_page(mtr, page, latch_type)); if (!zip_size) { bit_offset = (page_no % UNIV_PAGE_SIZE) * IBUF_BITS_PER_PAGE @@ -1109,21 +1136,29 @@ Must not be called when recv_no_ibuf_operations==TRUE. @return TRUE if level 2 or level 3 page */ UNIV_INTERN ibool -ibuf_page( -/*======*/ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint page_no,/*!< in: page number */ - mtr_t* mtr) /*!< in: mtr which will contain an x-latch to the - bitmap page if the page is not one of the fixed - address ibuf pages, or NULL, in which case a new - transaction is created. */ +ibuf_page_low( +/*==========*/ + ulint space, /*!< in: space id */ + ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ + ulint page_no,/*!< in: page number */ +#ifdef UNIV_DEBUG + ibool x_latch,/*!< in: FALSE if relaxed check + (avoid latching the bitmap page) */ +#endif /* UNIV_DEBUG */ + const char* file, /*!< in: file name */ + ulint line, /*!< in: line where called */ + mtr_t* mtr) /*!< in: mtr which will contain an + x-latch to the bitmap page if the page + is not one of the fixed address ibuf + pages, or NULL, in which case a new + transaction is created. */ { ibool ret; mtr_t local_mtr; page_t* bitmap_page; ut_ad(!recv_no_ibuf_operations); + ut_ad(x_latch || mtr == NULL); if (ibuf_fixed_addr_page(space, zip_size, page_no)) { @@ -1135,12 +1170,55 @@ ibuf_page( ut_ad(fil_space_get_type(IBUF_SPACE_ID) == FIL_TABLESPACE); +#ifdef UNIV_DEBUG + if (!x_latch) { + mtr_start(&local_mtr); + + /* Get the bitmap page without a page latch, so that + we will not be violating the latching order when + another bitmap page has already been latched by this + thread. The page will be buffer-fixed, and thus it + cannot be removed or relocated while we are looking at + it. The contents of the page could change, but the + IBUF_BITMAP_IBUF bit that we are interested in should + not be modified by any other thread. Nobody should be + calling ibuf_add_free_page() or ibuf_remove_free_page() + while the page is linked to the insert buffer b-tree. */ + + bitmap_page = buf_block_get_frame( + buf_page_get_gen( + space, zip_size, + ibuf_bitmap_page_no_calc(zip_size, page_no), + RW_NO_LATCH, NULL, BUF_GET_NO_LATCH, + file, line, &local_mtr)); +# ifdef UNIV_SYNC_DEBUG + /* This is for tracking Bug #58212. This check and message can + be removed once it has been established that our assumptions + about this condition are correct. The bug was only a one-time + occurrence, unable to repeat since then. */ + void* latch = sync_thread_levels_contains(SYNC_IBUF_BITMAP); + if (latch) { + fprintf(stderr, "Bug#58212 UNIV_SYNC_DEBUG" + " levels %p (%u,%u)\n", + latch, (unsigned) space, (unsigned) page_no); + } +# endif /* UNIV_SYNC_DEBUG */ + ret = ibuf_bitmap_page_get_bits_low( + bitmap_page, page_no, zip_size, + MTR_MEMO_BUF_FIX, &local_mtr, IBUF_BITMAP_IBUF); + + mtr_commit(&local_mtr); + return(ret); + } +#endif /* UNIV_DEBUG */ + if (mtr == NULL) { mtr = &local_mtr; mtr_start(mtr); } - bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, mtr); + bitmap_page = ibuf_bitmap_get_map_page_func(space, page_no, zip_size, + file, line, mtr); ret = ibuf_bitmap_page_get_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_IBUF, mtr); diff --git a/storage/innobase/include/ibuf0ibuf.h b/storage/innobase/include/ibuf0ibuf.h index dd05bcb0608..330efae780c 100644 --- a/storage/innobase/include/ibuf0ibuf.h +++ b/storage/innobase/include/ibuf0ibuf.h @@ -244,15 +244,44 @@ Must not be called when recv_no_ibuf_operations==TRUE. @return TRUE if level 2 or level 3 page */ UNIV_INTERN ibool -ibuf_page( -/*======*/ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint page_no,/*!< in: page number */ - mtr_t* mtr); /*!< in: mtr which will contain an x-latch to the - bitmap page if the page is not one of the fixed - address ibuf pages, or NULL, in which case a new - transaction is created. */ +ibuf_page_low( +/*==========*/ + ulint space, /*!< in: space id */ + ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ + ulint page_no,/*!< in: page number */ +#ifdef UNIV_DEBUG + ibool x_latch,/*!< in: FALSE if relaxed check + (avoid latching the bitmap page) */ +#endif /* UNIV_DEBUG */ + const char* file, /*!< in: file name */ + ulint line, /*!< in: line where called */ + mtr_t* mtr) /*!< in: mtr which will contain an + x-latch to the bitmap page if the page + is not one of the fixed address ibuf + pages, or NULL, in which case a new + transaction is created. */ + __attribute__((warn_unused_result)); +#ifdef UNIV_DEBUG +/** Checks if a page is a level 2 or 3 page in the ibuf hierarchy of +pages. Must not be called when recv_no_ibuf_operations==TRUE. +@param space tablespace identifier +@param zip_size compressed page size in bytes, or 0 +@param page_no page number +@param mtr mini-transaction or NULL +@return TRUE if level 2 or level 3 page */ +# define ibuf_page(space, zip_size, page_no, mtr) \ + ibuf_page_low(space, zip_size, page_no, TRUE, __FILE__, __LINE__, mtr) +#else /* UVIV_DEBUG */ +/** Checks if a page is a level 2 or 3 page in the ibuf hierarchy of +pages. Must not be called when recv_no_ibuf_operations==TRUE. +@param space tablespace identifier +@param zip_size compressed page size in bytes, or 0 +@param page_no page number +@param mtr mini-transaction or NULL +@return TRUE if level 2 or level 3 page */ +# define ibuf_page(space, zip_size, page_no, mtr) \ + ibuf_page_low(space, zip_size, page_no, __FILE__, __LINE__, mtr) +#endif /* UVIV_DEBUG */ /***********************************************************************//** Frees excess pages from the ibuf free list. This function is called when an OS thread calls fsp services to allocate a new file segment, or a new page to a From 789425e0b49eefff126442083d2f309c64729f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 2 Dec 2010 10:32:27 +0200 Subject: [PATCH 20/52] Fix UNIV_MEM_DEBUG compilation failure caused by Bug#58226 fix. To fix Bug#58226 (speed up UNIV_DEBUG), among other things we no longer disable the inlining of the functions that are defined in InnoDB .ic files. Inside UNIV_MEM_DEBUG, there was an implicit type conversion from void* that is all right in C, but not in C++. Now that inlining was enabled, the C++ compiler would see the code and complain. Approved by Jimmy Yang on IRC. --- storage/innobase/include/mem0mem.ic | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/storage/innobase/include/mem0mem.ic b/storage/innobase/include/mem0mem.ic index cbce2edc661..d214c3fe6c9 100644 --- a/storage/innobase/include/mem0mem.ic +++ b/storage/innobase/include/mem0mem.ic @@ -350,27 +350,27 @@ mem_heap_get_top( ulint n) /*!< in: size of the topmost element */ { mem_block_t* block; - void* buf; + byte* buf; ut_ad(mem_heap_check(heap)); block = UT_LIST_GET_LAST(heap->base); - buf = (byte*)block + mem_block_get_free(block) - MEM_SPACE_NEEDED(n); + buf = (byte*) block + mem_block_get_free(block) - MEM_SPACE_NEEDED(n); #ifdef UNIV_MEM_DEBUG - ut_ad(mem_block_get_start(block) <=(ulint)((byte*)buf - (byte*)block)); + ut_ad(mem_block_get_start(block) <= (ulint) (buf - (byte*) block)); /* In the debug version, advance buf to point at the storage which was given to the caller in the allocation*/ - buf = (byte*)buf + MEM_FIELD_HEADER_SIZE; + buf += MEM_FIELD_HEADER_SIZE; /* Check that the field lengths agree */ - ut_ad(n == (ulint)mem_field_header_get_len(buf)); + ut_ad(n == mem_field_header_get_len(buf)); #endif - return(buf); + return((void*) buf); } /*****************************************************************//** From 691bf28a5609bb2070c468069fbbd51390c009dd Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 2 Dec 2010 10:36:45 +0200 Subject: [PATCH 21/52] Use "rep; nop" in InnoDB if it is available and "pause" is not --- storage/innobase/include/ut0ut.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/innobase/include/ut0ut.h b/storage/innobase/include/ut0ut.h index 5c7859a94f5..cd5c7ca99f1 100644 --- a/storage/innobase/include/ut0ut.h +++ b/storage/innobase/include/ut0ut.h @@ -61,6 +61,8 @@ typedef time_t ib_time_t; Also asm volatile may trigger a memory barrier (spilling all registers to memory). */ # define UT_RELAX_CPU() __asm__ __volatile__ ("pause") +#elif defined(HAVE_FAKE_PAUSE_INSTRUCTION) +# define UT_RELAX_CPU() __asm__ __volatile__ ("rep; nop") #elif defined(HAVE_ATOMIC_BUILTINS) # define UT_RELAX_CPU() do { \ volatile lint volatile_var; \ From 8903bae21abe2b6747f52fd074aff8d9e604ee5a Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 2 Dec 2010 12:04:10 +0200 Subject: [PATCH 22/52] Define MYSQL_(MAJOR|MINOR)_VERSION the way things are defined in CMake instead of hacking a custom ADD_DEFINITIONS(). Approved by: Kent Boortz (via IRC) --- config.h.cmake | 3 +++ storage/innobase/CMakeLists.txt | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/config.h.cmake b/config.h.cmake index 54567df4548..de1f0a65db7 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -604,6 +604,9 @@ #cmakedefine SO_EXT "@CMAKE_SHARED_MODULE_SUFFIX@" +#define MYSQL_MAJOR_VERSION @MAJOR_VERSION@ +#define MYSQL_MINOR_VERSION @MINOR_VERSION@ + #define PACKAGE "mysql" #define PACKAGE_BUGREPORT "" #define PACKAGE_NAME "MySQL Server" diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 76967cd06d2..4bbda0d2477 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -19,10 +19,6 @@ INCLUDE(CheckFunctionExists) INCLUDE(CheckCSourceCompiles) INCLUDE(CheckCSourceRuns) -# Make MySQL version available to InnoDB -ADD_DEFINITIONS("-DMYSQL_MAJOR_VERSION=${MAJOR_VERSION}") -ADD_DEFINITIONS("-DMYSQL_MINOR_VERSION=${MINOR_VERSION}") - # OS tests IF(UNIX) IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") From efcaa1ef7f8b67a625070f4b34cb78912da3d378 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 3 Dec 2010 09:35:53 +0200 Subject: [PATCH 23/52] Cherry pick vasil.dimov@oracle.com-20101201140708-fsc5xlu9bkpov6pv from mysql-trunk-innodb. The same problem also exists in 5.5. X revision-id: vasil.dimov@oracle.com-20101201140708-fsc5xlu9bkpov6pv X committer: Vasil Dimov X branch nick: mysql-trunk-innodb X timestamp: Wed 2010-12-01 16:07:08 +0200 X message: X Fix Bug#58432 innodb.innodb_bug56143 fails under valgrind X X Use a longer timeout for semaphore waits if (possibly) running under X Valgrind. X X Approved by: Marko (via IRC) --- storage/innobase/sync/sync0arr.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/storage/innobase/sync/sync0arr.c b/storage/innobase/sync/sync0arr.c index b7e8206b575..ce9d42c4921 100644 --- a/storage/innobase/sync/sync0arr.c +++ b/storage/innobase/sync/sync0arr.c @@ -923,12 +923,25 @@ sync_array_print_long_waits(void) ulint fatal_timeout = srv_fatal_semaphore_wait_threshold; ibool fatal = FALSE; +#ifdef UNIV_DEBUG_VALGRIND + /* Increase the timeouts if running under valgrind because it executes + extremely slowly. UNIV_DEBUG_VALGRIND does not necessary mean that + we are running under valgrind but we have no better way to tell. + See Bug#58432 innodb.innodb_bug56143 fails under valgrind + for an example */ +# define SYNC_ARRAY_TIMEOUT 2400 + fatal_timeout *= 10; +#else +# define SYNC_ARRAY_TIMEOUT 240 +#endif + for (i = 0; i < sync_primary_wait_array->n_cells; i++) { cell = sync_array_get_nth_cell(sync_primary_wait_array, i); if (cell->wait_object != NULL && cell->waiting - && difftime(time(NULL), cell->reservation_time) > 240) { + && difftime(time(NULL), cell->reservation_time) + > SYNC_ARRAY_TIMEOUT) { fputs("InnoDB: Warning: a long semaphore wait:\n", stderr); sync_array_cell_print(stderr, cell); @@ -970,6 +983,8 @@ sync_array_print_long_waits(void) " to the standard error stream\n"); } +#undef SYNC_ARRAY_TIMEOUT + return(fatal); } From 662fb84a42695ddf64468d7990e7bd830165d6c2 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 9 Dec 2010 01:19:46 -0800 Subject: [PATCH 24/52] Fix Bug #57600 output of I/O sum[%lu] can go negative rb://532 approved by Sunny Bains --- storage/innodb_plugin/ChangeLog | 4 ++++ storage/innodb_plugin/buf/buf0lru.c | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 5ca60eb73d5..347dc9a5183 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,7 @@ +2010-12-09 The InnoDB Team + * buf/buf0lru.c: + Fix Bug#57600 output of I/O sum[%lu] can go negative + 2010-11-11 The InnoDB Team * thr/thr0loc.c, trx/trx0i_s.c: Fix Bug#57802 Empty ASSERTION parameter passed to the HASH_SEARCH macro diff --git a/storage/innodb_plugin/buf/buf0lru.c b/storage/innodb_plugin/buf/buf0lru.c index 78d8d348e2a..e4cf218bf2e 100644 --- a/storage/innodb_plugin/buf/buf0lru.c +++ b/storage/innodb_plugin/buf/buf0lru.c @@ -1942,6 +1942,7 @@ buf_LRU_stat_update(void) /*=====================*/ { buf_LRU_stat_t* item; + buf_LRU_stat_t cur_stat; /* If we haven't started eviction yet then don't update stats. */ if (buf_pool->freed_page_clock == 0) { @@ -1955,12 +1956,19 @@ buf_LRU_stat_update(void) buf_LRU_stat_arr_ind++; buf_LRU_stat_arr_ind %= BUF_LRU_STAT_N_INTERVAL; - /* Add the current value and subtract the obsolete entry. */ - buf_LRU_stat_sum.io += buf_LRU_stat_cur.io - item->io; - buf_LRU_stat_sum.unzip += buf_LRU_stat_cur.unzip - item->unzip; + /* Add the current value and subtract the obsolete entry. + Since buf_LRU_stat_cur is not protected by any mutex, + it can be changing between adding to buf_LRU_stat_sum + and copying to item. Assign it to local variables to make + sure the same value assign to the buf_LRU_stat_sum + and item */ + cur_stat = buf_LRU_stat_cur; + + buf_LRU_stat_sum.io += cur_stat.io - item->io; + buf_LRU_stat_sum.unzip += cur_stat.unzip - item->unzip; /* Put current entry in the array. */ - memcpy(item, &buf_LRU_stat_cur, sizeof *item); + memcpy(item, &cur_stat, sizeof *item); buf_pool_mutex_exit(); From 5f80beebd0a6df7d21c791a85a215df453eeb0ef Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2010 10:42:35 -0600 Subject: [PATCH 25/52] 55222 - Previous patch had a bug in unused code which was reactivated in mysql-trunk-innodb in rev revno: 3367 Tue 2010-12-07 02:25:25-0800. The crash happens only when lower_case_table_names=2, such as on MacOS, when running the new testcase innodb-system-table-view. Specifically, it crashes when any query is made against the INFORMATION_SCHEMA. INNODB_SYS_FOREIGN table. The function dict_process_sys_foreign_rec() is only used for displaying SYS_FOREIGN records so it does not need a lookup version of those names to be allocated. In this patch, those new function calls are deleted. --- storage/innobase/dict/dict0load.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index a5f60d8a2f7..3252e85b358 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -441,7 +441,7 @@ dict_process_sys_fields_rec( /********************************************************************//** This function parses a SYS_FOREIGN record and populate a dict_foreign_t structure with the information from the record. For detail information -about SYS_FOREIGN fields, please refer to dict_load_foreign() function +about SYS_FOREIGN fields, please refer to dict_load_foreign() function. @return error message, or NULL on success */ UNIV_INTERN const char* @@ -469,6 +469,11 @@ dict_process_sys_foreign_rec( err_len: return("incorrect column length in SYS_FOREIGN"); } + + /* This recieves a dict_foreign_t* that points to a stack variable. + So mem_heap_free(foreign->heap) is not used as elsewhere. + Since the heap used here is freed elsewhere, foreign->heap + is not assigned. */ foreign->id = mem_heap_strdupl(heap, (const char*) field, len); rec_get_nth_field_offs_old(rec, 1/*DB_TRX_ID*/, &len); @@ -480,22 +485,22 @@ err_len: goto err_len; } + /* The _lookup versions of the referenced and foreign table names + are not assigned since they are not used in this dict_foreign_t */ + field = rec_get_nth_field_old(rec, 3/*FOR_NAME*/, &len); if (UNIV_UNLIKELY(len < 1 || len == UNIV_SQL_NULL)) { goto err_len; } foreign->foreign_table_name = mem_heap_strdupl( heap, (const char*) field, len); - dict_mem_foreign_table_name_lookup_set(foreign, TRUE); field = rec_get_nth_field_old(rec, 4/*REF_NAME*/, &len); if (UNIV_UNLIKELY(len < 1 || len == UNIV_SQL_NULL)) { goto err_len; } - foreign->referenced_table_name = mem_heap_strdupl( heap, (const char*) field, len); - dict_mem_referenced_table_name_lookup_set(foreign, TRUE); field = rec_get_nth_field_old(rec, 5/*N_COLS*/, &len); if (UNIV_UNLIKELY(len != 4)) { From e988de279fc0102293844f87a29727910dd477ab Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Tue, 14 Dec 2010 11:38:19 +0200 Subject: [PATCH 26/52] Speed up innodb_bug57255.test Submitted by: Stewart Smith (via internals@lists.mysql.com) --- mysql-test/suite/innodb/t/innodb_bug57255.test | 2 ++ mysql-test/suite/innodb_plugin/t/innodb_bug57255.test | 2 ++ 2 files changed, 4 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_bug57255.test b/mysql-test/suite/innodb/t/innodb_bug57255.test index 2b37a0a6092..0bf686578b2 100644 --- a/mysql-test/suite/innodb/t/innodb_bug57255.test +++ b/mysql-test/suite/innodb/t/innodb_bug57255.test @@ -12,6 +12,7 @@ create table C(id int not null auto_increment primary key, f1 int not null, fore insert into A values(1), (2); --disable_query_log +begin; let $i=257; while ($i) { @@ -24,6 +25,7 @@ while ($i) insert into C(f1) values(2); dec $i; } +commit; --enable_query_log # Following Deletes should not report error diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug57255.test b/mysql-test/suite/innodb_plugin/t/innodb_bug57255.test index 96184c355b6..e5056d3e23c 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug57255.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug57255.test @@ -12,6 +12,7 @@ create table C(id int not null auto_increment primary key, f1 int not null, fore insert into A values(1), (2); --disable_query_log +begin; let $i=257; while ($i) { @@ -24,6 +25,7 @@ while ($i) insert into C(f1) values(2); dec $i; } +commit; --enable_query_log # Following Deletes should not report error From 919f3f89923b5fd17282f89e64d777dfc5ee7d26 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 16 Dec 2010 09:53:07 +0200 Subject: [PATCH 27/52] Increment InnoDB version from 1.1.4 to 1.1.5 InnoDB 1.1.4 was released with MySQL 5.5.8 --- storage/innobase/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 84d81d51ec0..4470d221da9 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -51,7 +51,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 1 -#define INNODB_VERSION_BUGFIX 4 +#define INNODB_VERSION_BUGFIX 5 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From 5e5a9701dc590ce713ce32aa9020a368904015d9 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Fri, 17 Dec 2010 17:27:41 +1100 Subject: [PATCH 28/52] Bug #18274 - InnoDB auto_increment field reset on OPTIMIZE TABLE With the above bug fix, the maximum autoinc value is preserved when a table is optimized. Update resut file to reflect that. --- .../suite/parts/r/partition_auto_increment_innodb.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result index 5fd576322d5..34e47f58272 100644 --- a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result +++ b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result @@ -134,7 +134,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `c1` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`c1`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 +) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=latin1 DROP TABLE t1; CREATE TABLE t1 (a INT NULL AUTO_INCREMENT, @@ -440,7 +440,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `c1` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`c1`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 +) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=latin1 /*!50100 PARTITION BY HASH (c1) PARTITIONS 2 */ DROP TABLE t1; From 590fdf7792df16bf7866574432d19e48e49254c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 21 Dec 2010 11:39:19 +0200 Subject: [PATCH 29/52] Bug#58912 InnoDB unnecessarily avoids update-in-place on column prefix indexes row_upd_changes_ord_field_binary(): Do not return TRUE if the update vector changes a column that is covered by a prefix index, but does not change the column prefix. Add the row_ext_t parameter for determining whether the prefixes of externally stored columns match. dfield_datas_are_binary_equal(): Add the parameter len, for comparing column prefixes when len > 0. innodb.test: Add a test case where the patch of Bug #55284 failed without this fix. rb:537 approved by Jimmy Yang --- .../suite/innodb_plugin/r/innodb.result | 8 +- mysql-test/suite/innodb_plugin/t/innodb.test | 12 +++ storage/innodb_plugin/ChangeLog | 10 ++ storage/innodb_plugin/btr/btr0cur.c | 3 +- storage/innodb_plugin/include/data0data.h | 11 ++- storage/innodb_plugin/include/data0data.ic | 22 +++-- storage/innodb_plugin/include/row0upd.h | 5 +- storage/innodb_plugin/row/row0purge.c | 2 +- storage/innodb_plugin/row/row0umod.c | 4 +- storage/innodb_plugin/row/row0upd.c | 93 +++++++++++++------ 10 files changed, 128 insertions(+), 42 deletions(-) diff --git a/mysql-test/suite/innodb_plugin/r/innodb.result b/mysql-test/suite/innodb_plugin/r/innodb.result index 75a7023f9d0..d3f7a0b9fff 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb.result +++ b/mysql-test/suite/innodb_plugin/r/innodb.result @@ -1,5 +1,8 @@ drop table if exists t1,t2,t3,t4; drop database if exists mysqltest; +CREATE TABLE bug58912 (a BLOB, b TEXT, PRIMARY KEY(a(1))) ENGINE=InnoDB; +INSERT INTO bug58912 VALUES(REPEAT('a',8000),REPEAT('b',8000)); +UPDATE bug58912 SET a=REPEAT('a',7999); create table t1 (id int unsigned not null auto_increment, code tinyint unsigned not null, name char(20) not null, primary key (id), key (code), unique (name)) engine=innodb; insert into t1 (code, name) values (1, 'Tim'), (1, 'Monty'), (2, 'David'), (2, 'Erik'), (3, 'Sasha'), (3, 'Jeremy'), (4, 'Matt'); select id, code, name from t1 order by id; @@ -1676,10 +1679,10 @@ variable_value - @innodb_rows_deleted_orig 71 SELECT variable_value - @innodb_rows_inserted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_inserted'; variable_value - @innodb_rows_inserted_orig -1066 +1067 SELECT variable_value - @innodb_rows_updated_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_updated'; variable_value - @innodb_rows_updated_orig -865 +866 SELECT variable_value - @innodb_row_lock_waits_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_row_lock_waits'; variable_value - @innodb_row_lock_waits_orig 0 @@ -3239,3 +3242,4 @@ Variable_name Value Handler_update 1 Variable_name Value Handler_delete 1 +DROP TABLE bug58912; diff --git a/mysql-test/suite/innodb_plugin/t/innodb.test b/mysql-test/suite/innodb_plugin/t/innodb.test index 60ba7d1e3bf..cfc2277d501 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb.test +++ b/mysql-test/suite/innodb_plugin/t/innodb.test @@ -43,6 +43,15 @@ drop table if exists t1,t2,t3,t4; drop database if exists mysqltest; --enable_warnings +# Bug#58912 InnoDB unnecessarily avoids update-in-place on column prefixes +CREATE TABLE bug58912 (a BLOB, b TEXT, PRIMARY KEY(a(1))) ENGINE=InnoDB; +INSERT INTO bug58912 VALUES(REPEAT('a',8000),REPEAT('b',8000)); +UPDATE bug58912 SET a=REPEAT('a',7999); +# The above statements used to trigger a failure during purge when +# Bug#55284 was fixed while Bug#58912 was not. Defer the DROP TABLE, +# so that purge gets a chance to run (and a double free of the +# off-page column can be detected, if one is to occur.) + # # Small basic test with ignore # @@ -2541,6 +2550,9 @@ SET GLOBAL innodb_thread_concurrency = @innodb_thread_concurrency_orig; -- enable_query_log +# Clean up after the Bug#55284/Bug#58912 test case. +DROP TABLE bug58912; + ####################################################################### # # # Please, DO NOT TOUCH this file as well as the innodb.result file. # diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 347dc9a5183..13e2836dc98 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,8 +1,18 @@ +2010-12-21 The InnoDB Team + + * include/data0data.h, include/data0data.ic, include/row0upd.h, + btr/btr0cur.c, row/row0purge.c, row/row0umod.c, row/row0upd.c, + innodb.result, innodb.test: + Fix Bug#58912 InnoDB unnecessarily avoids update-in-place + on column prefix indexes + 2010-12-09 The InnoDB Team + * buf/buf0lru.c: Fix Bug#57600 output of I/O sum[%lu] can go negative 2010-11-11 The InnoDB Team + * thr/thr0loc.c, trx/trx0i_s.c: Fix Bug#57802 Empty ASSERTION parameter passed to the HASH_SEARCH macro diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index f9f0d156f52..2a26d8ffb33 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -1756,7 +1756,8 @@ btr_cur_update_in_place( NOT call it if index is secondary */ if (!dict_index_is_clust(index) - || row_upd_changes_ord_field_binary(NULL, index, update)) { + || row_upd_changes_ord_field_binary(NULL, NULL, + index, update)) { /* Remove possible hash index pointer to this record */ btr_search_update_hash_on_delete(cursor); diff --git a/storage/innodb_plugin/include/data0data.h b/storage/innodb_plugin/include/data0data.h index f9fce3f3657..cab8d790ac1 100644 --- a/storage/innodb_plugin/include/data0data.h +++ b/storage/innodb_plugin/include/data0data.h @@ -154,14 +154,19 @@ dfield_dup( dfield_t* field, /*!< in/out: data field */ mem_heap_t* heap); /*!< in: memory heap where allocated */ /*********************************************************************//** -Tests if data length and content is equal for two dfields. -@return TRUE if equal */ +Tests if two data fields are equal. +If len==0, tests the data length and content for equality. +If len>0, tests the first len bytes of the content for equality. +@return TRUE if both fields are NULL or if they are equal */ UNIV_INLINE ibool dfield_datas_are_binary_equal( /*==========================*/ const dfield_t* field1, /*!< in: field */ - const dfield_t* field2);/*!< in: field */ + const dfield_t* field2, /*!< in: field */ + ulint len) /*!< in: maximum prefix to compare, + or 0 to compare the whole field length */ + __attribute__((nonnull, warn_unused_result)); /*********************************************************************//** Tests if dfield data length and content is equal to the given. @return TRUE if equal */ diff --git a/storage/innodb_plugin/include/data0data.ic b/storage/innodb_plugin/include/data0data.ic index da79aa33702..74e0f7d09a0 100644 --- a/storage/innodb_plugin/include/data0data.ic +++ b/storage/innodb_plugin/include/data0data.ic @@ -229,20 +229,30 @@ dfield_dup( } /*********************************************************************//** -Tests if data length and content is equal for two dfields. -@return TRUE if equal */ +Tests if two data fields are equal. +If len==0, tests the data length and content for equality. +If len>0, tests the first len bytes of the content for equality. +@return TRUE if both fields are NULL or if they are equal */ UNIV_INLINE ibool dfield_datas_are_binary_equal( /*==========================*/ const dfield_t* field1, /*!< in: field */ - const dfield_t* field2) /*!< in: field */ + const dfield_t* field2, /*!< in: field */ + ulint len) /*!< in: maximum prefix to compare, + or 0 to compare the whole field length */ { - ulint len; + ulint len2 = len; - len = field1->len; + if (field1->len == UNIV_SQL_NULL || len == 0 || field1->len < len) { + len = field1->len; + } - return(len == field2->len + if (field2->len == UNIV_SQL_NULL || len2 == 0 || field2->len < len2) { + len2 = field2->len; + } + + return(len == len2 && (len == UNIV_SQL_NULL || !memcmp(field1->data, field2->data, len))); } diff --git a/storage/innodb_plugin/include/row0upd.h b/storage/innodb_plugin/include/row0upd.h index ea14cd64213..58d7ad15ecc 100644 --- a/storage/innodb_plugin/include/row0upd.h +++ b/storage/innodb_plugin/include/row0upd.h @@ -286,10 +286,13 @@ row_upd_changes_ord_field_binary( row and the data values in update are not known when this function is called, e.g., at compile time */ + const row_ext_t*ext, /*!< NULL, or prefixes of the externally + stored columns in the old row */ dict_index_t* index, /*!< in: index of the record */ - const upd_t* update);/*!< in: update vector for the row; NOTE: the + const upd_t* update) /*!< in: update vector for the row; NOTE: the field numbers in this MUST be clustered index positions! */ + __attribute__((nonnull(3,4), warn_unused_result)); /***********************************************************//** Checks if an update vector changes an ordering field of an index record. This function is fast if the update vector is short or the number of ordering diff --git a/storage/innodb_plugin/row/row0purge.c b/storage/innodb_plugin/row/row0purge.c index 31b255cf2d4..8bf2ae0f458 100644 --- a/storage/innodb_plugin/row/row0purge.c +++ b/storage/innodb_plugin/row/row0purge.c @@ -413,7 +413,7 @@ row_purge_upd_exist_or_extern( while (node->index != NULL) { index = node->index; - if (row_upd_changes_ord_field_binary(NULL, node->index, + if (row_upd_changes_ord_field_binary(NULL, NULL, node->index, node->update)) { /* Build the older version of the index entry */ entry = row_build_index_entry(node->row, NULL, diff --git a/storage/innodb_plugin/row/row0umod.c b/storage/innodb_plugin/row/row0umod.c index 5998dadd16d..49c51a75f18 100644 --- a/storage/innodb_plugin/row/row0umod.c +++ b/storage/innodb_plugin/row/row0umod.c @@ -668,8 +668,8 @@ row_undo_mod_upd_exist_sec( while (node->index != NULL) { index = node->index; - if (row_upd_changes_ord_field_binary(node->row, node->index, - node->update)) { + if (row_upd_changes_ord_field_binary( + node->row, node->ext, node->index, node->update)) { /* Build the newest version of the index entry */ entry = row_build_index_entry(node->row, node->ext, diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 0dc550b93f5..20f31e15514 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1198,20 +1198,21 @@ row_upd_changes_ord_field_binary( row and the data values in update are not known when this function is called, e.g., at compile time */ + const row_ext_t*ext, /*!< NULL, or prefixes of the externally + stored columns in the old row */ dict_index_t* index, /*!< in: index of the record */ const upd_t* update) /*!< in: update vector for the row; NOTE: the field numbers in this MUST be clustered index positions! */ { - ulint n_unique; - ulint n_upd_fields; - ulint i, j; - dict_index_t* clust_index; + ulint n_unique; + ulint i; + const dict_index_t* clust_index; - ut_ad(update && index); + ut_ad(update); + ut_ad(index); n_unique = dict_index_get_n_unique(index); - n_upd_fields = upd_get_n_fields(update); clust_index = dict_table_get_first_index(index->table); @@ -1219,33 +1220,72 @@ row_upd_changes_ord_field_binary( const dict_field_t* ind_field; const dict_col_t* col; - ulint col_pos; ulint col_no; + const upd_field_t* upd_field; + const dfield_t* dfield; + dfield_t dfield_ext; + ulint dfield_len; + const byte* buf; ind_field = dict_index_get_nth_field(index, i); col = dict_field_get_col(ind_field); - col_pos = dict_col_get_clust_pos(col, clust_index); col_no = dict_col_get_no(col); - for (j = 0; j < n_upd_fields; j++) { + upd_field = upd_get_field_by_field_no( + update, dict_col_get_clust_pos(col, clust_index)); - const upd_field_t* upd_field - = upd_get_nth_field(update, j); + if (upd_field == NULL) { + continue; + } - /* Note that if the index field is a column prefix - then it may be that row does not contain an externally - stored part of the column value, and we cannot compare - the datas */ + if (row == NULL) { + ut_ad(ext == NULL); + return(TRUE); + } - if (col_pos == upd_field->field_no - && (row == NULL - || ind_field->prefix_len > 0 - || !dfield_datas_are_binary_equal( - dtuple_get_nth_field(row, col_no), - &(upd_field->new_val)))) { + dfield = dtuple_get_nth_field(row, col_no); - return(TRUE); + /* This treatment of column prefix indexes is loosely + based on row_build_index_entry(). */ + + if (UNIV_LIKELY(ind_field->prefix_len == 0) + || dfield_is_null(dfield)) { + /* do nothing special */ + } else if (UNIV_LIKELY_NULL(ext)) { + /* See if the column is stored externally. */ + buf = row_ext_lookup(ext, col_no, &dfield_len); + + ut_ad(col->ord_part); + + if (UNIV_LIKELY_NULL(buf)) { + if (UNIV_UNLIKELY(buf == field_ref_zero)) { + /* This should never happen, but + we try to fail safe here. */ + ut_ad(0); + return(TRUE); + } + + goto copy_dfield; } + } else if (dfield_is_ext(dfield)) { + dfield_len = dfield_get_len(dfield); + ut_a(dfield_len > BTR_EXTERN_FIELD_REF_SIZE); + dfield_len -= BTR_EXTERN_FIELD_REF_SIZE; + ut_a(dict_index_is_clust(index) + || ind_field->prefix_len <= dfield_len); + buf = dfield_get_data(dfield); +copy_dfield: + ut_a(dfield_len > 0); + dfield_copy(&dfield_ext, dfield); + dfield_set_data(&dfield_ext, buf, dfield_len); + dfield = &dfield_ext; + } + + if (!dfield_datas_are_binary_equal( + dfield, &upd_field->new_val, + ind_field->prefix_len)) { + + return(TRUE); } } @@ -1329,7 +1369,7 @@ row_upd_changes_first_fields_binary( if (col_pos == upd_field->field_no && !dfield_datas_are_binary_equal( dtuple_get_nth_field(entry, i), - &(upd_field->new_val))) { + &upd_field->new_val, 0)) { return(TRUE); } @@ -1568,8 +1608,8 @@ row_upd_sec_step( ut_ad(!dict_index_is_clust(node->index)); if (node->state == UPD_NODE_UPDATE_ALL_SEC - || row_upd_changes_ord_field_binary(node->row, node->index, - node->update)) { + || row_upd_changes_ord_field_binary(node->row, node->ext, + node->index, node->update)) { return(row_upd_sec_index_entry(node, thr)); } @@ -1973,7 +2013,8 @@ exit_func: row_upd_store_row(node); - if (row_upd_changes_ord_field_binary(node->row, index, node->update)) { + if (row_upd_changes_ord_field_binary(node->row, node->ext, index, + node->update)) { /* Update causes an ordering field (ordering fields within the B-tree) of the clustered index record to change: perform From 721a890e480c7a704c94497167948ad797043517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 21 Dec 2010 13:27:22 +0200 Subject: [PATCH 30/52] Bug #55284 Double BLOB free due to lock wait while updating PRIMARY KEY This bug fix requires that Bug #58912 be fixed as well (bzr revision id marko.makela@oracle.com-20101221093919-mcmmgd4zpse9567d). Otherwise, another double BLOB free could occur when InnoDB would try to perform an update-in-place as delete-and-insert-by-update-in-place. row_upd_clust_rec_by_insert(): Do not disown the externally stored columns from the old record (btr_cur_mark_extern_inherited_fields()) until after checking the foreign key constraints and successfully inserting the updated record. If a lock wait timeout occurs between the delete-marking of the old record and the insertion of the updated record, mark the columns inherited before retrying the insert. Distinguish the state UPD_NODE_INSERT_BLOB from UPD_NODE_INSERT_CLUSTERED. btr_cur_del_mark_set_clust_rec(): Replace the cursor with block,rec,index,offsets so that the offsets need not be recalculated. Assert that rec is on a clustered index leaf page. btr_cur_disown_inherited_fields(): Renamed from btr_cur_mark_extern_inherited_fields(). Use upd_get_field_by_field_no(). Assert that there are externally stored columns. Assert that a mini-transaction is passed. Remove the return status. (The only caller, row_upd_clust_rec_by_insert(), will have determined that some fields have changed ownership.) btr_cur_mark_dtuple_inherited_extern(): Rename to row_upd_clust_rec_by_insert_inherit_func() and declare as static. Add the debug parameters rec, offsets. When rec is given, assert that the off-page columns match those in the inesrt tuple and that the off-page columns are owned by the record. Assert that the non-updated off-page columns in the insert tuple are owned, and mark them inherited. row_upd_clust_rec_by_insert_inherit(): A wrapper macro for row_upd_clust_rec_by_insert_inherit_func(). row_undo_mod_upd_exist_sec(): Adjust a comment about row_upd_clust_rec_by_insert(). rb:508 approved by Jimmy Yang --- storage/innodb_plugin/ChangeLog | 6 + storage/innodb_plugin/btr/btr0cur.c | 153 +++------------ storage/innodb_plugin/include/btr0cur.h | 42 ++-- storage/innodb_plugin/include/row0upd.h | 9 +- storage/innodb_plugin/row/row0umod.c | 9 +- storage/innodb_plugin/row/row0upd.c | 246 ++++++++++++++++++------ 6 files changed, 240 insertions(+), 225 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 13e2836dc98..ce470a0c027 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2010-12-21 The InnoDB Team + * include/btr0cur.h, include/row0upd.h, btr/btr0cur.c, + row/row0umod.c, row/row0upd.c: + Fix Bug#55284 Double free of off-page columns due to lock wait + while updating PRIMARY KEY + 2010-12-21 The InnoDB Team * include/data0data.h, include/data0data.ic, include/row0upd.h, diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 2a26d8ffb33..c57255a25ae 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -2509,27 +2509,24 @@ ulint btr_cur_del_mark_set_clust_rec( /*===========================*/ ulint flags, /*!< in: undo logging and locking flags */ - btr_cur_t* cursor, /*!< in: cursor */ + buf_block_t* block, /*!< in/out: buffer block of the record */ + rec_t* rec, /*!< in/out: record */ + dict_index_t* index, /*!< in: clustered index of the record */ + const ulint* offsets,/*!< in: rec_get_offsets(rec) */ ibool val, /*!< in: value to set */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr */ { - dict_index_t* index; - buf_block_t* block; roll_ptr_t roll_ptr; ulint err; - rec_t* rec; page_zip_des_t* page_zip; trx_t* trx; - mem_heap_t* heap = NULL; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets = offsets_; - rec_offs_init(offsets_); - rec = btr_cur_get_rec(cursor); - index = cursor->index; + ut_ad(dict_index_is_clust(index)); + ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table)); - offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); + ut_ad(buf_block_get_frame(block) == page_align(rec)); + ut_ad(page_is_leaf(page_align(rec))); #ifdef UNIV_DEBUG if (btr_cur_print_record_ops && thr) { @@ -2541,13 +2538,12 @@ btr_cur_del_mark_set_clust_rec( ut_ad(dict_index_is_clust(index)); ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets))); - err = lock_clust_rec_modify_check_and_lock(flags, - btr_cur_get_block(cursor), + err = lock_clust_rec_modify_check_and_lock(flags, block, rec, index, offsets, thr); if (err != DB_SUCCESS) { - goto func_exit; + return(err); } err = trx_undo_report_row_operation(flags, TRX_UNDO_MODIFY_OP, thr, @@ -2555,11 +2551,9 @@ btr_cur_del_mark_set_clust_rec( &roll_ptr); if (err != DB_SUCCESS) { - goto func_exit; + return(err); } - block = btr_cur_get_block(cursor); - if (block->is_hashed) { rw_lock_x_lock(&btr_search_latch); } @@ -2582,10 +2576,6 @@ btr_cur_del_mark_set_clust_rec( btr_cur_del_mark_set_clust_rec_log(flags, rec, index, val, trx, roll_ptr, mtr); -func_exit: - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } return(err); } @@ -3477,108 +3467,36 @@ btr_cur_set_ownership_of_extern_field( } /*******************************************************************//** -Marks not updated extern fields as not-owned by this record. The ownership -is transferred to the updated record which is inserted elsewhere in the +Marks non-updated off-page fields as disowned by this record. The ownership +must be transferred to the updated record which is inserted elsewhere in the index tree. In purge only the owner of externally stored field is allowed -to free the field. -@return TRUE if BLOB ownership was transferred */ +to free the field. */ UNIV_INTERN -ibool -btr_cur_mark_extern_inherited_fields( -/*=================================*/ +void +btr_cur_disown_inherited_fields( +/*============================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ rec_t* rec, /*!< in/out: record in a clustered index */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ const upd_t* update, /*!< in: update vector */ - mtr_t* mtr) /*!< in: mtr, or NULL if not logged */ + mtr_t* mtr) /*!< in/out: mini-transaction */ { - ulint n; - ulint j; ulint i; - ibool change_ownership = FALSE; - ut_ad(rec_offs_validate(rec, NULL, offsets)); + ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); + ut_ad(rec_offs_any_extern(offsets)); + ut_ad(mtr); - if (!rec_offs_any_extern(offsets)) { - - return(FALSE); - } - - n = rec_offs_n_fields(offsets); - - for (i = 0; i < n; i++) { - if (rec_offs_nth_extern(offsets, i)) { - - /* Check it is not in updated fields */ - - if (update) { - for (j = 0; j < upd_get_n_fields(update); - j++) { - if (upd_get_nth_field(update, j) - ->field_no == i) { - - goto updated; - } - } - } - + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i) + && !upd_get_field_by_field_no(update, i)) { btr_cur_set_ownership_of_extern_field( page_zip, rec, index, offsets, i, FALSE, mtr); - - change_ownership = TRUE; -updated: - ; } } - - return(change_ownership); -} - -/*******************************************************************//** -The complement of the previous function: in an update entry may inherit -some externally stored fields from a record. We must mark them as inherited -in entry, so that they are not freed in a rollback. */ -UNIV_INTERN -void -btr_cur_mark_dtuple_inherited_extern( -/*=================================*/ - dtuple_t* entry, /*!< in/out: updated entry to be - inserted to clustered index */ - const upd_t* update) /*!< in: update vector */ -{ - ulint i; - - for (i = 0; i < dtuple_get_n_fields(entry); i++) { - - dfield_t* dfield = dtuple_get_nth_field(entry, i); - byte* data; - ulint len; - ulint j; - - if (!dfield_is_ext(dfield)) { - continue; - } - - /* Check if it is in updated fields */ - - for (j = 0; j < upd_get_n_fields(update); j++) { - if (upd_get_nth_field(update, j)->field_no == i) { - - goto is_updated; - } - } - - data = dfield_get_data(dfield); - len = dfield_get_len(dfield); - data[len - BTR_EXTERN_FIELD_REF_SIZE + BTR_EXTERN_LEN] - |= BTR_EXTERN_INHERITED_FLAG; - -is_updated: - ; - } } /*******************************************************************//** @@ -3616,29 +3534,6 @@ btr_cur_unmark_extern_fields( } } -/*******************************************************************//** -Marks all extern fields in a dtuple as owned by the record. */ -UNIV_INTERN -void -btr_cur_unmark_dtuple_extern_fields( -/*================================*/ - dtuple_t* entry) /*!< in/out: clustered index entry */ -{ - ulint i; - - for (i = 0; i < dtuple_get_n_fields(entry); i++) { - dfield_t* dfield = dtuple_get_nth_field(entry, i); - - if (dfield_is_ext(dfield)) { - byte* data = dfield_get_data(dfield); - ulint len = dfield_get_len(dfield); - - data[len - BTR_EXTERN_FIELD_REF_SIZE + BTR_EXTERN_LEN] - &= ~BTR_EXTERN_OWNER_FLAG; - } - } -} - /*******************************************************************//** Flags the data tuple fields that are marked as extern storage in the update vector. We use this function to remember which fields we must diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index 1e1dc0f580a..b477ad0320a 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -332,10 +332,14 @@ ulint btr_cur_del_mark_set_clust_rec( /*===========================*/ ulint flags, /*!< in: undo logging and locking flags */ - btr_cur_t* cursor, /*!< in: cursor */ + buf_block_t* block, /*!< in/out: buffer block of the record */ + rec_t* rec, /*!< in/out: record */ + dict_index_t* index, /*!< in: clustered index of the record */ + const ulint* offsets,/*!< in: rec_get_offsets(rec) */ ibool val, /*!< in: value to set */ que_thr_t* thr, /*!< in: query thread */ - mtr_t* mtr); /*!< in: mtr */ + mtr_t* mtr) /*!< in: mtr */ + __attribute__((nonnull)); /***********************************************************//** Sets a secondary index record delete mark to TRUE or FALSE. @return DB_SUCCESS, DB_LOCK_WAIT, or error number */ @@ -481,40 +485,22 @@ btr_estimate_number_of_different_key_vals( /*======================================*/ dict_index_t* index); /*!< in: index */ /*******************************************************************//** -Marks not updated extern fields as not-owned by this record. The ownership -is transferred to the updated record which is inserted elsewhere in the +Marks non-updated off-page fields as disowned by this record. The ownership +must be transferred to the updated record which is inserted elsewhere in the index tree. In purge only the owner of externally stored field is allowed -to free the field. -@return TRUE if BLOB ownership was transferred */ +to free the field. */ UNIV_INTERN -ibool -btr_cur_mark_extern_inherited_fields( -/*=================================*/ +void +btr_cur_disown_inherited_fields( +/*============================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ rec_t* rec, /*!< in/out: record in a clustered index */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ const upd_t* update, /*!< in: update vector */ - mtr_t* mtr); /*!< in: mtr, or NULL if not logged */ -/*******************************************************************//** -The complement of the previous function: in an update entry may inherit -some externally stored fields from a record. We must mark them as inherited -in entry, so that they are not freed in a rollback. */ -UNIV_INTERN -void -btr_cur_mark_dtuple_inherited_extern( -/*=================================*/ - dtuple_t* entry, /*!< in/out: updated entry to be - inserted to clustered index */ - const upd_t* update); /*!< in: update vector */ -/*******************************************************************//** -Marks all extern fields in a dtuple as owned by the record. */ -UNIV_INTERN -void -btr_cur_unmark_dtuple_extern_fields( -/*================================*/ - dtuple_t* entry); /*!< in/out: clustered index entry */ + mtr_t* mtr) /*!< in/out: mini-transaction */ + __attribute__((nonnull(2,3,4,5,6))); /*******************************************************************//** Stores the fields in big_rec_vec to the tablespace and puts pointers to them in rec. The extern flags in rec will have to be set beforehand. diff --git a/storage/innodb_plugin/include/row0upd.h b/storage/innodb_plugin/include/row0upd.h index 58d7ad15ecc..b61e6b6dca1 100644 --- a/storage/innodb_plugin/include/row0upd.h +++ b/storage/innodb_plugin/include/row0upd.h @@ -465,11 +465,16 @@ struct upd_node_struct{ #define UPD_NODE_INSERT_CLUSTERED 3 /* clustered index record should be inserted, old record is already delete marked */ -#define UPD_NODE_UPDATE_ALL_SEC 4 /* an ordering field of the clustered +#define UPD_NODE_INSERT_BLOB 4 /* clustered index record should be + inserted, old record is already + delete-marked; non-updated BLOBs + should be inherited by the new record + and disowned by the old record */ +#define UPD_NODE_UPDATE_ALL_SEC 5 /* an ordering field of the clustered index record was changed, or this is a delete operation: should update all the secondary index records */ -#define UPD_NODE_UPDATE_SOME_SEC 5 /* secondary index entries should be +#define UPD_NODE_UPDATE_SOME_SEC 6 /* secondary index entries should be looked at and updated if an ordering field changed */ diff --git a/storage/innodb_plugin/row/row0umod.c b/storage/innodb_plugin/row/row0umod.c index 49c51a75f18..562f8093c38 100644 --- a/storage/innodb_plugin/row/row0umod.c +++ b/storage/innodb_plugin/row/row0umod.c @@ -676,11 +676,10 @@ row_undo_mod_upd_exist_sec( index, heap); if (UNIV_UNLIKELY(!entry)) { /* The server must have crashed in - row_upd_clust_rec_by_insert(), in - row_ins_index_entry_low() before - btr_store_big_rec_extern_fields() - has written the externally stored columns - (BLOBs) of the new clustered index entry. */ + row_upd_clust_rec_by_insert() before + the updated externally stored columns (BLOBs) + of the new clustered index entry were + written. */ /* The table must be in DYNAMIC or COMPRESSED format. REDUNDANT and COMPACT formats diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 20f31e15514..248f270bd9f 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1616,6 +1616,91 @@ row_upd_sec_step( return(DB_SUCCESS); } +#ifdef UNIV_DEBUG +# define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update) \ + row_upd_clust_rec_by_insert_inherit_func(rec,offsets,entry,update) +#else /* UNIV_DEBUG */ +# define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update) \ + row_upd_clust_rec_by_insert_inherit_func(entry,update) +#endif /* UNIV_DEBUG */ +/*******************************************************************//** +Mark non-updated off-page columns inherited when the primary key is +updated. We must mark them as inherited in entry, so that they are not +freed in a rollback. A limited version of this function used to be +called btr_cur_mark_dtuple_inherited_extern(). +@return TRUE if any columns were inherited */ +static __attribute__((warn_unused_result)) +ibool +row_upd_clust_rec_by_insert_inherit_func( +/*=====================================*/ +#ifdef UNIV_DEBUG + const rec_t* rec, /*!< in: old record, or NULL */ + const ulint* offsets,/*!< in: rec_get_offsets(rec), or NULL */ +#endif /* UNIV_DEBUG */ + dtuple_t* entry, /*!< in/out: updated entry to be + inserted into the clustered index */ + const upd_t* update) /*!< in: update vector */ +{ + ibool inherit = FALSE; + ulint i; + + ut_ad(!rec == !offsets); + ut_ad(!rec || rec_offs_any_extern(offsets)); + + for (i = 0; i < dtuple_get_n_fields(entry); i++) { + dfield_t* dfield = dtuple_get_nth_field(entry, i); + byte* data; + ulint len; + + ut_ad(!offsets + || !rec_offs_nth_extern(offsets, i) + == !dfield_is_ext(dfield) + || upd_get_field_by_field_no(update, i)); + if (!dfield_is_ext(dfield) + || upd_get_field_by_field_no(update, i)) { + continue; + } + +#ifdef UNIV_DEBUG + if (UNIV_LIKELY(rec != NULL)) { + const byte* rec_data + = rec_get_nth_field(rec, offsets, i, &len); + ut_ad(len == dfield_get_len(dfield)); + ut_ad(len != UNIV_SQL_NULL); + ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE); + + rec_data += len - BTR_EXTERN_FIELD_REF_SIZE; + + /* The pointer must not be zero. */ + ut_ad(memcmp(rec_data, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + /* The BLOB must be owned. */ + ut_ad(!(rec_data[BTR_EXTERN_LEN] + & BTR_EXTERN_OWNER_FLAG)); + } +#endif /* UNIV_DEBUG */ + + len = dfield_get_len(dfield); + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + data = dfield_get_data(dfield) + len + - BTR_EXTERN_FIELD_REF_SIZE; + /* The pointer must not be zero. */ + ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE)); + /* The BLOB must be owned. */ + ut_a(!(data[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG)); + + data[BTR_EXTERN_LEN] |= BTR_EXTERN_INHERITED_FLAG; + /* The BTR_EXTERN_INHERITED_FLAG only matters in + rollback. Purge will always free the extern fields of + a delete-marked row. */ + + inherit = TRUE; + } + + return(inherit); +} + /***********************************************************//** Marks the clustered index record deleted and inserts the updated version of the record to the index. This function should be used when the ordering @@ -1634,14 +1719,16 @@ row_upd_clust_rec_by_insert( a foreign key constraint */ mtr_t* mtr) /*!< in/out: mtr; gets committed here */ { - mem_heap_t* heap = NULL; + mem_heap_t* heap; btr_pcur_t* pcur; btr_cur_t* btr_cur; trx_t* trx; dict_table_t* table; dtuple_t* entry; ulint err; - ibool change_ownership = FALSE; + ibool change_ownership = FALSE; + rec_t* rec; + ulint* offsets = NULL; ut_ad(node); ut_ad(dict_index_is_clust(index)); @@ -1651,53 +1738,7 @@ row_upd_clust_rec_by_insert( pcur = node->pcur; btr_cur = btr_pcur_get_btr_cur(pcur); - if (node->state != UPD_NODE_INSERT_CLUSTERED) { - rec_t* rec; - dict_index_t* index; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets; - rec_offs_init(offsets_); - - err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG, - btr_cur, TRUE, thr, mtr); - if (err != DB_SUCCESS) { - mtr_commit(mtr); - return(err); - } - - /* Mark as not-owned the externally stored fields which the new - row inherits from the delete marked record: purge should not - free those externally stored fields even if the delete marked - record is removed from the index tree, or updated. */ - - rec = btr_cur_get_rec(btr_cur); - index = dict_table_get_first_index(table); - offsets = rec_get_offsets(rec, index, offsets_, - ULINT_UNDEFINED, &heap); - change_ownership = btr_cur_mark_extern_inherited_fields( - btr_cur_get_page_zip(btr_cur), rec, index, offsets, - node->update, mtr); - if (check_ref) { - /* NOTE that the following call loses - the position of pcur ! */ - err = row_upd_check_references_constraints( - node, pcur, table, index, offsets, thr, mtr); - if (err != DB_SUCCESS) { - mtr_commit(mtr); - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } - return(err); - } - } - } - - mtr_commit(mtr); - - if (!heap) { - heap = mem_heap_create(500); - } - node->state = UPD_NODE_INSERT_CLUSTERED; + heap = mem_heap_create(1000); entry = row_build_index_entry(node->upd_row, node->upd_ext, index, heap); @@ -1705,23 +1746,104 @@ row_upd_clust_rec_by_insert( row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id); - if (change_ownership) { - /* If we return from a lock wait, for example, we may have - extern fields marked as not-owned in entry (marked in the - if-branch above). We must unmark them, take the ownership - back. */ + switch (node->state) { + default: + ut_error; + case UPD_NODE_INSERT_BLOB: + /* A lock wait occurred in row_ins_index_entry() in + the previous invocation of this function. Mark the + off-page columns in the entry inherited. */ - btr_cur_unmark_dtuple_extern_fields(entry); + change_ownership = row_upd_clust_rec_by_insert_inherit( + NULL, NULL, entry, node->update); + ut_a(change_ownership); + /* fall through */ + case UPD_NODE_INSERT_CLUSTERED: + /* A lock wait occurred in row_ins_index_entry() in + the previous invocation of this function. */ + break; + case UPD_NODE_UPDATE_CLUSTERED: + /* This is the first invocation of the function where + we update the primary key. Delete-mark the old record + in the clustered index and prepare to insert a new entry. */ + rec = btr_cur_get_rec(btr_cur); + offsets = rec_get_offsets(rec, index, NULL, + ULINT_UNDEFINED, &heap); + ut_ad(page_rec_is_user_rec(rec)); - /* We must mark non-updated extern fields in entry as - inherited, so that a possible rollback will not free them. */ + err = btr_cur_del_mark_set_clust_rec( + BTR_NO_LOCKING_FLAG, btr_cur_get_block(btr_cur), + rec, index, offsets, TRUE, thr, mtr); + if (err != DB_SUCCESS) { +err_exit: + mtr_commit(mtr); + mem_heap_free(heap); + return(err); + } - btr_cur_mark_dtuple_inherited_extern(entry, node->update); + /* If the the new row inherits externally stored + fields (off-page columns a.k.a. BLOBs) from the + delete-marked old record, mark them disowned by the + old record and owned by the new entry. */ + + if (rec_offs_any_extern(offsets)) { + change_ownership = row_upd_clust_rec_by_insert_inherit( + rec, offsets, entry, node->update); + + if (change_ownership) { + btr_pcur_store_position(pcur, mtr); + } + } + + if (check_ref) { + /* NOTE that the following call loses + the position of pcur ! */ + err = row_upd_check_references_constraints( + node, pcur, table, index, offsets, thr, mtr); + if (err != DB_SUCCESS) { + goto err_exit; + } + } } + mtr_commit(mtr); + err = row_ins_index_entry(index, entry, node->upd_ext ? node->upd_ext->n_ext : 0, TRUE, thr); + node->state = change_ownership + ? UPD_NODE_INSERT_BLOB + : UPD_NODE_INSERT_CLUSTERED; + + if (err == DB_SUCCESS && change_ownership) { + /* Mark the non-updated fields disowned by the old record. */ + + /* NOTE: this transaction has an x-lock on the record + and therefore other transactions cannot modify the + record when we have no latch on the page. In addition, + we assume that other query threads of the same + transaction do not modify the record in the meantime. + Therefore we can assert that the restoration of the + cursor succeeds. */ + + mtr_start(mtr); + + if (!btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr)) { + ut_error; + } + + rec = btr_cur_get_rec(btr_cur); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); + ut_ad(page_rec_is_user_rec(rec)); + + btr_cur_disown_inherited_fields( + btr_cur_get_page_zip(btr_cur), + rec, index, offsets, node->update, mtr); + + mtr_commit(mtr); + } + mem_heap_free(heap); return(err); @@ -1865,8 +1987,9 @@ row_upd_del_mark_clust_rec( /* Mark the clustered index record deleted; we do not have to check locks, because we assume that we have an x-lock on the record */ - err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG, - btr_cur, TRUE, thr, mtr); + err = btr_cur_del_mark_set_clust_rec( + BTR_NO_LOCKING_FLAG, btr_cur_get_block(btr_cur), + btr_cur_get_rec(btr_cur), index, offsets, TRUE, thr, mtr); if (err == DB_SUCCESS && check_ref) { /* NOTE that the following call loses the position of pcur ! */ @@ -2083,7 +2206,8 @@ row_upd( } if (node->state == UPD_NODE_UPDATE_CLUSTERED - || node->state == UPD_NODE_INSERT_CLUSTERED) { + || node->state == UPD_NODE_INSERT_CLUSTERED + || node->state == UPD_NODE_INSERT_BLOB) { log_free_check(); err = row_upd_clust_step(node, thr); From 845e725a51763907fc55ac5e3e613500ff50d9b3 Mon Sep 17 00:00:00 2001 From: Calvin Sun Date: Mon, 27 Dec 2010 22:55:49 -0600 Subject: [PATCH 31/52] Fix a build error on Windows, introduced by revision-id: marko.makela@oracle.com-20101221112722-1yxxzzgqtem8bcm7 The fix was suggested by Jimmy. --- storage/innodb_plugin/row/row0upd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 248f270bd9f..4aa1474a25b 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1683,8 +1683,8 @@ row_upd_clust_rec_by_insert_inherit_func( len = dfield_get_len(dfield); ut_a(len != UNIV_SQL_NULL); ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); - data = dfield_get_data(dfield) + len - - BTR_EXTERN_FIELD_REF_SIZE; + data = dfield_get_data(dfield); + data += len - BTR_EXTERN_FIELD_REF_SIZE; /* The pointer must not be zero. */ ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE)); /* The BLOB must be owned. */ From 4b223f2af378accb38e25a31895a441fe864b1fd Mon Sep 17 00:00:00 2001 From: Calvin Sun Date: Mon, 27 Dec 2010 23:01:39 -0600 Subject: [PATCH 32/52] Fix a build error on Windows, introduced by revision-id: marko.makela@oracle.com-20101221112722-1yxxzzgqtem8bcm7 The fix was suggested by Jimmy. --- storage/innobase/row/row0upd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/row/row0upd.c b/storage/innobase/row/row0upd.c index 553b44b90fe..03ed40c6647 100644 --- a/storage/innobase/row/row0upd.c +++ b/storage/innobase/row/row0upd.c @@ -1711,8 +1711,8 @@ row_upd_clust_rec_by_insert_inherit_func( len = dfield_get_len(dfield); ut_a(len != UNIV_SQL_NULL); ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); - data = dfield_get_data(dfield) + len - - BTR_EXTERN_FIELD_REF_SIZE; + data = dfield_get_data(dfield); + data += len - BTR_EXTERN_FIELD_REF_SIZE; /* The pointer must not be zero. */ ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE)); /* The BLOB must be owned. */ From 909d7c299cb40e8ccf63b5c1995caa71c07ddee0 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Tue, 4 Jan 2011 11:46:20 +0200 Subject: [PATCH 33/52] PS-instrument the array of latches (rw locks) dict_table_stats_latches[]. This adds 64 new rows to performance_schema.rwlock_instances. This patch will make perfschema.binlog_mix perfschema.binlog_row tests fail, but they will be fixed by http://lists.mysql.com/commits/127862 Approved by: Jimmy (rb://554) --- storage/innobase/dict/dict0dict.c | 3 ++- storage/innobase/handler/ha_innodb.cc | 3 ++- storage/innobase/include/sync0rw.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index f3660cef611..b96657b56b6 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -75,6 +75,7 @@ UNIV_INTERN rw_lock_t dict_operation_lock; #ifdef UNIV_PFS_RWLOCK UNIV_INTERN mysql_pfs_key_t dict_operation_lock_key; UNIV_INTERN mysql_pfs_key_t index_tree_rw_lock_key; +UNIV_INTERN mysql_pfs_key_t dict_table_stats_latch_key; #endif /* UNIV_PFS_RWLOCK */ #ifdef UNIV_PFS_MUTEX @@ -715,7 +716,7 @@ dict_init(void) &dict_foreign_err_mutex, SYNC_ANY_LATCH); for (i = 0; i < DICT_TABLE_STATS_LATCHES_SIZE; i++) { - rw_lock_create(PFS_NOT_INSTRUMENTED, + rw_lock_create(dict_table_stats_latch_key, &dict_table_stats_latches[i], SYNC_INDEX_TREE); } } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 5c7301d453c..a7dbc9004ff 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -285,7 +285,8 @@ static PSI_rwlock_info all_innodb_rwlocks[] = { {&checkpoint_lock_key, "checkpoint_lock", 0}, {&trx_i_s_cache_lock_key, "trx_i_s_cache_lock", 0}, {&trx_purge_latch_key, "trx_purge_latch", 0}, - {&index_tree_rw_lock_key, "index_tree_rw_lock", 0} + {&index_tree_rw_lock_key, "index_tree_rw_lock", 0}, + {&dict_table_stats_latch_key, "dict_table_stats", 0} }; # endif /* UNIV_PFS_RWLOCK */ diff --git a/storage/innobase/include/sync0rw.h b/storage/innobase/include/sync0rw.h index 70471186f6d..5988fdfb382 100644 --- a/storage/innobase/include/sync0rw.h +++ b/storage/innobase/include/sync0rw.h @@ -122,6 +122,7 @@ extern mysql_pfs_key_t checkpoint_lock_key; extern mysql_pfs_key_t trx_i_s_cache_lock_key; extern mysql_pfs_key_t trx_purge_latch_key; extern mysql_pfs_key_t index_tree_rw_lock_key; +extern mysql_pfs_key_t dict_table_stats_latch_key; #endif /* UNIV_PFS_RWLOCK */ From cafdf6e6d20b848df396cb83dd7bfe27f1c1a22a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Jan 2011 12:34:39 -0600 Subject: [PATCH 34/52] 43818 - Patch for mysql-5.1-innodb Avoid handler::info() call for three Information Schema tables; TABLE_CONSTRAINTS, KEY_COLUMN_USAGE, & REFERENTIAL_CONTRAINTS --- sql/sql_show.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ed7a8d32eb8..7eff7740fc0 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4647,9 +4647,10 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, TABLE *show_table= tables->table; KEY *key_info=show_table->key_info; uint primary_key= show_table->s->primary_key; - show_table->file->info(HA_STATUS_VARIABLE | - HA_STATUS_NO_LOCK | - HA_STATUS_TIME); + + // This is not needed since no statistics are displayed. + // show_table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); + for (uint i=0 ; i < show_table->s->keys ; i++, key_info++) { if (i != primary_key && !(key_info->flags & HA_NOSAME)) @@ -4831,9 +4832,10 @@ static int get_schema_key_column_usage_record(THD *thd, TABLE *show_table= tables->table; KEY *key_info=show_table->key_info; uint primary_key= show_table->s->primary_key; - show_table->file->info(HA_STATUS_VARIABLE | - HA_STATUS_NO_LOCK | - HA_STATUS_TIME); + + // This is not needed since no statistics are displayed. + // show_table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); + for (uint i=0 ; i < show_table->s->keys ; i++, key_info++) { if (i != primary_key && !(key_info->flags & HA_NOSAME)) @@ -5562,9 +5564,9 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables, { List f_key_list; TABLE *show_table= tables->table; - show_table->file->info(HA_STATUS_VARIABLE | - HA_STATUS_NO_LOCK | - HA_STATUS_TIME); + + // This is not needed since no statistics are displayed. + // show_table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); show_table->file->get_foreign_key_list(thd, &f_key_list); FOREIGN_KEY_INFO *f_key_info; From c143ff48488a3dbff793d854eaba04abbef6c280 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Tue, 4 Jan 2011 22:31:46 -0800 Subject: [PATCH 35/52] Fix Bug #59157 valgrind conditional jump warning from dict_load_foreign. This is 5.1 built-in specific as the dict_table_t strcture is allocated with mem_heap_zalloc since 5.1 plugin. Approved by Sunny Bains --- storage/innobase/dict/dict0mem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index 168771ca307..9573ea18fde 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -87,6 +87,8 @@ dict_mem_table_create( table->big_rows = 0; + table->fk_max_recusive_level = 0; + mutex_create(&table->autoinc_mutex, SYNC_DICT_AUTOINC_MUTEX); table->autoinc = 0; From fe8fbaaddbeeaca21fb6810a82da611d5fb21407 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Tue, 4 Jan 2011 22:44:12 -0800 Subject: [PATCH 36/52] Fix Bug #59197 double quote in field comment prevents foreign key constraint creation rb://557 Approved by Sunny Bains --- storage/innobase/dict/dict0dict.c | 2 +- storage/innodb_plugin/ChangeLog | 5 +++++ storage/innodb_plugin/dict/dict0dict.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index b52a94c3348..fda6555e082 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -2217,7 +2217,7 @@ dict_scan_to( quote = '\0'; } else if (quote) { /* Within quotes: do nothing. */ - } else if (*ptr == '`' || *ptr == '"') { + } else if (*ptr == '`' || *ptr == '"' || *ptr == '\'') { /* Starting quote: remember the quote character. */ quote = *ptr; } else { diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index ce470a0c027..cf76d0fe432 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2011-01-04 The InnoDB Team + * dict/dict0dict.c: + Fix Bug#59197 double quote in field comment prevents foreign + key constraint creation + 2010-12-21 The InnoDB Team * include/btr0cur.h, include/row0upd.h, btr/btr0cur.c, row/row0umod.c, row/row0upd.c: diff --git a/storage/innodb_plugin/dict/dict0dict.c b/storage/innodb_plugin/dict/dict0dict.c index eb3169bd176..67765555658 100644 --- a/storage/innodb_plugin/dict/dict0dict.c +++ b/storage/innodb_plugin/dict/dict0dict.c @@ -2688,7 +2688,7 @@ dict_scan_to( quote = '\0'; } else if (quote) { /* Within quotes: do nothing. */ - } else if (*ptr == '`' || *ptr == '"') { + } else if (*ptr == '`' || *ptr == '"' || *ptr == '\'') { /* Starting quote: remember the quote character. */ quote = *ptr; } else { From 15273e4310448c5f52d52df6992289651798ed2f Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 6 Jan 2011 09:05:45 +0200 Subject: [PATCH 37/52] (Builtin InnoDB) Fix Bug#59303 Correct URL in crash message old URL: http://dev.mysql.com/doc/refman/5.1/en/forcing-recovery.html new URL: http://dev.mysql.com/doc/refman/5.1/en/forcing-innodb-recovery.html Notice that there is a redirect from the old URL to the new URL, so visiting the old URL does not give "page not found" error. --- storage/innobase/btr/btr0btr.c | 2 +- storage/innobase/buf/buf0buf.c | 4 ++-- storage/innobase/fsp/fsp0fsp.c | 2 +- storage/innobase/include/buf0buf.ic | 4 ++-- storage/innobase/log/log0recv.c | 2 +- storage/innobase/row/row0mysql.c | 2 +- storage/innobase/ut/ut0dbg.c | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/storage/innobase/btr/btr0btr.c b/storage/innobase/btr/btr0btr.c index b1b59227883..9438277050d 100644 --- a/storage/innobase/btr/btr0btr.c +++ b/storage/innobase/btr/btr0btr.c @@ -610,7 +610,7 @@ btr_page_get_father_for_rec( "InnoDB: corruption. If the crash happens at " "the database startup, see\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html about\n" + "forcing-innodb-recovery.html about\n" "InnoDB: forcing recovery. " "Then dump + drop + reimport.\n", stderr); } diff --git a/storage/innobase/buf/buf0buf.c b/storage/innobase/buf/buf0buf.c index 500088c3901..08e033e7a63 100644 --- a/storage/innobase/buf/buf0buf.c +++ b/storage/innobase/buf/buf0buf.c @@ -334,7 +334,7 @@ buf_page_is_corrupted( "InnoDB: tablespace but not the InnoDB " "log files. See\n" "InnoDB: http://dev.mysql.com/doc/refman/" - "5.1/en/forcing-recovery.html\n" + "5.1/en/forcing-innodb-recovery.html\n" "InnoDB: for more information.\n", (ulong) mach_read_from_4(read_buf + FIL_PAGE_OFFSET), @@ -2067,7 +2067,7 @@ buf_page_io_complete( " table for corruption.\n" "InnoDB: See also" " http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html\n" + "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); if (srv_force_recovery < SRV_FORCE_IGNORE_CORRUPT) { diff --git a/storage/innobase/fsp/fsp0fsp.c b/storage/innobase/fsp/fsp0fsp.c index 1f033c4b140..d228e683957 100644 --- a/storage/innobase/fsp/fsp0fsp.c +++ b/storage/innobase/fsp/fsp0fsp.c @@ -3046,7 +3046,7 @@ fseg_free_page_low( crash: fputs("InnoDB: Please refer to\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html\n" + "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); ut_error; } diff --git a/storage/innobase/include/buf0buf.ic b/storage/innobase/include/buf0buf.ic index b077ff0c181..58c5fd9ef3d 100644 --- a/storage/innobase/include/buf0buf.ic +++ b/storage/innobase/include/buf0buf.ic @@ -218,7 +218,7 @@ buf_block_align( "InnoDB: corruption. If this happens in an" " InnoDB database recovery, see\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html\n" + "forcing-innodb-recovery.html\n" "InnoDB: how to force recovery.\n", ptr, frame_zero, buf_pool->high_end); @@ -257,7 +257,7 @@ buf_frame_align( "InnoDB: corruption. If this happens in an" " InnoDB database recovery, see\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html\n" + "forcing-innodb-recovery.html\n" "InnoDB: how to force recovery.\n", ptr, buf_pool->frame_zero, buf_pool->high_end); diff --git a/storage/innobase/log/log0recv.c b/storage/innobase/log/log0recv.c index 6e1ca58cb15..9683486238c 100644 --- a/storage/innobase/log/log0recv.c +++ b/storage/innobase/log/log0recv.c @@ -1826,7 +1826,7 @@ recv_report_corrupt_log( "InnoDB: on your InnoDB tables to check that they are ok!\n" "InnoDB: If mysqld crashes after this recovery, look at\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html\n" + "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); fflush(stderr); diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 4fdd92c7cba..cee97871470 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -552,7 +552,7 @@ handle_new_error: " after the startup or when\n" "InnoDB: you dump the tables, look at\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html" + "forcing-innodb-recovery.html" " for help.\n", stderr); } else if (err == DB_FOREIGN_EXCEED_MAX_CASCADE) { diff --git a/storage/innobase/ut/ut0dbg.c b/storage/innobase/ut/ut0dbg.c index 8c4be190d77..88be1b34d9b 100644 --- a/storage/innobase/ut/ut0dbg.c +++ b/storage/innobase/ut/ut0dbg.c @@ -58,7 +58,7 @@ ut_dbg_assertion_failed( "InnoDB: immediately after the mysqld startup, there may be\n" "InnoDB: corruption in the InnoDB tablespace. Please refer to\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "forcing-recovery.html\n" + "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); #if defined(UNIV_SYNC_DEBUG) || !defined(UT_DBG_USE_ABORT) ut_dbg_stop_threads = TRUE; From fd7de284d412143b82a68c04d019deaf8bfc1f99 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 6 Jan 2011 09:12:53 +0200 Subject: [PATCH 38/52] (InnoDB Plugin) Fix Bug#59303 Correct URL in crash message old URL: http://dev.mysql.com/doc/refman/5.1/en/forcing-recovery.html new URL: http://dev.mysql.com/doc/refman/5.1/en/forcing-innodb-recovery.html Notice that there is a redirect from the old URL to the new URL, so visiting the old URL does not give "page not found" error. --- storage/innodb_plugin/btr/btr0btr.c | 2 +- storage/innodb_plugin/buf/buf0buf.c | 4 ++-- storage/innodb_plugin/fsp/fsp0fsp.c | 2 +- storage/innodb_plugin/log/log0recv.c | 2 +- storage/innodb_plugin/row/row0mysql.c | 2 +- storage/innodb_plugin/ut/ut0dbg.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index 29cd470e650..32e2caecdb8 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -663,7 +663,7 @@ btr_page_get_father_node_ptr_func( " to fix the\n" "InnoDB: corruption. If the crash happens at " "the database startup, see\n" - "InnoDB: " REFMAN "forcing-recovery.html about\n" + "InnoDB: " REFMAN "forcing-innodb-recovery.html about\n" "InnoDB: forcing recovery. " "Then dump + drop + reimport.\n", stderr); diff --git a/storage/innodb_plugin/buf/buf0buf.c b/storage/innodb_plugin/buf/buf0buf.c index 65d4311b840..dac416f9472 100644 --- a/storage/innodb_plugin/buf/buf0buf.c +++ b/storage/innodb_plugin/buf/buf0buf.c @@ -375,7 +375,7 @@ buf_page_is_corrupted( "you may have copied the InnoDB\n" "InnoDB: tablespace but not the InnoDB " "log files. See\n" - "InnoDB: " REFMAN "forcing-recovery.html\n" + "InnoDB: " REFMAN "forcing-innodb-recovery.html\n" "InnoDB: for more information.\n", (ulong) mach_read_from_4(read_buf + FIL_PAGE_OFFSET), @@ -3240,7 +3240,7 @@ corrupt: "InnoDB: TABLE to scan your" " table for corruption.\n" "InnoDB: See also " - REFMAN "forcing-recovery.html\n" + REFMAN "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); if (srv_force_recovery < SRV_FORCE_IGNORE_CORRUPT) { diff --git a/storage/innodb_plugin/fsp/fsp0fsp.c b/storage/innodb_plugin/fsp/fsp0fsp.c index f600e96bf88..e9d24b8fdf6 100644 --- a/storage/innodb_plugin/fsp/fsp0fsp.c +++ b/storage/innodb_plugin/fsp/fsp0fsp.c @@ -3326,7 +3326,7 @@ fseg_free_page_low( "InnoDB: database!\n", (ulong) page); crash: fputs("InnoDB: Please refer to\n" - "InnoDB: " REFMAN "forcing-recovery.html\n" + "InnoDB: " REFMAN "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); ut_error; } diff --git a/storage/innodb_plugin/log/log0recv.c b/storage/innodb_plugin/log/log0recv.c index c1d12dad413..1591bb02ec2 100644 --- a/storage/innodb_plugin/log/log0recv.c +++ b/storage/innodb_plugin/log/log0recv.c @@ -2188,7 +2188,7 @@ recv_report_corrupt_log( "InnoDB: far enough in recovery! Please run CHECK TABLE\n" "InnoDB: on your InnoDB tables to check that they are ok!\n" "InnoDB: If mysqld crashes after this recovery, look at\n" - "InnoDB: " REFMAN "forcing-recovery.html\n" + "InnoDB: " REFMAN "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); fflush(stderr); diff --git a/storage/innodb_plugin/row/row0mysql.c b/storage/innodb_plugin/row/row0mysql.c index fa89f0c0f25..c4911400cee 100644 --- a/storage/innodb_plugin/row/row0mysql.c +++ b/storage/innodb_plugin/row/row0mysql.c @@ -573,7 +573,7 @@ handle_new_error: "InnoDB: If the mysqld server crashes" " after the startup or when\n" "InnoDB: you dump the tables, look at\n" - "InnoDB: " REFMAN "forcing-recovery.html" + "InnoDB: " REFMAN "forcing-innodb-recovery.html" " for help.\n", stderr); break; case DB_FOREIGN_EXCEED_MAX_CASCADE: diff --git a/storage/innodb_plugin/ut/ut0dbg.c b/storage/innodb_plugin/ut/ut0dbg.c index 4484e6c36de..7839466e16f 100644 --- a/storage/innodb_plugin/ut/ut0dbg.c +++ b/storage/innodb_plugin/ut/ut0dbg.c @@ -79,7 +79,7 @@ ut_dbg_assertion_failed( " or crashes, even\n" "InnoDB: immediately after the mysqld startup, there may be\n" "InnoDB: corruption in the InnoDB tablespace. Please refer to\n" - "InnoDB: " REFMAN "forcing-recovery.html\n" + "InnoDB: " REFMAN "forcing-innodb-recovery.html\n" "InnoDB: about forcing recovery.\n", stderr); #if defined(UNIV_SYNC_DEBUG) || !defined(UT_DBG_USE_ABORT) ut_dbg_stop_threads = TRUE; From ae6e2a23b20ca5ab13edf595515af738daa1eeda Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Thu, 6 Jan 2011 21:41:30 +1100 Subject: [PATCH 39/52] Fix Bug #58653 - Sporadic crash due to assertion failure 0 == space->n_pending_flushes Check whether the master and purge thread are active after creating them. Do not proceed until both threads have started. We do this by checking whether a slot has been reserved by both the respective threads. Add srv_thread_has_reserved_slot() returns slot no or ULINT_UNDEFINED. rb://536 Approved by Jimmy --- storage/innobase/include/srv0srv.h | 8 ++++++++ storage/innobase/srv/srv0srv.c | 31 ++++++++++++++++++++++++++++++ storage/innobase/srv/srv0start.c | 18 +++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 30e3648576a..612ff4c6e54 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -476,6 +476,14 @@ enum srv_thread_type srv_get_thread_type(void); /*=====================*/ /*********************************************************************//** +Check whether thread type has reserved a slot. +@return slot number or UNDEFINED if not found*/ +UNIV_INTERN +ulint +srv_thread_has_reserved_slot( +/*=========================*/ + enum srv_thread_type type); /*!< in: thread type to check */ +/*********************************************************************//** Sets the info describing an i/o thread current state. */ UNIV_INTERN void diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 6e9fc6ab1c7..1c57d6960d0 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -982,6 +982,37 @@ srv_get_thread_type(void) return(type); } +/*********************************************************************//** +Check whether thread type has reserved a slot. Return the first slot that +is found. This works because we currently have only 1 thread of each type. +@return slot number or ULINT_UNDEFINED if not found*/ +UNIV_INTERN +ulint +srv_thread_has_reserved_slot( +/*=========================*/ + enum srv_thread_type type) /*!< in: thread type to check */ +{ + ulint i; + ulint slot_no = ULINT_UNDEFINED; + + mutex_enter(&kernel_mutex); + + for (i = 0; i < OS_THREAD_MAX_N; i++) { + srv_slot_t* slot; + + slot = srv_table_get_nth_slot(i); + + if (slot->in_use && slot->type == type) { + slot_no = i; + break; + } + } + + mutex_exit(&kernel_mutex); + + return(slot_no); +} + /*********************************************************************//** Initializes the server. */ UNIV_INTERN diff --git a/storage/innobase/srv/srv0start.c b/storage/innobase/srv/srv0start.c index 0e121b58d81..acb448a1a67 100644 --- a/storage/innobase/srv/srv0start.c +++ b/storage/innobase/srv/srv0start.c @@ -1833,6 +1833,24 @@ innobase_start_or_create_for_mysql(void) os_thread_create(&srv_purge_thread, NULL, NULL); } + /* Wait for the purge and master thread to startup. */ + + while (srv_shutdown_state == SRV_SHUTDOWN_NONE) { + if (srv_thread_has_reserved_slot(SRV_MASTER) == ULINT_UNDEFINED + || (srv_n_purge_threads == 1 + && srv_thread_has_reserved_slot(SRV_WORKER) + == ULINT_UNDEFINED)) { + + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: " + "Waiting for the background threads to " + "start\n"); + os_thread_sleep(1000000); + } else { + break; + } + } + #ifdef UNIV_DEBUG /* buf_debug_prints = TRUE; */ #endif /* UNIV_DEBUG */ From 5ef429fd3cccbaa4746b31b97a7d2a52959bd27f Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 6 Jan 2011 19:36:20 -0800 Subject: [PATCH 40/52] Fix Bug #55397 cannot select from innodb_trx when trx_query contains blobs that aren't strings rb://560 approved by Sunny Bains --- storage/innodb_plugin/ChangeLog | 5 +++++ storage/innodb_plugin/handler/i_s.cc | 12 ++++++++++-- storage/innodb_plugin/include/trx0i_s.h | 2 ++ storage/innodb_plugin/trx/trx0i_s.c | 3 ++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index cf76d0fe432..8eb63fe8c78 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2011-01-06 The InnoDB Team + * handler/i_s.cc, include/trx0i_s.h, trx/trx0i_s.c: + Fix Bug#55397 cannot select from innodb_trx when trx_query contains + blobs that aren't strings + 2011-01-04 The InnoDB Team * dict/dict0dict.c: Fix Bug#59197 double quote in field comment prevents foreign diff --git a/storage/innodb_plugin/handler/i_s.cc b/storage/innodb_plugin/handler/i_s.cc index 9ad2d656365..24996496b0c 100644 --- a/storage/innodb_plugin/handler/i_s.cc +++ b/storage/innodb_plugin/handler/i_s.cc @@ -371,8 +371,16 @@ fill_innodb_trx_from_cache( row->trx_mysql_thread_id)); /* trx_query */ - OK(field_store_string(fields[IDX_TRX_QUERY], - row->trx_query)); + if (row->trx_query) { + /* store will do appropriate character set + conversion check */ + fields[IDX_TRX_QUERY]->store( + row->trx_query, strlen(row->trx_query), + row->trx_query_cs); + fields[IDX_TRX_QUERY]->set_notnull(); + } else { + fields[IDX_TRX_QUERY]->set_null(); + } OK(schema_table_store_record(thd, table)); } diff --git a/storage/innodb_plugin/include/trx0i_s.h b/storage/innodb_plugin/include/trx0i_s.h index 7bd4e1b88c8..48d41038ea4 100644 --- a/storage/innodb_plugin/include/trx0i_s.h +++ b/storage/innodb_plugin/include/trx0i_s.h @@ -110,6 +110,8 @@ struct i_s_trx_row_struct { /*!< thd_get_thread_id() */ const char* trx_query; /*!< MySQL statement being executed in the transaction */ + struct charset_info_st* trx_query_cs; /*!< charset encode the MySQL + statement */ }; /** This structure represents INFORMATION_SCHEMA.innodb_lock_waits row */ diff --git a/storage/innodb_plugin/trx/trx0i_s.c b/storage/innodb_plugin/trx/trx0i_s.c index 3bf5ece9b9c..267e91db22e 100644 --- a/storage/innodb_plugin/trx/trx0i_s.c +++ b/storage/innodb_plugin/trx/trx0i_s.c @@ -498,7 +498,6 @@ fill_trx_row( stmt = innobase_get_stmt(trx->mysql_thd, &stmt_len); if (stmt != NULL) { - char query[TRX_I_S_TRX_QUERY_MAX_LEN + 1]; if (stmt_len > TRX_I_S_TRX_QUERY_MAX_LEN) { @@ -512,6 +511,8 @@ fill_trx_row( cache->storage, stmt, stmt_len + 1, MAX_ALLOWED_FOR_STORAGE(cache)); + row->trx_query_cs = innobase_get_charset(trx->mysql_thd); + if (row->trx_query == NULL) { return(FALSE); From 7365ef123c2af5bb4afcb5b5366643dcd0ac6bf5 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 7 Jan 2011 11:12:22 +0200 Subject: [PATCH 41/52] Fix Bug#59327 Fix autoconf usage for innodb_plugin AC_CHECK_FUNCS(f1 f2 f3, ACTION_IF_PRESENT) ACTION_IF_PRESENT is executed if any of f1, f2 or f3 is present. Fix this misusage, we want the action to be executed if all of the functions are present. --- storage/innodb_plugin/plug.in | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/storage/innodb_plugin/plug.in b/storage/innodb_plugin/plug.in index b2af11295bc..fa793647b1f 100644 --- a/storage/innodb_plugin/plug.in +++ b/storage/innodb_plugin/plug.in @@ -139,16 +139,20 @@ MYSQL_PLUGIN_ACTIONS(innodb_plugin, [ ) AC_MSG_CHECKING(whether Solaris libc atomic functions are available) - # either define HAVE_IB_SOLARIS_ATOMICS or not - AC_CHECK_FUNCS(atomic_cas_ulong \ - atomic_cas_32 \ - atomic_cas_64 \ - atomic_add_long_nv \ - atomic_swap_uchar, - - AC_DEFINE([HAVE_IB_SOLARIS_ATOMICS], [1], - [Define to 1 if Solaris libc atomic functions \ - are available]) + # Define HAVE_IB_SOLARIS_ATOMICS if _all_ of the following + # functions are present. + AC_CHECK_FUNC(atomic_add_long_nv, + AC_CHECK_FUNC(atomic_cas_32, + AC_CHECK_FUNC(atomic_cas_64, + AC_CHECK_FUNC(atomic_cas_ulong, + AC_CHECK_FUNC(atomic_swap_uchar, + AC_DEFINE([HAVE_IB_SOLARIS_ATOMICS], [1], + [Define to 1 if Solaris libc atomic functions are available] + ) + ) + ) + ) + ) ) AC_MSG_CHECKING(whether pthread_t can be used by Solaris libc atomic functions) From 211c56c02293041a3d35205da978065c8baf0747 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 7 Jan 2011 13:54:07 +0200 Subject: [PATCH 42/52] Fix InnoDB style after mikael@dator8-20101217205840-i7ltx8m6z3uc42kh --- storage/innobase/handler/ha_innodb.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 743ebbceaaf..e35eabcaea6 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -7837,14 +7837,14 @@ ha_innobase::info_low( are asked by MySQL to avoid locking. Another reason to avoid the call is that it uses quite a lot of CPU. See Bug#38185. */ - if (flag & HA_STATUS_NO_LOCK || - !(flag & HA_STATUS_VARIABLE_EXTRA)) { + if (flag & HA_STATUS_NO_LOCK + || !(flag & HA_STATUS_VARIABLE_EXTRA)) { /* We do not update delete_length if no locking is requested so the "old" value can remain. delete_length is initialized to 0 in the ha_statistics' constructor. Also we only - need delete_length to be set when - HA_STATUS_VARIABLE_EXTRA is set */ + need delete_length to be set when + HA_STATUS_VARIABLE_EXTRA is set */ } else if (UNIV_UNLIKELY (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE)) { /* Avoid accessing the tablespace if From 530a83a16d83aede26c9edb47163363070145e99 Mon Sep 17 00:00:00 2001 From: Matthias Leich Date: Fri, 7 Jan 2011 13:08:05 +0100 Subject: [PATCH 43/52] 1. Fix for Bug#58600 main.not_embedded_server test does not cleanup properly - remove the superfluous file - add an preemptive removal of the outfile before the SELECT ... INTO OUTFILE ... 2. Remove an already disabled subtest It's functionality is covered by tests in the suite funcs_1. 3. Adjust the formatting within some sub testcase to the formatting used in all other sub testcases --- mysql-test/r/not_embedded_server.result | 14 +++---- mysql-test/t/not_embedded_server.test | 50 +++++++++---------------- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result index ce229bf3e2e..1f8fdb65407 100644 --- a/mysql-test/r/not_embedded_server.result +++ b/mysql-test/r/not_embedded_server.result @@ -7,13 +7,13 @@ slave_skip_errors OFF # # FLUSH PRIVILEGES should not implicitly unlock locked tables. # -drop table if exists t1; -create table t1 (c1 int); -lock tables t1 read; -flush privileges; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 INT); +LOCK TABLES t1 READ; +FLUSH PRIVILEGES; ERROR HY000: Table 'host' was not locked with LOCK TABLES -unlock tables; -drop table t1; +UNLOCK TABLES; +DROP TABLE t1; # # Bug#54812: assert in Diagnostics_area::set_ok_status during EXPLAIN # @@ -28,7 +28,7 @@ CREATE FUNCTION f() RETURNS INT RETURN 1; GRANT FILE ON *.* TO 'nopriv_user'@'localhost'; FLUSH PRIVILEGES; connection: con1 -SELECT MAX(key1) FROM t1 WHERE f() < 1 INTO OUTFILE 'mytest'; +SELECT MAX(key1) FROM t1 WHERE f() < 1 INTO OUTFILE ''; ERROR 42000: execute command denied to user 'nopriv_user'@'localhost' for routine 'test.f' INSERT INTO t2 SELECT MAX(key1) FROM t1 WHERE f() < 1; ERROR 42000: execute command denied to user 'nopriv_user'@'localhost' for routine 'test.f' diff --git a/mysql-test/t/not_embedded_server.test b/mysql-test/t/not_embedded_server.test index 3fea1f630e0..c1dbba82292 100644 --- a/mysql-test/t/not_embedded_server.test +++ b/mysql-test/t/not_embedded_server.test @@ -4,31 +4,7 @@ -- source include/not_embedded.inc -# The following fails sporadically because 'check-testcase' runs -# queries before this test and there is no way to guarantee that any -# previous process finishes. The purpose of the test is not clearly -# stated, there is no reference to any bug report, and "select from -# I_S from prepared statement" doesn't look like something that's -# really imporant to test. I'm commenting out this for now. If -# anyone wants to keep this, please fix the race and motivate why we -# need to test this. If you see this comment and it is after mid-2009 -# or so, feel free to remove this test from the file. /Sven -# -# -## Show full process list with prepare -## To not show other connections, this must be the first test and we must -## have a server restart before this one -## -## We don't have any 4.1 tests as we use I_S to query the PROCESSLIST to -## exclude system threads that may/may not be active in the server -## (namely the ndb injector thread) -## -## End of 4.1 tests -# -#prepare stmt1 from ' SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND!=\'Daemon\' '; -#--replace_column 1 number 6 time 3 localhost -#execute stmt1; -#deallocate prepare stmt1; +# End of 4.1 tests call mtr.add_suppression("Can't open and lock privilege tables: Table 'host' was not locked with LOCK TABLES"); @@ -46,14 +22,14 @@ SHOW VARIABLES like 'slave_skip_errors'; --echo # FLUSH PRIVILEGES should not implicitly unlock locked tables. --echo # --disable_warnings -drop table if exists t1; +DROP TABLE IF EXISTS t1; --enable_warnings -create table t1 (c1 int); -lock tables t1 read; +CREATE TABLE t1 (c1 INT); +LOCK TABLES t1 READ; --error ER_TABLE_NOT_LOCKED -flush privileges; -unlock tables; -drop table t1; +FLUSH PRIVILEGES; +UNLOCK TABLES; +DROP TABLE t1; --echo # --echo # Bug#54812: assert in Diagnostics_area::set_ok_status during EXPLAIN @@ -83,8 +59,18 @@ connect (con1,localhost,nopriv_user,,); connection con1; --echo connection: con1 +let outfile=$MYSQLTEST_VARDIR/tmp/mytest; +--error 0,1 +--remove_file $outfile +--replace_result $outfile --error ER_PROCACCESS_DENIED_ERROR -SELECT MAX(key1) FROM t1 WHERE f() < 1 INTO OUTFILE 'mytest'; +eval SELECT MAX(key1) FROM t1 WHERE f() < 1 INTO OUTFILE '$outfile'; +# A removal of the outfile is necessary, at least today (2010-12-07), because +# the outfile is created even if the SELECT statement fails. +# If the server is improved in the future this may not happen. +# ==> Do not fail if the outfile does not exist. +--error 0,1 +--remove_file $outfile --error ER_PROCACCESS_DENIED_ERROR INSERT INTO t2 SELECT MAX(key1) FROM t1 WHERE f() < 1; From bc37d5c211e100d002b0ca7f0074318ca7231e39 Mon Sep 17 00:00:00 2001 From: Matthias Leich Date: Fri, 7 Jan 2011 14:16:28 +0100 Subject: [PATCH 44/52] Fix for Bug#47745 innodb.innodb-timeout fails sporadically - Second scenario checked: Ensure via wait routines that the commit comes after the processing of the statement which should get finally the ER_LOCK_WAIT_TIMEOUT --> This should prevent the current bug. - First scenario checked: Ensure via wait routines that the statement is already waiting for getting the lock before the commit is given. --> No effect on the current bug, but ensure that the right scenario is reached. - Take care that disconnects are finished before the test ends. --> Reduce the potential to harm succeeding tests. - "Mangle" the printout of the current default innodb_lock_wait_timeout value --> No need to adjust the test in case the default gets changed in future. --- .../suite/innodb/r/innodb-timeout.result | 8 +- mysql-test/suite/innodb/t/innodb-timeout.test | 73 ++++++++++++++++--- 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb-timeout.result b/mysql-test/suite/innodb/r/innodb-timeout.result index be9a688cd72..bb71ba5cb3d 100644 --- a/mysql-test/suite/innodb/r/innodb-timeout.result +++ b/mysql-test/suite/innodb/r/innodb-timeout.result @@ -13,13 +13,14 @@ set global innodb_lock_wait_timeout=347; select @@innodb_lock_wait_timeout; @@innodb_lock_wait_timeout 42 -set innodb_lock_wait_timeout=1; +set innodb_lock_wait_timeout=10; select @@innodb_lock_wait_timeout; @@innodb_lock_wait_timeout -1 +10 select @@innodb_lock_wait_timeout; @@innodb_lock_wait_timeout 347 +SET @connection_b_id = ; create table t1(a int primary key)engine=innodb; begin; insert into t1 values(1),(2),(3); @@ -31,8 +32,9 @@ a 3 begin; insert into t1 values(4); +set innodb_lock_wait_timeout=3; select * from t1 for update; commit; ERROR HY000: Lock wait timeout exceeded; try restarting transaction drop table t1; -set global innodb_lock_wait_timeout=50; +set global innodb_lock_wait_timeout=; diff --git a/mysql-test/suite/innodb/t/innodb-timeout.test b/mysql-test/suite/innodb/t/innodb-timeout.test index f23fe3cff2d..b29aec8e83d 100644 --- a/mysql-test/suite/innodb/t/innodb-timeout.test +++ b/mysql-test/suite/innodb/t/innodb-timeout.test @@ -1,6 +1,6 @@ -- source include/have_innodb.inc -let $timeout=`select @@innodb_lock_wait_timeout`; +let $initial_timeout=`select @@innodb_lock_wait_timeout`; set global innodb_lock_wait_timeout=42; connect (a,localhost,root,,); @@ -12,19 +12,24 @@ set innodb_lock_wait_timeout=1; select @@innodb_lock_wait_timeout; connection b; +let $connection_b_id=`SELECT CONNECTION_ID()`; select @@innodb_lock_wait_timeout; set global innodb_lock_wait_timeout=347; select @@innodb_lock_wait_timeout; -set innodb_lock_wait_timeout=1; +set innodb_lock_wait_timeout=10; select @@innodb_lock_wait_timeout; connect (c,localhost,root,,); connection c; + select @@innodb_lock_wait_timeout; -connection default; + disconnect c; +--source include/wait_until_disconnected.inc connection a; +--replace_result $connection_b_id +eval SET @connection_b_id = $connection_b_id; create table t1(a int primary key)engine=innodb; begin; insert into t1 values(1),(2),(3); @@ -33,7 +38,37 @@ connection b; --send select * from t1 for update; +# Observation on information_schema.processlist (2010-12 mysql-5.5) +# ----------------------------------------------------------------- +# As soon as the server started the execution of the +# connection a: --send select ... for update +# High parallel load could delay this up to two seconds. +# and before either +# - the innodb_lock_wait_timeout was exceeded +# -> connection b reap gets ER_LOCK_WAIT_TIMEOUT +# or +# - connection a commits, the lock disappears and the statement +# of connection b finishes +# -> connection b reap gets success + result set +# we see within information_schema.processlist for connection b a row +# command state info +# Query Sending data select * from t1 for update +# The highest time value seen was @@innodb_lock_wait_timeout + 1. +# Please note that there is unfortunately nothing which says +# that we are just waiting for a lock. + connection a; +# In order to ensure that the execution of +# connection b: select * from t1 for update +# has really started and is most probably waiting for the lock now we poll on +# information_schema.processlist. +# Also our current session innodb_lock_wait_timeout of 10 seconds should big +# enough to prevent that connection b ends up with getting ER_LOCK_WAIT_TIMEOUT. +# +let $wait_timeout= 10; +let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist +WHERE id = @connection_b_id AND INFO = 'select * from t1 for update'; +--source include/wait_condition.inc commit; connection b; @@ -44,21 +79,39 @@ begin; insert into t1 values(4); connection b; +set innodb_lock_wait_timeout=3; +# 3 seconds should be big enough that the wait routine of connection a will +# hit the time span where our next statement is visible within the +# information_schema.processlist. --send select * from t1 for update; connection a; -sleep 2; +# Wait till the execution of the connection b statement was started. +let $wait_timeout= 10; +let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist +WHERE id = @connection_b_id AND INFO = 'select * from t1 for update'; +--source include/wait_condition.inc +# Wait till the execution of the connection b statement has ended. +let $wait_timeout= 10; +let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist +WHERE id = @connection_b_id AND INFO IS NULL; +--source include/wait_condition.inc +# Give "commit" though this must be too late for the statement of connection b. commit; connection b; --error ER_LOCK_WAIT_TIMEOUT reap; -drop table t1; + +disconnect b; +--source include/wait_until_disconnected.inc + +connection a; +disconnect a; +--source include/wait_until_disconnected.inc connection default; - -disconnect a; -disconnect b; - -eval set global innodb_lock_wait_timeout=$timeout; +drop table t1; +--replace_result $initial_timeout +eval set global innodb_lock_wait_timeout=$initial_timeout; From b342d3e763a54fe2d1cb02bc744890c61deeb3c7 Mon Sep 17 00:00:00 2001 From: Matthias Leich Date: Fri, 7 Jan 2011 14:37:46 +0100 Subject: [PATCH 45/52] Fix for Bug#58414 Race condition in show_check.test Basically take care that disconnects are finished. --- mysql-test/t/show_check.test | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index fc3cfc76939..32b1f6bbc90 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -5,9 +5,6 @@ # depends on the presence of the log tables (which are CSV-based). --source include/have_csv.inc -# Save the initial number of concurrent sessions ---source include/count_sessions.inc - # # Test of some show commands # @@ -53,7 +50,7 @@ optimize table t1; optimize table t1; drop table t1; -#show variables; +# show variables; --echo -- Here we enable metadata just to check that the collation of the --echo -- resultset is non-binary for string type. This should be changed @@ -332,6 +329,7 @@ drop table t1; --error ER_DBACCESS_DENIED_ERROR drop database mysqltest; disconnect con1; +--source include/wait_until_disconnected.inc connect (con2,localhost,mysqltest_2,,test); connection con2; @@ -344,6 +342,7 @@ drop table mysqltest.t1; --error ER_DBACCESS_DENIED_ERROR drop database mysqltest; disconnect con2; +--source include/wait_until_disconnected.inc connect (con3,localhost,mysqltest_3,,test); connection con3; @@ -353,6 +352,7 @@ show create database mysqltest; drop table mysqltest.t1; drop database mysqltest; disconnect con3; +--source include/wait_until_disconnected.inc connection default; set names binary; @@ -900,10 +900,12 @@ CREATE TABLE t1( --let $outfile1=$MYSQLTEST_VARDIR/tmp/show_check.mysqltest1.sql +--source include/count_sessions.inc --echo --echo ---> Dumping mysqltest1 to outfile1 - --exec $MYSQL_DUMP --default-character-set=latin1 --character-sets-dir=$MYSQL_SHAREDIR/charsets --databases mysqltest1 > $outfile1 +# Take care that the additional session caused by MYSQL_DUMP has disappeared. +--source include/wait_until_count_sessions.inc # - Clean mysqltest1; @@ -917,9 +919,12 @@ DROP DATABASE mysqltest1; --echo --echo +--source include/count_sessions.inc --echo ---> Restoring mysqltest1... --exec $MYSQL test < $outfile1 --remove_file $outfile1 +# Take care that the additional session caused by MYSQL has disappeared. +--source include/wait_until_count_sessions.inc # - Check definition of the table. @@ -989,14 +994,14 @@ grant select on `mysqltest`.`t1` to mysqltest_4@localhost; connect (con4,localhost,mysqltest_4,,mysqltest); connection con4; show create database mysqltest; +disconnect con4; +--source include/wait_until_disconnected.inc connection default; delete from mysql.user where user='mysqltest_4'; delete from mysql.db where user='mysqltest_4'; delete from mysql.tables_priv where user='mysqltest_4'; flush privileges; drop database mysqltest; -connection default; -disconnect con4; # # Ensure that show plugin code is tested @@ -1192,6 +1197,7 @@ SHOW ENGINE MYISAM STATUS; --enable_result_log disconnect conn1; +--source include/wait_until_disconnected.inc connection default; DROP USER test_u@localhost; @@ -1214,8 +1220,9 @@ CONNECTION con1; --error ER_QUERY_INTERRUPTED SHOW CREATE TABLE non_existent; -CONNECTION default; DISCONNECT con1; +--source include/wait_until_disconnected.inc +CONNECTION default; --echo End of 5.1 tests @@ -1239,9 +1246,11 @@ connection con1; --echo # This statement used to be blocked. SHOW CREATE TABLE t1; +disconnect con1; +--source include/wait_until_disconnected.inc + --echo # Switching to connection 'default'. connection default; -disconnect con1; UNLOCK TABLES; DROP TABLE t1; @@ -1317,17 +1326,16 @@ connection con1; # Should not block. ALTER TABLE t1 CHARACTER SET = utf8; +disconnect con1; +--source include/wait_until_disconnected.inc + --echo # Connection default connection default; COMMIT; DROP TRIGGER t1_bi; DROP TABLE t1; -disconnect con1; -# Wait till all disconnects are completed ---source include/wait_until_count_sessions.inc - --echo # --echo # Bug#57306 SHOW PROCESSLIST does not display string literals well. --echo # @@ -1352,6 +1360,7 @@ SELECT RELEASE_LOCK('t'); --connection con1 --reap --disconnect con1 +--source include/wait_until_disconnected.inc --connection default SET NAMES latin1; From 8a49f0b13839ea236362df1783dfdc7979aa0d17 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 7 Jan 2011 16:52:44 +0200 Subject: [PATCH 46/52] Followup to vasil.dimov@oracle.com-20110107091222-q23qpb5skev0j9gc Do not use nested AC_CHECK_FUNC() because they result in: ./configure: line 52688: syntax error: unexpected end of file (which happens only on some platforms and does not happen on others, I have no idea what is the reason for this) --- storage/innodb_plugin/plug.in | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/storage/innodb_plugin/plug.in b/storage/innodb_plugin/plug.in index fa793647b1f..765025149a1 100644 --- a/storage/innodb_plugin/plug.in +++ b/storage/innodb_plugin/plug.in @@ -141,19 +141,22 @@ MYSQL_PLUGIN_ACTIONS(innodb_plugin, [ AC_MSG_CHECKING(whether Solaris libc atomic functions are available) # Define HAVE_IB_SOLARIS_ATOMICS if _all_ of the following # functions are present. - AC_CHECK_FUNC(atomic_add_long_nv, - AC_CHECK_FUNC(atomic_cas_32, - AC_CHECK_FUNC(atomic_cas_64, - AC_CHECK_FUNC(atomic_cas_ulong, - AC_CHECK_FUNC(atomic_swap_uchar, - AC_DEFINE([HAVE_IB_SOLARIS_ATOMICS], [1], - [Define to 1 if Solaris libc atomic functions are available] - ) - ) - ) - ) + AC_CHECK_FUNCS(atomic_add_long_nv \ + atomic_cas_32 \ + atomic_cas_64 \ + atomic_cas_ulong \ + atomic_swap_uchar) + + if test "${ac_cv_func_atomic_add_long_nv}" = "yes" -a \ + "${ac_cv_func_atomic_cas_32}" = "yes" -a \ + "${ac_cv_func_atomic_cas_64}" = "yes" -a \ + "${ac_cv_func_atomic_cas_ulong}" = "yes" -a \ + "${ac_cv_func_atomic_swap_uchar}" = "yes" ; then + + AC_DEFINE([HAVE_IB_SOLARIS_ATOMICS], [1], + [Define to 1 if Solaris libc atomic functions are available] ) - ) + fi AC_MSG_CHECKING(whether pthread_t can be used by Solaris libc atomic functions) # either define HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS or not From 998065c3a6f3d65e88c3926b31088a245f77406d Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Fri, 7 Jan 2011 16:33:36 -0200 Subject: [PATCH 47/52] Bug#51023: Mysql server crashes on SIGHUP and destroys InnoDB files From a user perspective, the problem is that a FLUSH LOGS or SIGHUP signal could end up associating the stdout and stderr to random files. In the case of this bug report, the streams would end up associated to InnoDB ibd files. The freopen(3) function is not thread-safe on FreeBSD. What this means is that if another thread calls open(2) during freopen() is executing that another thread's fd returned by open(2) may get re-associated with the file being passed to freopen(3). See FreeBSD PR number 79887 for reference: http://www.freebsd.org/cgi/query-pr.cgi?pr=79887 This problem is worked around by substituting a internal hook within the FILE structure. This avoids the loss of atomicity by not having the original fd closed before its duplicated. Patch based on the original work by Vasil Dimov. include/my_sys.h: Export my_freopen. mysys/my_fopen.c: Add a my_freopen abstraction to workaround bugs in specific OSes. Add a prototype for getosreldate() as older FreeBSD versions did not define one. sql/log.cc: Move freopen abstraction code over to mysys. The streams are now only reopened for writing. --- include/my_sys.h | 1 + mysys/my_fopen.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++- sql/log.cc | 76 ++++---------------------- 3 files changed, 146 insertions(+), 66 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 90fd78f77ba..0ac220cec31 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -664,6 +664,7 @@ extern void init_glob_errs(void); extern void wait_for_free_space(const char *filename, int errors); extern FILE *my_fopen(const char *FileName,int Flags,myf MyFlags); extern FILE *my_fdopen(File Filedes,const char *name, int Flags,myf MyFlags); +extern FILE *my_freopen(const char *path, const char *mode, FILE *stream); extern int my_fclose(FILE *fd,myf MyFlags); extern int my_chsize(File fd,my_off_t newlength, int filler, myf MyFlags); extern int my_sync(File fd, myf my_flags); diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index 44156da6ae3..a822b63dd63 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -18,6 +18,10 @@ #include #include "mysys_err.h" +#if defined(__FreeBSD__) +extern int getosreldate(void); +#endif + static void make_ftype(char * to,int flag); /* @@ -97,8 +101,137 @@ FILE *my_fopen(const char *filename, int flags, myf MyFlags) } /* my_fopen */ - /* Close a stream */ +#if defined(_WIN32) +static FILE *my_win_freopen(const char *path, FILE *stream) +{ + int handle_fd, fd= _fileno(stream); + HANDLE osfh; + + DBUG_ASSERT(filename && stream); + + /* Services don't have stdout/stderr on Windows, so _fileno returns -1. */ + if (fd < 0) + { + if (!freopen(filename, mode, stream)) + return NULL; + + fd= _fileno(stream); + } + + if ((osfh= CreateFile(path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) + return NULL; + + if ((handle_fd= _open_osfhandle((intptr_t)osfh, + _O_APPEND | _O_TEXT)) == -1) + { + CloseHandle(osfh); + return NULL; + } + + if (_dup2(handle_fd, fd) < 0) + { + CloseHandle(osfh); + return NULL; + } + + _close(handle_fd); + + return stream; +} + +#elif defined(__FreeBSD__) + +/* No close operation hook. */ + +static int no_close(void *cookie __attribute__((unused))) +{ + return 0; +} + +/* + A hack around a race condition in the implementation of freopen. + + The race condition steams from the fact that the current fd of + the stream is closed before its number is used to duplicate the + new file descriptor. This defeats the desired atomicity of the + close and duplicate of dup2(). + + See PR number 79887 for reference: + http://www.freebsd.org/cgi/query-pr.cgi?pr=79887 +*/ + +static FILE *my_freebsd_freopen(const char *path, const char *mode, FILE *stream) +{ + int old_fd; + FILE *result; + + flockfile(stream); + + old_fd= fileno(stream); + + /* Use a no operation close hook to avoid having the fd closed. */ + stream->_close= no_close; + + /* Relies on the implicit dup2 to close old_fd. */ + result= freopen(path, mode, stream); + + /* If successful, the _close hook was replaced. */ + + if (result == NULL) + close(old_fd); + else + funlockfile(result); + + return result; +} + +#endif + + +/** + Change the file associated with a file stream. + + @param path Path to file. + @param mode Mode of the stream. + @param stream File stream. + + @note + This function is used to redirect stdout and stderr to a file and + subsequently to close and reopen that file for log rotation. + + @retval A FILE pointer on success. Otherwise, NULL. +*/ + +FILE *my_freopen(const char *path, const char *mode, FILE *stream) +{ + FILE *result; + +#if defined(_WIN32) + result= my_win_freopen(path, mode, stream); +#elif defined(__FreeBSD__) + /* + XXX: Once the fix is ported to the stable releases, this should + be dependent upon the specific FreeBSD versions. Check at: + http://www.freebsd.org/cgi/query-pr.cgi?pr=79887 + */ + if (getosreldate() > 900027) + result= freopen(path, mode, stream); + else + result= my_freebsd_freopen(path, mode, stream); +#else + result= freopen(path, mode, stream); +#endif + + return result; +} + + +/* Close a stream */ int my_fclose(FILE *fd, myf MyFlags) { int err,file; diff --git a/sql/log.cc b/sql/log.cc index f3d3420194c..23182fa1902 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -5171,80 +5171,26 @@ void sql_perror(const char *message) } -#ifdef __WIN__ +/* + Change the file associated with two output streams. Used to + redirect stdout and stderr to a file. The streams are reopened + only for appending (writing at end of file). +*/ extern "C" my_bool reopen_fstreams(const char *filename, FILE *outstream, FILE *errstream) { - int handle_fd; - int err_fd, out_fd; - HANDLE osfh; + if (outstream && !my_freopen(filename, "a", outstream)) + return TRUE; - DBUG_ASSERT(filename && errstream); - - // Services don't have stdout/stderr on Windows, so _fileno returns -1. - err_fd= _fileno(errstream); - if (err_fd < 0) - { - if (!freopen(filename, "a+", errstream)) - return TRUE; + if (errstream && !my_freopen(filename, "a", errstream)) + return TRUE; + /* The error stream must be unbuffered. */ + if (errstream) setbuf(errstream, NULL); - err_fd= _fileno(errstream); - } - - if (outstream) - { - out_fd= _fileno(outstream); - if (out_fd < 0) - { - if (!freopen(filename, "a+", outstream)) - return TRUE; - out_fd= _fileno(outstream); - } - } - - if ((osfh= CreateFile(filename, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | - FILE_SHARE_DELETE, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, - NULL)) == INVALID_HANDLE_VALUE) - return TRUE; - - if ((handle_fd= _open_osfhandle((intptr_t)osfh, - _O_APPEND | _O_TEXT)) == -1) - { - CloseHandle(osfh); - return TRUE; - } - - if (_dup2(handle_fd, err_fd) < 0) - { - CloseHandle(osfh); - return TRUE; - } - - if (outstream && _dup2(handle_fd, out_fd) < 0) - { - CloseHandle(osfh); - return TRUE; - } - - _close(handle_fd); - return FALSE; -} -#else -extern "C" my_bool reopen_fstreams(const char *filename, - FILE *outstream, FILE *errstream) -{ - if (outstream && !freopen(filename, "a+", outstream)) - return TRUE; - - if (errstream && !freopen(filename, "a+", errstream)) - return TRUE; return FALSE; } -#endif /* From 844d6ed4b26e676418eb3867682fc1a2949e4706 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Fri, 7 Jan 2011 17:28:06 -0200 Subject: [PATCH 48/52] Bug#51023: Mysql server crashes on SIGHUP and destroys InnoDB files WIN32 compilation fixes: define ETIMEDOUT only if not available and fix typos and add a missing parameter. --- include/my_pthread.h | 4 +++- mysys/my_fopen.c | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/my_pthread.h b/include/my_pthread.h index a7e4ea25064..3880511da2d 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -126,7 +126,9 @@ struct tm *gmtime_r(const time_t *timep,struct tm *tmp); void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/ -#define ETIMEDOUT 145 /* Win32 doesn't have this */ +#ifndef ETIMEDOUT +#define ETIMEDOUT 145 /* Win32 might not have this */ +#endif #define getpid() GetCurrentThreadId() #define HAVE_LOCALTIME_R 1 #define _REENTRANT 1 diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index a822b63dd63..b8373ecb3ab 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -103,17 +103,17 @@ FILE *my_fopen(const char *filename, int flags, myf MyFlags) #if defined(_WIN32) -static FILE *my_win_freopen(const char *path, FILE *stream) +static FILE *my_win_freopen(const char *path, const char *mode, FILE *stream) { int handle_fd, fd= _fileno(stream); HANDLE osfh; - DBUG_ASSERT(filename && stream); + DBUG_ASSERT(path && stream); /* Services don't have stdout/stderr on Windows, so _fileno returns -1. */ if (fd < 0) { - if (!freopen(filename, mode, stream)) + if (!freopen(path, mode, stream)) return NULL; fd= _fileno(stream); From d00b9f103af025c78a63fe63d8755991a3a71058 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Fri, 7 Jan 2011 17:32:41 -0200 Subject: [PATCH 49/52] Bug#58765: Warning in item.h on Windows Truncate the maximum result length (64-bit wide type) to fit into the item maximum length (32-bit wide type). This is possible as this specific branch is only used if the maximum result length is less than 0x1000000 (MAX_BLOB_WIDTH), which fits comfortably in a 32-bit wide type. --- sql/item.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item.h b/sql/item.h index 6d10c6a6076..3fa11cfd8dd 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1231,7 +1231,7 @@ public: maybe_null= 1; } else - max_length= max_result_length; + max_length= (uint32) max_result_length; } void fix_length_and_charset_datetime(uint32 max_char_length_arg) { From 2815ffeeb9a62c04359c49e21a8b5653bde996f1 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Sat, 8 Jan 2011 16:51:19 +0200 Subject: [PATCH 50/52] Increment InnoDB Plugin version from 1.0.14 to 1.0.15. InnoDB Plugin 1.0.14 has been released with MySQL 5.1.54. --- storage/innodb_plugin/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index cab3af5297e..bbff8ddf1e3 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -46,7 +46,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 0 -#define INNODB_VERSION_BUGFIX 14 +#define INNODB_VERSION_BUGFIX 15 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From 1b64516756174c5de34ad77e54e3ecf164863ec4 Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Mon, 10 Jan 2011 13:16:50 +0100 Subject: [PATCH 51/52] Bug#57986 ORDER BY clause is not used after a UNION, if embedded in a SELECT An ORDER BY clause was bound to the incorrect (sub-)statement when used in a UNION context. In a query like: SELECT * FROM a UNION SELECT * FROM b ORDER BY c the result of SELECT * FROM b is sorted, and then combined with a. The correct behaviour is that the ORDER BY clause should be applied on the final set. Similar behaviour was seen on LIMIT clauses as well. In a UNION statement, there will be a select_lex object for each of the two selects, and a select_lex_unit object that describes the UNION itself. Similarly, the same behaviour was also seen on derived tables. The bug was caused by using a grammar rule for ORDER BY and LIMIT that bound these elements to thd->lex->current_select, which points to the last of the two selects, instead of to the fake_select_lex member of the master select_lex_unit object. sql/sql_yacc.yy: Need to use (opt_)union_order_or_limit to bind to the correct select_lex object. --- mysql-test/r/union.result | 100 ++++++++++++++++++++++++++++++++++++++ mysql-test/t/union.test | 54 ++++++++++++++++++++ sql/sql_yacc.yy | 9 +++- 3 files changed, 161 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 33fc4333d1c..7f0ec2275d7 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1644,3 +1644,103 @@ b 2 DROP TABLE t1,t2; End of 5.1 tests +# +# Bug#57986 ORDER BY clause is not used after a UNION, +# if embedded in a SELECT +# +CREATE TABLE t1 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); +CREATE TABLE t2 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); +INSERT INTO t1 (c1, c2) VALUES ('t1a', 1), ('t1a', 2), ('t1a', 3), ('t1b', 2), ('t1b', 1); +INSERT INTO t2 (c1, c2) VALUES ('t2a', 1), ('t2a', 2), ('t2a', 3), ('t2b', 2), ('t2b', 1); +SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY c2, c1; +c1 c2 +t1a 1 +t1b 1 +t2a 1 +t2b 1 +t1a 2 +t1b 2 +t2a 2 +t2b 2 +t1a 3 +t2a 3 +SELECT * FROM t1 UNION (SELECT * FROM t2) ORDER BY c2, c1; +c1 c2 +t1a 1 +t1b 1 +t2a 1 +t2b 1 +t1a 2 +t1b 2 +t2a 2 +t2b 2 +t1a 3 +t2a 3 +SELECT * FROM t1 UNION (SELECT * FROM t2 ORDER BY c2, c1); +c1 c2 +t1a 1 +t1a 2 +t1a 3 +t1b 2 +t1b 1 +t2a 1 +t2a 2 +t2a 3 +t2b 2 +t2b 1 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +(SELECT c1, c2 FROM t2) +ORDER BY c2, c1 +) AS res; +c1 c2 +t1a 1 +t1b 1 +t2a 1 +t2b 1 +t1a 2 +t1b 2 +t2a 2 +t2b 2 +t1a 3 +t2a 3 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +(SELECT c1, c2 FROM t2) +ORDER BY c2 DESC, c1 LIMIT 1 +) AS res; +c1 c2 +t1a 3 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +(SELECT c1, c2 FROM t2 ORDER BY c2 DESC, c1 LIMIT 1) +) AS res; +c1 c2 +t1a 1 +t1a 2 +t1a 3 +t1b 2 +t1b 1 +t2a 3 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +SELECT c1, c2 FROM t2 +ORDER BY c2 DESC, c1 DESC LIMIT 1 +) AS res; +c1 c2 +t2a 3 +SELECT c1, c2 FROM ( +( +(SELECT c1, c2 FROM t1) +UNION +(SELECT c1, c2 FROM t2) +) +ORDER BY c2 DESC, c1 ASC LIMIT 1 +) AS res; +c1 c2 +t1a 3 +DROP TABLE t1, t2; diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 596fc5f41ef..694f1bab15c 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -1117,3 +1117,57 @@ DROP TABLE t1,t2; --echo End of 5.1 tests + +--echo # +--echo # Bug#57986 ORDER BY clause is not used after a UNION, +--echo # if embedded in a SELECT +--echo # + +CREATE TABLE t1 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); +CREATE TABLE t2 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); + + +INSERT INTO t1 (c1, c2) VALUES ('t1a', 1), ('t1a', 2), ('t1a', 3), ('t1b', 2), ('t1b', 1); +INSERT INTO t2 (c1, c2) VALUES ('t2a', 1), ('t2a', 2), ('t2a', 3), ('t2b', 2), ('t2b', 1); + +SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY c2, c1; +SELECT * FROM t1 UNION (SELECT * FROM t2) ORDER BY c2, c1; +SELECT * FROM t1 UNION (SELECT * FROM t2 ORDER BY c2, c1); + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + (SELECT c1, c2 FROM t2) + ORDER BY c2, c1 +) AS res; + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + (SELECT c1, c2 FROM t2) + ORDER BY c2 DESC, c1 LIMIT 1 +) AS res; + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + (SELECT c1, c2 FROM t2 ORDER BY c2 DESC, c1 LIMIT 1) +) AS res; + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + SELECT c1, c2 FROM t2 + ORDER BY c2 DESC, c1 DESC LIMIT 1 +) AS res; + +SELECT c1, c2 FROM ( + ( + (SELECT c1, c2 FROM t1) + UNION + (SELECT c1, c2 FROM t2) + ) + ORDER BY c2 DESC, c1 ASC LIMIT 1 +) AS res; + +DROP TABLE t1, t2; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9aa938437b1..c283747156a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9396,7 +9396,7 @@ table_factor: ; select_derived_union: - select_derived opt_order_clause opt_limit_clause + select_derived opt_union_order_or_limit | select_derived_union UNION_SYM union_option @@ -9412,7 +9412,7 @@ select_derived_union: */ Lex->pop_context(); } - opt_order_clause opt_limit_clause + opt_union_order_or_limit ; /* The equivalent of select_init2 for nested queries. */ @@ -13862,6 +13862,11 @@ union_opt: | union_order_or_limit { $$= 1; } ; +opt_union_order_or_limit: + /* Empty */ + | union_order_or_limit + ; + union_order_or_limit: { THD *thd= YYTHD; From 90650edf697d498d0969f4bd9b81301ffc633dfa Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Mon, 10 Jan 2011 13:43:12 +0100 Subject: [PATCH 52/52] Bug#58970 Problem Subquery (without referencing a table) and Order By When having a UNION statement in a subquery, with no referenced tables (or only a reference to the virtual table 'dual'), the UNION did not allow an ORDER BY clause. i.e: SELECT(SELECT 1 AS a UNION SELECT 0 AS a ORDER BY a) AS b or SELECT(SELECT 1 AS a FROM dual UNION SELECT 0 as a ORDER BY a) AS b In addition, an ORDER BY / LIMIT clause was not accepted in subqueries even for single SELECT statements with no referenced tables (or with 'dual' as table reference) i.e: SELECT(SELECT 1 AS a ORDER BY a) AS b or SELECT(SELECT 1 AS a FROM dual ORDER BY a) AS b The fix was to allow an optional ORDER BY/LIMIT clause to the grammar for these cases. See also: Bug#57986 --- mysql-test/r/union.result | 25 +++++++++++++++++++++++++ mysql-test/t/union.test | 13 +++++++++++++ sql/sql_yacc.yy | 3 ++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 7f0ec2275d7..1f3422c1767 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1744,3 +1744,28 @@ ORDER BY c2 DESC, c1 ASC LIMIT 1 c1 c2 t1a 3 DROP TABLE t1, t2; +# +# Bug #58970 Problem Subquery (without referencing a table) +# and Order By +# +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a ASC LIMIT 1) AS dev; +dev +0 +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a DESC LIMIT 1) AS dev; +dev +1 +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a ASC LIMIT 1) AS dev; +dev +0 +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; +dev +1 +SELECT(SELECT 1 AS a ORDER BY a) AS dev; +dev +1 +SELECT(SELECT 1 AS a LIMIT 1) AS dev; +dev +1 +SELECT(SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; +dev +1 diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 694f1bab15c..c6599517e90 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -1171,3 +1171,16 @@ SELECT c1, c2 FROM ( ) AS res; DROP TABLE t1, t2; + +--echo # +--echo # Bug #58970 Problem Subquery (without referencing a table) +--echo # and Order By +--echo # + +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a ASC LIMIT 1) AS dev; +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a DESC LIMIT 1) AS dev; +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a ASC LIMIT 1) AS dev; +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; +SELECT(SELECT 1 AS a ORDER BY a) AS dev; +SELECT(SELECT 1 AS a LIMIT 1) AS dev; +SELECT(SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c283747156a..397afd26d8d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13914,7 +13914,7 @@ query_specification: ; query_expression_body: - query_specification + query_specification opt_union_order_or_limit | query_expression_body UNION_SYM union_option { @@ -13922,6 +13922,7 @@ query_expression_body: MYSQL_YYABORT; } query_specification + opt_union_order_or_limit { Lex->pop_context(); $$= $1;