From 83c8b1901871ef35f9bf7a792aa8e518f289c0d5 Mon Sep 17 00:00:00 2001 From: "chuck.bell@oracle.com" <> Date: Thu, 15 Dec 2011 16:16:48 -0500 Subject: [PATCH 01/18] BUG#12969301 : mysql_plugin: enable is ignored if plugin exists This patch changes the mechanism by which the client enables a plugin. Instead of using INSERT IGNORE to reload a plugin library, it now uses REPLACE INTO. This allows users to load a library multiple times replacing the existing values in the mysql.plugin table. This allows users to replace the symbol reference to a different dl name in the table. Thus permitting enabling of multiple versions of the same library without first disabling the old version. A regression test was added to ensure this feature works. --- client/mysql_plugin.c | 10 ++- mysql-test/include/libdaemon_example.ini | 9 +++ mysql-test/t/mysql_plugin.test | 77 +++++++++++++++++++++++- 3 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 mysql-test/include/libdaemon_example.ini diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index fb2a031bb8e..15f67e2b3e7 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -1018,7 +1018,7 @@ static int find_plugin(char *tp_path) Build the boostrap file. Create a new file and populate it with SQL commands to ENABLE or DISABLE - the plugin via INSERT and DELETE operations on the mysql.plugin table. + the plugin via REPLACE and DELETE operations on the mysql.plugin table. param[in] operation The type of operation (ENABLE or DISABLE) param[out] bootstrap A FILE* pointer @@ -1035,12 +1035,16 @@ static int build_bootstrap_file(char *operation, char *bootstrap) Perform plugin operation : ENABLE or DISABLE The following creates a temporary bootstrap file and populates it with - the appropriate SQL commands for the operation. For ENABLE, INSERT + the appropriate SQL commands for the operation. For ENABLE, REPLACE statements are created. For DISABLE, DELETE statements are created. The values for these statements are derived from the plugin_data read from the .ini configuration file. Once the file is built, a call to mysqld is made in read only, bootstrap modes to read the SQL statements and execute them. + + Note: Replace was used so that if a user loads a newer version of a + library with a different library name, the new library name is + used for symbols that match. */ if ((error= make_tempfile(bootstrap, "sql"))) { @@ -1057,7 +1061,7 @@ static int build_bootstrap_file(char *operation, char *bootstrap) if (strcasecmp(operation, "enable") == 0) { int i= 0; - fprintf(file, "INSERT IGNORE INTO mysql.plugin VALUES "); + fprintf(file, "REPLACE INTO mysql.plugin VALUES "); for (i= 0; i < (int)array_elements(plugin_data.components); i++) { /* stop when we read the end of the symbol list - marked with NULL */ diff --git a/mysql-test/include/libdaemon_example.ini b/mysql-test/include/libdaemon_example.ini new file mode 100644 index 00000000000..7e7df5bd2a2 --- /dev/null +++ b/mysql-test/include/libdaemon_example.ini @@ -0,0 +1,9 @@ +# +# Plugin configuration file. Place the following on a separate line: +# +# library binary file name (without .so or .dll) +# component_name +# [component_name] - additional components in plugin +# +liblibdaemon_example +daemon_example diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test index c5968df85f8..71617b86330 100644 --- a/mysql-test/t/mysql_plugin.test +++ b/mysql-test/t/mysql_plugin.test @@ -155,6 +155,74 @@ eval INSERT INTO mysql.plugin VALUES ('wonky', '$DAEMONEXAMPLE'); --replace_regex /\.dll/.so/ SELECT * FROM mysql.plugin WHERE dl like 'libdaemon%' ORDER BY name; +# MTR will remove this file later, but this might be too late. +--error 0,1 +--remove_file $expect_file +--write_file $expect_file +wait +EOF +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +# +# Disable the plugin - to remove winky, wonky entries +# +--exec $MYSQL_PLUGIN_CMD DISABLE daemon_example + +# +# Enable the plugin again +# +--exec $MYSQL_PLUGIN_CMD ENABLE daemon_example + +# +# Restart the server +# +--append_file $expect_file +restart +EOF +--enable_reconnect +--source include/wait_until_connected_again.inc + +--echo # +--echo # Ensure the plugin is loaded. +--echo # +--replace_regex /\.dll/.so/ +SELECT * FROM mysql.plugin WHERE dl like '%libdaemon%' ORDER BY name; + +# MTR will remove this file later, but this might be too late. +--error 0,1 +--remove_file $expect_file +--write_file $expect_file +wait +EOF +--shutdown_server 10 +--source include/wait_until_disconnected.inc + +# To test the case where the same plugin is reloaded with a different soname, +# we must copy the example daemon to a new location renaming it. + +let $DAEMON_RELOAD = lib$DAEMONEXAMPLE; +--copy_file $PLUGIN_DIR/$DAEMONEXAMPLE $PLUGIN_DIR/$DAEMON_RELOAD +--copy_file include/libdaemon_example.ini $PLUGIN_DIR/libdaemon_example.ini + +# Now reload it and see that it is a different name. +--exec $MYSQL_PLUGIN_CMD ENABLE libdaemon_example + +# +# Restart the server +# +--append_file $expect_file +restart +EOF +--enable_reconnect +--source include/wait_until_connected_again.inc + +--echo # +--echo # Ensure the plugin is replaced. +--echo # +--replace_regex /\.dll/.so/ +SELECT * FROM mysql.plugin WHERE dl like '%libdaemon%' ORDER BY name; + --echo # --echo # Disable the plugin... --echo # @@ -170,7 +238,12 @@ EOF # # Disable the plugin # ---exec $MYSQL_PLUGIN_CMD DISABLE daemon_example +--exec $MYSQL_PLUGIN_CMD DISABLE libdaemon_example + +# Remove files for last test case. + +--remove_file $PLUGIN_DIR/$DAEMON_RELOAD +--remove_file $DAEMONEXAMPLE_DIR/libdaemon_example.ini # # Restart the server @@ -184,7 +257,7 @@ EOF --echo # --echo # Ensure the plugin isn't loaded. --echo # -SELECT * FROM mysql.plugin WHERE dl like 'libdaemon%' ORDER BY name; +SELECT * FROM mysql.plugin WHERE dl like '%libdaemon%' ORDER BY name; # # Stop the server for error conditions From 1c1bcb1c807d65d3bcf448acde01d1adc56fa28b Mon Sep 17 00:00:00 2001 From: Tatjana Azundris Nuernberg Date: Sun, 19 Feb 2012 03:18:49 +0000 Subject: [PATCH 02/18] BUG 13454045 - 63524: BUG #35396 "ABNORMAL/IMPOSSIBLE/LARGE QUERY_TIME AND LOCK_TIME" HAPPENS A If a query's end time is before before its start time, the system clock has been turn back (daylight savings time etc.). When the system clock is changed, we can't tell for certain a given query was actually slow. We did not protect against logging such a query with a bogus execution time (resulting from end_time - start_time being negative), and possibly logging it even though it did not really take long to run. We now have a sanity check in place. --- sql/sql_parse.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 989c3e0f42f..0f190809ab9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1734,8 +1734,9 @@ void log_slow_statement(THD *thd) ulonglong end_utime_of_query= thd->current_utime(); thd_proc_info(thd, "logging slow query"); - if (((end_utime_of_query - thd->utime_after_lock) > - thd->variables.long_query_time || + if ((((end_utime_of_query > thd->utime_after_lock) && + ((end_utime_of_query - thd->utime_after_lock) > + thd->variables.long_query_time)) || ((thd->server_status & (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && opt_log_queries_not_using_indexes && From 923bf1462deeafe51cc076b1eeee11e58af38f2e Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 20 Feb 2012 13:25:37 +0200 Subject: [PATCH 03/18] Adjust .result files of mysqld--help-notwin and mysqld--help-win mtr tests. This is a followup to vasil.dimov@oracle.com-20120217130947-03319op732dsf4m2 which added a deprecation notice to ignore-builtin-innodb --- mysql-test/r/mysqld--help-notwin.result | 3 ++- mysql-test/r/mysqld--help-win.result | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 8dacb035f36..3df31eeebd5 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -175,7 +175,8 @@ The following options may be given as the first argument: GROUP_CONCAT() -?, --help Display this help and exit. --ignore-builtin-innodb - Disable initialization of builtin InnoDB plugin + DEPRECATED. This option will be removed in future + releases. Disable initialization of builtin InnoDB plugin --init-connect=name Command(s) that are executed for each new connection --init-file=name Read SQL commands from this file at startup --init-rpl-role=name diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index d7a06c5b23e..cd7043eb59f 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -175,7 +175,8 @@ The following options may be given as the first argument: GROUP_CONCAT() -?, --help Display this help and exit. --ignore-builtin-innodb - Disable initialization of builtin InnoDB plugin + DEPRECATED. This option will be removed in future + releases. Disable initialization of builtin InnoDB plugin --init-connect=name Command(s) that are executed for each new connection --init-file=name Read SQL commands from this file at startup --init-rpl-role=name From a7b43600866025797efb4ce88825629a4d8306fb Mon Sep 17 00:00:00 2001 From: Hery Ramilison Date: Mon, 20 Feb 2012 16:56:59 +0100 Subject: [PATCH 04/18] cloning 5.5.22 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index aa87ee35b40..94796f2c7ba 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=22 +MYSQL_VERSION_PATCH=23 MYSQL_VERSION_EXTRA= From 7e21bee031dc8f3623e95c3664d0463f17cffc40 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Mon, 20 Feb 2012 22:59:11 +0100 Subject: [PATCH 05/18] Bug#11761296: 53775: QUERY ON PARTITIONED TABLE RETURNS CACHED RESULT FROM PREVIOUS TRANSACTION The current Query Cache API is not fully compatible with the partitioning engine. There is no good way to implement support for QC due to: 1) a static callback for ha_partition would need to have access to all partition names and call the underlying callback for each [sub]partition with the correct name. 2) pruning would be impossible, even if one used the ulonglong engine_data due to if engine_data is changed, the table is invalidated by the QC. So the only viable solution to avoid incorrect data is to not allow caching of queries using partitioned tables. (There are some extra changes, due to removal of \r as line break) --- mysql-test/include/query_cache.inc | 38 +++-- mysql-test/r/cache_innodb.result | 7 +- mysql-test/r/partition_cache.result | 205 +++++++++++++++++++++++++++ mysql-test/t/cache_innodb-master.opt | 1 - mysql-test/t/partition_cache.test | 21 +++ sql/ha_partition.cc | 28 ++-- sql/ha_partition.h | 20 ++- 7 files changed, 286 insertions(+), 34 deletions(-) create mode 100644 mysql-test/r/partition_cache.result delete mode 100644 mysql-test/t/cache_innodb-master.opt create mode 100644 mysql-test/t/partition_cache.test diff --git a/mysql-test/include/query_cache.inc b/mysql-test/include/query_cache.inc index 7ce97b42158..ecc5014880c 100644 --- a/mysql-test/include/query_cache.inc +++ b/mysql-test/include/query_cache.inc @@ -4,6 +4,9 @@ # $engine_type -- storage engine to be tested # $test_foreign_keys -- 0, skip foreign key tests # -- 1, do not skip foreign key tests +# $partitions_a -- partition by column 'a' +# $partitions_id -- partition by column 'id' +# $partitions_s1 -- partition by column 's1' # have to be set before sourcing this script. # # Last update: @@ -19,47 +22,61 @@ eval SET SESSION STORAGE_ENGINE = $engine_type; drop table if exists t1,t2,t3; --enable_warnings +set @save_query_cache_size = @@global.query_cache_size; +set GLOBAL query_cache_size = 1355776; + # # Without auto_commit. # flush status; set autocommit=0; -create table t1 (a int not null); +eval create table t1 (a int not null)$partitions_a; insert into t1 values (1),(2),(3); +--sorted_result select * from t1; show status like "Qcache_queries_in_cache"; drop table t1; commit; set autocommit=1; begin; -create table t1 (a int not null); +eval create table t1 (a int not null)$partitions_a; insert into t1 values (1),(2),(3); +--sorted_result select * from t1; show status like "Qcache_queries_in_cache"; drop table t1; commit; -create table t1 (a int not null); -create table t2 (a int not null); -create table t3 (a int not null); +eval create table t1 (a int not null)$partitions_a; +eval create table t2 (a int not null)$partitions_a; +eval create table t3 (a int not null)$partitions_a; insert into t1 values (1),(2); insert into t2 values (1),(2); insert into t3 values (1),(2); +--sorted_result select * from t1; +--sorted_result select * from t2; +--sorted_result select * from t3; show status like "Qcache_queries_in_cache"; show status like "Qcache_hits"; begin; +--sorted_result select * from t1; +--sorted_result select * from t2; +--sorted_result select * from t3; show status like "Qcache_queries_in_cache"; show status like "Qcache_hits"; insert into t1 values (3); insert into t2 values (3); insert into t1 values (4); +--sorted_result select * from t1; +--sorted_result select * from t2; +--sorted_result select * from t3; show status like "Qcache_queries_in_cache"; show status like "Qcache_hits"; @@ -67,7 +84,7 @@ commit; show status like "Qcache_queries_in_cache"; drop table t3,t2,t1; -CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id)); +eval CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id))$partitions_id; select count(*) from t1; insert into t1 (id) values (0); select count(*) from t1; @@ -78,8 +95,6 @@ if ($test_foreign_keys) # # one statement roll back inside transation # -let $save_query_cache_size=`select @@global.query_cache_size`; -set GLOBAL query_cache_size=1355776; CREATE TABLE t1 ( id int(10) NOT NULL auto_increment, a varchar(25) default NULL, PRIMARY KEY (id), UNIQUE KEY a (a)); CREATE TABLE t2 ( id int(10) NOT NULL auto_increment, b varchar(25) default NULL, PRIMARY KEY (id), UNIQUE KEY b (b)); CREATE TABLE t3 ( id int(10) NOT NULL auto_increment, t1_id int(10) NOT NULL default '0', t2_id int(10) NOT NULL default '0', state int(11) default NULL, PRIMARY KEY (id), UNIQUE KEY t1_id (t1_id,t2_id), KEY t2_id (t2_id,t1_id), CONSTRAINT `t3_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`), CONSTRAINT `t3_ibfk_2` FOREIGN KEY (`t2_id`) REFERENCES `t2` (`id`)); @@ -95,9 +110,6 @@ insert into t3 VALUES ( NULL, 1, 1, 2 ); commit; select t1.* from t1, t2, t3 where t3.state & 1 = 0 and t3.t1_id = t1.id and t3.t2_id = t2.id and t1.id = 1 order by t1.a asc; drop table t3,t2,t1; ---disable_query_log -eval set GLOBAL query_cache_size=$save_query_cache_size; ---enable_query_log } # @@ -118,7 +130,7 @@ SET GLOBAL query_cache_size = 200000; flush status; SET @@autocommit=1; eval SET SESSION STORAGE_ENGINE = $engine_type; -CREATE TABLE t2 (s1 int, s2 varchar(1000), key(s1)); +eval CREATE TABLE t2 (s1 int, s2 varchar(1000), key(s1))$partitions_s1; INSERT INTO t2 VALUES (1,repeat('a',10)),(2,repeat('a',10)),(3,repeat('a',10)),(4,repeat('a',10)); COMMIT; START TRANSACTION; @@ -176,8 +188,8 @@ show status like "Qcache_queries_in_cache"; show status like "Qcache_hits"; # Final cleanup -eval set GLOBAL query_cache_size=$save_query_cache_size; disconnect connection1; --source include/wait_until_disconnected.inc connection default; +set @@global.query_cache_size = @save_query_cache_size; drop table t2; diff --git a/mysql-test/r/cache_innodb.result b/mysql-test/r/cache_innodb.result index b59298727c5..03989c80b9d 100644 --- a/mysql-test/r/cache_innodb.result +++ b/mysql-test/r/cache_innodb.result @@ -1,5 +1,7 @@ SET SESSION STORAGE_ENGINE = InnoDB; drop table if exists t1,t2,t3; +set @save_query_cache_size = @@global.query_cache_size; +set GLOBAL query_cache_size = 1355776; flush status; set autocommit=0; create table t1 (a int not null); @@ -100,7 +102,7 @@ show status like "Qcache_queries_in_cache"; Variable_name Value Qcache_queries_in_cache 2 drop table t3,t2,t1; -CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id)); +CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id)); select count(*) from t1; count(*) 0 @@ -109,7 +111,6 @@ select count(*) from t1; count(*) 1 drop table t1; -set GLOBAL query_cache_size=1355776; CREATE TABLE t1 ( id int(10) NOT NULL auto_increment, a varchar(25) default NULL, PRIMARY KEY (id), UNIQUE KEY a (a)); CREATE TABLE t2 ( id int(10) NOT NULL auto_increment, b varchar(25) default NULL, PRIMARY KEY (id), UNIQUE KEY b (b)); CREATE TABLE t3 ( id int(10) NOT NULL auto_increment, t1_id int(10) NOT NULL default '0', t2_id int(10) NOT NULL default '0', state int(11) default NULL, PRIMARY KEY (id), UNIQUE KEY t1_id (t1_id,t2_id), KEY t2_id (t2_id,t1_id), CONSTRAINT `t3_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`), CONSTRAINT `t3_ibfk_2` FOREIGN KEY (`t2_id`) REFERENCES `t2` (`id`)); @@ -218,5 +219,5 @@ Qcache_queries_in_cache 1 show status like "Qcache_hits"; Variable_name Value Qcache_hits 1 -set GLOBAL query_cache_size=1048576; +set @@global.query_cache_size = @save_query_cache_size; drop table t2; diff --git a/mysql-test/r/partition_cache.result b/mysql-test/r/partition_cache.result new file mode 100644 index 00000000000..e4e7cc8a4ac --- /dev/null +++ b/mysql-test/r/partition_cache.result @@ -0,0 +1,205 @@ +SET SESSION STORAGE_ENGINE = InnoDB; +drop table if exists t1,t2,t3; +set @save_query_cache_size = @@global.query_cache_size; +set GLOBAL query_cache_size = 1355776; +flush status; +set autocommit=0; +create table t1 (a int not null) PARTITION BY KEY (a) PARTITIONS 3; +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +drop table t1; +commit; +set autocommit=1; +begin; +create table t1 (a int not null) PARTITION BY KEY (a) PARTITIONS 3; +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +drop table t1; +commit; +create table t1 (a int not null) PARTITION BY KEY (a) PARTITIONS 3; +create table t2 (a int not null) PARTITION BY KEY (a) PARTITIONS 3; +create table t3 (a int not null) PARTITION BY KEY (a) PARTITIONS 3; +insert into t1 values (1),(2); +insert into t2 values (1),(2); +insert into t3 values (1),(2); +select * from t1; +a +1 +2 +select * from t2; +a +1 +2 +select * from t3; +a +1 +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +begin; +select * from t1; +a +1 +2 +select * from t2; +a +1 +2 +select * from t3; +a +1 +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +insert into t1 values (3); +insert into t2 values (3); +insert into t1 values (4); +select * from t1; +a +1 +2 +3 +4 +select * from t2; +a +1 +2 +3 +select * from t3; +a +1 +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +commit; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +drop table t3,t2,t1; +CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id)) PARTITION BY HASH (id) PARTITIONS 3; +select count(*) from t1; +count(*) +0 +insert into t1 (id) values (0); +select count(*) from t1; +count(*) +1 +drop table t1; +SET SESSION STORAGE_ENGINE = InnoDB; +SET @@autocommit=1; +connection default +SHOW VARIABLES LIKE 'have_query_cache'; +Variable_name Value +have_query_cache YES +SET GLOBAL query_cache_size = 200000; +flush status; +SET @@autocommit=1; +SET SESSION STORAGE_ENGINE = InnoDB; +CREATE TABLE t2 (s1 int, s2 varchar(1000), key(s1)) PARTITION BY KEY (s1) PARTITIONS 3; +INSERT INTO t2 VALUES (1,repeat('a',10)),(2,repeat('a',10)),(3,repeat('a',10)),(4,repeat('a',10)); +COMMIT; +START TRANSACTION; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +0 +UPDATE t2 SET s2 = 'w' WHERE s1 = 3; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +1 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +connection connection1 +START TRANSACTION; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +0 +INSERT INTO t2 VALUES (5,'w'); +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +1 +COMMIT; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +1 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +connection default +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +1 +COMMIT; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +connection connection1 +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +2 +START TRANSACTION; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +2 +INSERT INTO t2 VALUES (6,'w'); +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +3 +connection default +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +2 +START TRANSACTION; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +2 +DELETE from t2 WHERE s1=3; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +1 +COMMIT; +connection connection1 +COMMIT; +SELECT sql_cache count(*) FROM t2 WHERE s2 = 'w'; +count(*) +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +set @@global.query_cache_size = @save_query_cache_size; +drop table t2; diff --git a/mysql-test/t/cache_innodb-master.opt b/mysql-test/t/cache_innodb-master.opt deleted file mode 100644 index 5f0ebff98f6..00000000000 --- a/mysql-test/t/cache_innodb-master.opt +++ /dev/null @@ -1 +0,0 @@ ---set-variable=query_cache_size=1M diff --git a/mysql-test/t/partition_cache.test b/mysql-test/t/partition_cache.test new file mode 100644 index 00000000000..b5d19796b7c --- /dev/null +++ b/mysql-test/t/partition_cache.test @@ -0,0 +1,21 @@ +# t/cache_innodb.test +# +# Last update: +# 2006-07-26 ML test refactored (MySQL 5.1) +# main code t/innodb_cache.test --> include/query_cache.inc +# new wrapper t/cache_innodb.test +# + +--source include/have_query_cache.inc + +--source include/have_innodb.inc +--source include/have_partition.inc +let $engine_type= InnoDB; +# Using SELECT to get a space as first character. +let $partitions_a= `SELECT ' PARTITION BY KEY (a) PARTITIONS 3'`; +let $partitions_id= `SELECT ' PARTITION BY HASH (id) PARTITIONS 3'`; +let $partitions_s1= `SELECT ' PARTITION BY KEY (s1) PARTITIONS 3'`; +# partitioning does not support FOREIGN KEYs +let $test_foreign_keys= 0; + +--source include/query_cache.inc diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 7c7cf5a4302..7e044b5260e 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -6494,20 +6494,20 @@ int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys) return ret; err: if (file > m_file) - { - uint *key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys); - KEY *old_key_info= table_arg->key_info; - uint i; - /* Use the newly added key_info as table->key_info to remove them. */ - for (i= 0; i < num_of_keys; i++) - key_numbers[i]= i; - table_arg->key_info= key_info; - while (--file >= m_file) - { - (void) (*file)->prepare_drop_index(table_arg, key_numbers, num_of_keys); - (void) (*file)->final_drop_index(table_arg); - } - table_arg->key_info= old_key_info; + { + uint *key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys); + KEY *old_key_info= table_arg->key_info; + uint i; + /* Use the newly added key_info as table->key_info to remove them. */ + for (i= 0; i < num_of_keys; i++) + key_numbers[i]= i; + table_arg->key_info= key_info; + while (--file >= m_file) + { + (void) (*file)->prepare_drop_index(table_arg, key_numbers, num_of_keys); + (void) (*file)->final_drop_index(table_arg); + } + table_arg->key_info= old_key_info; } return ret; } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 46e2f447a47..813c0ae8960 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -219,9 +219,9 @@ public: */ ha_partition(handlerton *hton, TABLE_SHARE * table); ha_partition(handlerton *hton, partition_info * part_info); - ha_partition(handlerton *hton, TABLE_SHARE *share, - partition_info *part_info_arg, - ha_partition *clone_arg, + ha_partition(handlerton *hton, TABLE_SHARE *share, + partition_info *part_info_arg, + ha_partition *clone_arg, MEM_ROOT *clone_mem_root_arg); ~ha_partition(); /* @@ -553,6 +553,20 @@ public: virtual int extra(enum ha_extra_function operation); virtual int extra_opt(enum ha_extra_function operation, ulong cachesize); virtual int reset(void); + /* + Do not allow caching of partitioned tables, since we cannot return + a callback or engine_data that would work for a generic engine. + */ + virtual my_bool register_query_cache_table(THD *thd, char *table_key, + uint key_length, + qc_engine_callback + *engine_callback, + ulonglong *engine_data) + { + *engine_callback= NULL; + *engine_data= 0; + return FALSE; + } private: static const uint NO_CURRENT_PART_ID; From 03f48bc650c6593e000dd82bbe2c2a5d2078b354 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Tue, 21 Feb 2012 14:13:31 +0200 Subject: [PATCH 06/18] bumped up the version of the main tree to match the security tree --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 908171ca857..0f1968747c0 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.96) +AM_INIT_AUTOMAKE(mysql, 5.0.97) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 @@ -23,7 +23,7 @@ NDB_SHARED_LIB_VERSION=$NDB_SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=96 +NDB_VERSION_BUILD=97 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? From a66f29c30c500cdbea82f6acc771bef0aa36328d Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Tue, 21 Feb 2012 17:57:07 +0200 Subject: [PATCH 07/18] Fix Bug#13639142 64128: INNODB ERROR IN SERVER LOG OF INNODB_BUG34300 Suppress innodb_bug34300 from failing if InnoDB prints: 120221 11:05:03 InnoDB: ERROR: the age of the last checkpoint is 9439048, InnoDB: which exceeds the log group capacity 9433498. by default the log capacity is 2 log files, 5 MB each. --- mysql-test/suite/innodb/t/innodb_bug34300.test | 1 + mysql-test/suite/innodb_plugin/t/innodb_bug34300.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_bug34300.test b/mysql-test/suite/innodb/t/innodb_bug34300.test index 6c516ccb73e..c49b69f29fc 100644 --- a/mysql-test/suite/innodb/t/innodb_bug34300.test +++ b/mysql-test/suite/innodb/t/innodb_bug34300.test @@ -8,6 +8,7 @@ -- disable_query_log -- disable_result_log call mtr.add_suppression("InnoDB: Warning: a long semaphore wait:"); +call mtr.add_suppression("the age of the last checkpoint is"); # set packet size and reconnect SET @@global.max_allowed_packet=16777216; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug34300.test b/mysql-test/suite/innodb_plugin/t/innodb_bug34300.test index 8be53f0db30..6684ba692c8 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug34300.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug34300.test @@ -8,6 +8,7 @@ -- disable_query_log -- disable_result_log call mtr.add_suppression("InnoDB: Warning: a long semaphore wait:"); +call mtr.add_suppression("the age of the last checkpoint is"); # set packet size and reconnect let $max_packet=`select @@global.max_allowed_packet`; From df8827d0e6ed8f4a8640640a0bc0f407985d31fa Mon Sep 17 00:00:00 2001 From: Chaithra Gopalareddy Date: Fri, 24 Feb 2012 11:53:36 +0530 Subject: [PATCH 08/18] Bug#13012483:EXPLAIN EXTENDED, PREPARED STATEMENT, CRASH IN CHECK_SIMPLE_EQUALITY PROBLEM: Crash in "check_simple_equality" when using a subquery with "IN" and "ALL" in prepare. ANALYSIS: Crash can be reproduced using a simplified query like this one: prepare s from "select 1 from g1 where 1 < all ( select @:=(1 in (select 1 from g1)) from g1)"; This bug is currently present only on 5.5.and 5.1. Its fixed as part of work log(#1110) in 5.6. We are taking one change to fix this in 5.5 and 5.1. Problem seems to be present because we are trying to evaluate "is_null" on an argument which is part of a subquery (In Item_is_not_null_test::update_used_tables()). But the condition to evaluate is only when we do not have a sub query present, which means to say that "with_subselect" is not set. With respect to the above query, we create an object of type "Item_in_optimizer" which by definition is always associated with a subquery. While in 5.6 we set "with_subselect" to true for "Item_in_optimizer" object, we do not do the same in 5.5. This results in the evaluation for "is_null" resulting in a coredump. So, we are now setting "with_subselect" to true for "Item_in_optimizer" in 5.1 and 5.5. --- mysql-test/r/func_in.result | 10 ++++++++++ mysql-test/t/func_in.test | 13 +++++++++++++ sql/item_cmpfunc.h | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index 0b6117581f3..7f0e607e789 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -776,4 +776,14 @@ SELECT 1 IN (YEAR(FROM_UNIXTIME(NULL)) ,1); 1 IN (YEAR(FROM_UNIXTIME(NULL)) ,1) 1 # +# +# Bug#13012483: EXPLAIN EXTENDED, PREPARED STATEMENT, CRASH IN CHECK_SIMPLE_EQUALITY +# +CREATE TABLE t1 (a INT); +PREPARE s FROM "SELECT 1 FROM t1 WHERE 1 < ALL (SELECT @:= (1 IN (SELECT 1 FROM t1)) FROM t1)"; +EXECUTE s; +1 +DROP TABLE t1; +# End of test BUG#13012483 +# End of 5.1 tests diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index 08469b37967..dd4194e2a4e 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -562,4 +562,17 @@ SELECT 1 IN (YEAR(FROM_UNIXTIME(NULL)) ,1); --echo # +--echo # +--echo # Bug#13012483: EXPLAIN EXTENDED, PREPARED STATEMENT, CRASH IN CHECK_SIMPLE_EQUALITY +--echo # + +CREATE TABLE t1 (a INT); +PREPARE s FROM "SELECT 1 FROM t1 WHERE 1 < ALL (SELECT @:= (1 IN (SELECT 1 FROM t1)) FROM t1)"; +EXECUTE s; + +DROP TABLE t1; + +--echo # End of test BUG#13012483 + +--echo # --echo End of 5.1 tests diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 4d8cadfcce9..b185bf02790 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -254,7 +254,7 @@ public: Item_in_optimizer(Item *a, Item_in_subselect *b): Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0), save_cache(0), result_for_null_param(UNKNOWN) - {} + { with_subselect= true; } bool fix_fields(THD *, Item **); bool fix_left(THD *thd, Item **ref); bool is_null(); From 32df28595e15054aa30a4848385649ede8cdc2bf Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Fri, 24 Feb 2012 21:21:07 +0800 Subject: [PATCH 09/18] Fix Bug #64432 Port bug fix #54330 from mysql-5.1 to mysql-5.5 --- storage/innobase/row/row0merge.c | 93 +++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c index 82169e7efee..750f9d72cf2 100644 --- a/storage/innobase/row/row0merge.c +++ b/storage/innobase/row/row0merge.c @@ -1599,22 +1599,29 @@ row_merge( const dict_index_t* index, /*!< in: index being created */ merge_file_t* file, /*!< in/out: file containing index entries */ - ulint* half, /*!< in/out: half the file */ row_merge_block_t* block, /*!< in/out: 3 buffers */ int* tmpfd, /*!< in/out: temporary file handle */ - struct TABLE* table) /*!< in/out: MySQL table, for + struct TABLE* table, /*!< in/out: MySQL table, for reporting erroneous key value if applicable */ + ulint* num_run,/*!< in/out: Number of runs remain + to be merged */ + ulint* run_offset) /*!< in/out: Array contains the + first offset number for each merge + run */ { ulint foffs0; /*!< first input offset */ ulint foffs1; /*!< second input offset */ ulint error; /*!< error code */ merge_file_t of; /*!< output file */ - const ulint ihalf = *half; + const ulint ihalf = run_offset[*num_run / 2]; /*!< half the input file */ - ulint ohalf; /*!< half the output file */ + ulint n_run = 0; + /*!< num of runs generated from this merge */ + UNIV_MEM_ASSERT_W(block[0], 3 * sizeof block[0]); + ut_ad(ihalf < file->offset); of.fd = *tmpfd; @@ -1630,17 +1637,20 @@ row_merge( #endif /* POSIX_FADV_SEQUENTIAL */ /* Merge blocks to the output file. */ - ohalf = 0; foffs0 = 0; foffs1 = ihalf; + UNIV_MEM_INVALID(run_offset, *num_run * sizeof *run_offset); + for (; foffs0 < ihalf && foffs1 < file->offset; foffs0++, foffs1++) { - ulint ahalf; /*!< arithmetic half the input file */ if (UNIV_UNLIKELY(trx_is_interrupted(trx))) { return(DB_INTERRUPTED); } + /* Remember the offset number for this run */ + run_offset[n_run++] = of.offset; + error = row_merge_blocks(index, file, block, &foffs0, &foffs1, &of, table); @@ -1648,21 +1658,6 @@ row_merge( return(error); } - /* Record the offset of the output file when - approximately half the output has been generated. In - this way, the next invocation of row_merge() will - spend most of the time in this loop. The initial - estimate is ohalf==0. */ - ahalf = file->offset / 2; - ut_ad(ohalf <= of.offset); - - /* Improve the estimate until reaching half the input - file size, or we can not get any closer to it. All - comparands should be non-negative when !(ohalf < ahalf) - because ohalf <= of.offset. */ - if (ohalf < ahalf || of.offset - ahalf < ohalf - ahalf) { - ohalf = of.offset; - } } /* Copy the last blocks, if there are any. */ @@ -1672,6 +1667,9 @@ row_merge( return(DB_INTERRUPTED); } + /* Remember the offset number for this run */ + run_offset[n_run++] = of.offset; + if (!row_merge_blocks_copy(index, file, block, &foffs0, &of)) { return(DB_CORRUPTION); } @@ -1684,6 +1682,9 @@ row_merge( return(DB_INTERRUPTED); } + /* Remember the offset number for this run */ + run_offset[n_run++] = of.offset; + if (!row_merge_blocks_copy(index, file, block, &foffs1, &of)) { return(DB_CORRUPTION); } @@ -1695,10 +1696,23 @@ row_merge( return(DB_CORRUPTION); } + ut_ad(n_run <= *num_run); + + *num_run = n_run; + + /* Each run can contain one or more offsets. As merge goes on, + the number of runs (to merge) will reduce until we have one + single run. So the number of runs will always be smaller than + the number of offsets in file */ + ut_ad((*num_run) <= file->offset); + + /* The number of offsets in output file is always equal or + smaller than input file */ + ut_ad(of.offset <= file->offset); + /* Swap file descriptors for the next pass. */ *tmpfd = file->fd; *file = of; - *half = ohalf; UNIV_MEM_INVALID(block[0], 3 * sizeof block[0]); @@ -1723,27 +1737,44 @@ row_merge_sort( if applicable */ { ulint half = file->offset / 2; + ulint num_runs; + ulint* run_offset; + ulint error = DB_SUCCESS; + + /* Record the number of merge runs we need to perform */ + num_runs = file->offset; + + /* If num_runs are less than 1, nothing to merge */ + if (num_runs <= 1) { + return(error); + } + + /* "run_offset" records each run's first offset number */ + run_offset = (ulint*) mem_alloc(file->offset * sizeof(ulint)); + + /* This tells row_merge() where to start for the first round + of merge. */ + run_offset[half] = half; /* The file should always contain at least one byte (the end of file marker). Thus, it must be at least one block. */ ut_ad(file->offset > 0); + /* Merge the runs until we have one big run */ do { - ulint error; + error = row_merge(trx, index, file, block, tmpfd, + table, &num_runs, run_offset); - error = row_merge(trx, index, file, &half, - block, tmpfd, table); + UNIV_MEM_ASSERT_RW(run_offset, num_runs * sizeof *run_offset); if (error != DB_SUCCESS) { - return(error); + break; } + } while (num_runs > 1); - /* half > 0 should hold except when the file consists - of one block. No need to merge further then. */ - ut_ad(half > 0 || file->offset == 1); - } while (half < file->offset && half > 0); + mem_free(run_offset); - return(DB_SUCCESS); + return(error); } /*************************************************************//** From a910b47a8c977164c5e11c22f60d5ef1d6aa115c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 27 Feb 2012 23:19:14 +0200 Subject: [PATCH 10/18] Remove a bogus BLOB debug assertion that was added in Bug#13721257 fix. --- storage/innodb_plugin/row/row0row.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/storage/innodb_plugin/row/row0row.c b/storage/innodb_plugin/row/row0row.c index 7ec05f01821..c1f39fb68cb 100644 --- a/storage/innodb_plugin/row/row0row.c +++ b/storage/innodb_plugin/row/row0row.c @@ -245,15 +245,11 @@ row_build( #if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG if (rec_offs_any_null_extern(rec, offsets)) { /* This condition can occur during crash recovery - before trx_rollback_active() has completed execution. - - This condition is possible if the server crashed - during an insert or update-by-delete-and-insert before - btr_store_big_rec_extern_fields() did mtr_commit() all - BLOB pointers to the freshly inserted clustered index - record. */ - ut_a(trx_assert_recovered( - row_get_rec_trx_id(rec, index, offsets))); + before trx_rollback_active() has completed execution, + or when a concurrently executing + row_ins_index_entry_low() has committed the B-tree + mini-transaction but has not yet managed to restore + the cursor position for writing the big_rec. */ ut_a(trx_undo_roll_ptr_is_insert( row_get_rec_roll_ptr(rec, index, offsets))); } From ee1ea1990ec805df7d45b59f5631101959bd939f Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Tue, 28 Feb 2012 16:25:13 +0530 Subject: [PATCH 11/18] BUG#13333431 - INCORRECT DEFAULT PORT IN 'SHOW SLAVE HOSTS' OUTPUT This is a post commit patch for failing test on windows. --- mysql-test/r/mysqld--help-win.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index cd7043eb59f..bc917792239 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -923,7 +923,7 @@ relay-log-space-limit 0 replicate-same-server-id FALSE report-host (No default value) report-password (No default value) -report-port 3306 +report-port 0 report-user (No default value) rpl-recovery-rank 0 safe-user-create FALSE From 0664bb7cd0c837ed168c7f8223157e3c5ce0a03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 28 Feb 2012 14:00:00 +0200 Subject: [PATCH 12/18] Bug#12861864 RACE CONDITION IN BTR_GET_SIZE() AND DROP INDEX/TABLE/DATABASE also filed as Bug#13146269, Bug#13713178 btr_get_size(): Add mtr_t parameter. Require that the caller S-latches index->lock. If index->page==FIL_NULL or the index is to be dropped, return ULINT_UNDEFINED to indicate that the statistics are unavailable. dict_update_statistics(): If btr_get_size() returns ULINT_UNDEFINED, fake the index cardinality statistics. dict_index_set_page(): Unused function, remove. row_drop_table_for_mysql(): Before starting to drop the table, mark the indexes unavailable in the data dictionary cache while holding index->lock X-latch. ha_innobase::prepare_drop_index(), ha_innobase::final_drop_index(): When setting index->to_be_dropped, acquire the index->lock X-latch. rb:960 approved by Jimmy Yang --- storage/innodb_plugin/ChangeLog | 8 +++++ storage/innodb_plugin/btr/btr0btr.c | 26 +++++++++------- storage/innodb_plugin/dict/dict0dict.c | 22 ++++++++++---- .../innodb_plugin/handler/handler0alter.cc | 12 ++++++-- storage/innodb_plugin/include/btr0btr.h | 7 +++-- storage/innodb_plugin/include/dict0dict.h | 14 ++------- storage/innodb_plugin/include/dict0dict.ic | 21 ++----------- storage/innodb_plugin/include/dict0mem.h | 10 ++++--- storage/innodb_plugin/row/row0mysql.c | 30 +++++++++++++++++-- 9 files changed, 93 insertions(+), 57 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index d6115a28148..373335b6ff5 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,11 @@ +2012-02-28 The InnoDB Team + + * btr/btr0btr.c, dict/dict0dict.c, include/btr0btr.h, + include/dict0dict.h, include/dict0dict.ic, include/dict0mem.h, + handler/handler0alter.cc, row/row0mysql.c: + Fix Bug#12861864 RACE CONDITION IN BTR_GET_SIZE() AND + DROP INDEX/TABLE/DATABASE + 2012-02-15 The InnoDB Team * btr/btr0btr.c, btr/btr0cur.c, fsp/fsp0fsp.c, ibuf/ibuf0ibuf.c, diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index 20eb30588fa..05723c26e2f 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -991,45 +991,49 @@ btr_page_alloc( /**************************************************************//** Gets the number of pages in a B-tree. -@return number of pages */ +@return number of pages, or ULINT_UNDEFINED if the index is unavailable */ UNIV_INTERN ulint btr_get_size( /*=========*/ dict_index_t* index, /*!< in: index */ - ulint flag) /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ + ulint flag, /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ + mtr_t* mtr) /*!< in/out: mini-transaction where index + is s-latched */ { fseg_header_t* seg_header; page_t* root; ulint n; ulint dummy; - mtr_t mtr; - mtr_start(&mtr); + ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), + MTR_MEMO_S_LOCK)); - mtr_s_lock(dict_index_get_lock(index), &mtr); + if (index->page == FIL_NULL + || index->to_be_dropped + || *index->name == TEMP_INDEX_PREFIX) { + return(ULINT_UNDEFINED); + } - root = btr_root_get(index, &mtr); + root = btr_root_get(index, mtr); if (flag == BTR_N_LEAF_PAGES) { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; - fseg_n_reserved_pages(seg_header, &n, &mtr); + fseg_n_reserved_pages(seg_header, &n, mtr); } else if (flag == BTR_TOTAL_SIZE) { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; - n = fseg_n_reserved_pages(seg_header, &dummy, &mtr); + n = fseg_n_reserved_pages(seg_header, &dummy, mtr); seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; - n += fseg_n_reserved_pages(seg_header, &dummy, &mtr); + n += fseg_n_reserved_pages(seg_header, &dummy, mtr); } else { ut_error; } - mtr_commit(&mtr); - return(n); } diff --git a/storage/innodb_plugin/dict/dict0dict.c b/storage/innodb_plugin/dict/dict0dict.c index 2d2262acf87..ef3567c360d 100644 --- a/storage/innodb_plugin/dict/dict0dict.c +++ b/storage/innodb_plugin/dict/dict0dict.c @@ -4267,16 +4267,27 @@ dict_update_statistics( (srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE || (srv_force_recovery < SRV_FORCE_NO_LOG_REDO && dict_index_is_clust(index)))) { + mtr_t mtr; ulint size; - size = btr_get_size(index, BTR_TOTAL_SIZE); - index->stat_index_size = size; + mtr_start(&mtr); + mtr_s_lock(dict_index_get_lock(index), &mtr); - sum_of_index_sizes += size; + size = btr_get_size(index, BTR_TOTAL_SIZE, &mtr); - size = btr_get_size(index, BTR_N_LEAF_PAGES); + if (size != ULINT_UNDEFINED) { + sum_of_index_sizes += size; + index->stat_index_size = size; + size = btr_get_size( + index, BTR_N_LEAF_PAGES, &mtr); + } - if (size == 0) { + mtr_commit(&mtr); + + switch (size) { + case ULINT_UNDEFINED: + goto fake_statistics; + case 0: /* The root node of the tree is a leaf */ size = 1; } @@ -4293,6 +4304,7 @@ dict_update_statistics( various means, also via secondary indexes. */ ulint i; +fake_statistics: sum_of_index_sizes++; index->stat_index_size = index->stat_n_leaf_pages = 1; diff --git a/storage/innodb_plugin/handler/handler0alter.cc b/storage/innodb_plugin/handler/handler0alter.cc index 485e03737e3..e2702b157af 100644 --- a/storage/innodb_plugin/handler/handler0alter.cc +++ b/storage/innodb_plugin/handler/handler0alter.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2005, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 2005, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -1010,7 +1010,9 @@ ha_innobase::prepare_drop_index( goto func_exit; } + rw_lock_x_lock(dict_index_get_lock(index)); index->to_be_dropped = TRUE; + rw_lock_x_unlock(dict_index_get_lock(index)); } /* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined @@ -1129,7 +1131,9 @@ func_exit: = dict_table_get_first_index(prebuilt->table); do { + rw_lock_x_lock(dict_index_get_lock(index)); index->to_be_dropped = FALSE; + rw_lock_x_unlock(dict_index_get_lock(index)); index = dict_table_get_next_index(index); } while (index); } @@ -1189,7 +1193,9 @@ ha_innobase::final_drop_index( for (index = dict_table_get_first_index(prebuilt->table); index; index = dict_table_get_next_index(index)) { + rw_lock_x_lock(dict_index_get_lock(index)); index->to_be_dropped = FALSE; + rw_lock_x_unlock(dict_index_get_lock(index)); } goto func_exit; diff --git a/storage/innodb_plugin/include/btr0btr.h b/storage/innodb_plugin/include/btr0btr.h index 6e7bb8fc61e..793fbc76f3e 100644 --- a/storage/innodb_plugin/include/btr0btr.h +++ b/storage/innodb_plugin/include/btr0btr.h @@ -536,13 +536,16 @@ btr_parse_page_reorganize( #ifndef UNIV_HOTBACKUP /**************************************************************//** Gets the number of pages in a B-tree. -@return number of pages */ +@return number of pages, or ULINT_UNDEFINED if the index is unavailable */ UNIV_INTERN ulint btr_get_size( /*=========*/ dict_index_t* index, /*!< in: index */ - ulint flag); /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ + ulint flag, /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ + mtr_t* mtr) /*!< in/out: mini-transaction where index + is s-latched */ + __attribute__((nonnull, warn_unused_result)); /**************************************************************//** Allocates a new file page to be used in an index tree. NOTE: we assume that the caller has made the reservation for free extents! diff --git a/storage/innodb_plugin/include/dict0dict.h b/storage/innodb_plugin/include/dict0dict.h index fdb3bd5d50c..e728c78b9c0 100644 --- a/storage/innodb_plugin/include/dict0dict.h +++ b/storage/innodb_plugin/include/dict0dict.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -1018,14 +1018,6 @@ dict_index_get_page( /*================*/ const dict_index_t* tree); /*!< in: index */ /*********************************************************************//** -Sets the page number of the root of index tree. */ -UNIV_INLINE -void -dict_index_set_page( -/*================*/ - dict_index_t* index, /*!< in/out: index */ - ulint page); /*!< in: page number */ -/*********************************************************************//** Gets the read-write lock of the index tree. @return read-write lock */ UNIV_INLINE diff --git a/storage/innodb_plugin/include/dict0dict.ic b/storage/innodb_plugin/include/dict0dict.ic index 1704e9c2d71..f3d73b09bdb 100644 --- a/storage/innodb_plugin/include/dict0dict.ic +++ b/storage/innodb_plugin/include/dict0dict.ic @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -722,21 +722,6 @@ dict_index_get_page( return(index->page); } -/*********************************************************************//** -Sets the page number of the root of index tree. */ -UNIV_INLINE -void -dict_index_set_page( -/*================*/ - dict_index_t* index, /*!< in/out: index */ - ulint page) /*!< in: page number */ -{ - ut_ad(index); - ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); - - index->page = page; -} - /*********************************************************************//** Gets the read-write lock of the index tree. @return read-write lock */ diff --git a/storage/innodb_plugin/include/dict0mem.h b/storage/innodb_plugin/include/dict0mem.h index bd32a239cfd..1b26aea38c9 100644 --- a/storage/innodb_plugin/include/dict0mem.h +++ b/storage/innodb_plugin/include/dict0mem.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -305,7 +305,9 @@ struct dict_index_struct{ unsigned to_be_dropped:1; /*!< TRUE if this index is marked to be dropped in ha_innobase::prepare_drop_index(), - otherwise FALSE */ + otherwise FALSE. Protected by + dict_sys->mutex, dict_operation_lock and + index->lock.*/ dict_field_t* fields; /*!< array of field descriptions */ #ifndef UNIV_HOTBACKUP UT_LIST_NODE_T(dict_index_t) diff --git a/storage/innodb_plugin/row/row0mysql.c b/storage/innodb_plugin/row/row0mysql.c index 36aad4f8ca2..5f8fc6a903b 100644 --- a/storage/innodb_plugin/row/row0mysql.c +++ b/storage/innodb_plugin/row/row0mysql.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2000, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2000, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -3013,6 +3013,7 @@ row_drop_table_for_mysql( { dict_foreign_t* foreign; dict_table_t* table; + dict_index_t* index; ulint space_id; ulint err; const char* table_name; @@ -3305,6 +3306,18 @@ check_next_foreign: "END;\n" , FALSE, trx); + /* Mark all indexes unavailable in the data dictionary cache + before starting to drop the table. */ + + for (index = dict_table_get_first_index(table); + index != NULL; + index = dict_table_get_next_index(index)) { + rw_lock_x_lock(dict_index_get_lock(index)); + ut_ad(!index->to_be_dropped); + index->to_be_dropped = TRUE; + rw_lock_x_unlock(dict_index_get_lock(index)); + } + switch (err) { ibool is_temp; const char* name_or_path; @@ -3384,6 +3397,17 @@ check_next_foreign: the undo log. We can directly exit here and return the DB_TOO_MANY_CONCURRENT_TRXS error. */ + + /* Mark all indexes available in the data dictionary + cache again. */ + + for (index = dict_table_get_first_index(table); + index != NULL; + index = dict_table_get_next_index(index)) { + rw_lock_x_lock(dict_index_get_lock(index)); + index->to_be_dropped = FALSE; + rw_lock_x_unlock(dict_index_get_lock(index)); + } break; case DB_OUT_OF_FILE_SPACE: From 9b2a3e6352987e071783a6814edaa8ad33783f27 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Tue, 28 Feb 2012 19:43:09 +0530 Subject: [PATCH 13/18] BUG#13333431 - INCORRECT DEFAULT PORT IN 'SHOW SLAVE HOSTS' OUTPUT This is a post commit patch for failing test on windows. --- mysql-test/r/mysqld--help-win.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index bc917792239..db7dd264b76 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -923,7 +923,7 @@ relay-log-space-limit 0 replicate-same-server-id FALSE report-host (No default value) report-password (No default value) -report-port 0 +report-port 0 report-user (No default value) rpl-recovery-rank 0 safe-user-create FALSE From 26ed79ec14a54a9d8e7bad48090889fd9e83f1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 28 Feb 2012 21:41:55 +0200 Subject: [PATCH 14/18] Fix a mistake in the Bug#12861864 fix. row_drop_table_for_mysql(): Really flag the indexes unavailable before starting to drop the table. --- storage/innodb_plugin/row/row0mysql.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/storage/innodb_plugin/row/row0mysql.c b/storage/innodb_plugin/row/row0mysql.c index 5f8fc6a903b..d67a7b29e1c 100644 --- a/storage/innodb_plugin/row/row0mysql.c +++ b/storage/innodb_plugin/row/row0mysql.c @@ -3220,6 +3220,18 @@ check_next_foreign: trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); trx->table_id = table->id; + /* Mark all indexes unavailable in the data dictionary cache + before starting to drop the table. */ + + for (index = dict_table_get_first_index(table); + index != NULL; + index = dict_table_get_next_index(index)) { + rw_lock_x_lock(dict_index_get_lock(index)); + ut_ad(!index->to_be_dropped); + index->to_be_dropped = TRUE; + rw_lock_x_unlock(dict_index_get_lock(index)); + } + /* We use the private SQL parser of Innobase to generate the query graphs needed in deleting the dictionary data from system tables in Innobase. Deleting a row from SYS_INDEXES table also @@ -3306,18 +3318,6 @@ check_next_foreign: "END;\n" , FALSE, trx); - /* Mark all indexes unavailable in the data dictionary cache - before starting to drop the table. */ - - for (index = dict_table_get_first_index(table); - index != NULL; - index = dict_table_get_next_index(index)) { - rw_lock_x_lock(dict_index_get_lock(index)); - ut_ad(!index->to_be_dropped); - index->to_be_dropped = TRUE; - rw_lock_x_unlock(dict_index_get_lock(index)); - } - switch (err) { ibool is_temp; const char* name_or_path; From 9af695fb4587a6e53241d05635caaaa113ac95d8 Mon Sep 17 00:00:00 2001 From: Praveenkumar Hulakund Date: Wed, 29 Feb 2012 12:23:15 +0530 Subject: [PATCH 15/18] Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES IGNORED AND BREAKS REPLICATION Analysis: ======================== sql_mode "NO_BACKSLASH_ESCAPES": When user want to use backslash as character input, instead of escape character in a string literal then sql_mode can be set to "NO_BACKSLASH_ESCAPES". With this mode enabled, backslash becomes an ordinary character like any other. SQL_MODE set applies to the current client session. And while creating the stored procedure, MySQL stores the current sql_mode and always executes the stored procedure in sql_mode stored with the Procedure, regardless of the server SQL mode in effect when the routine is invoked. In the scenario (for which bug is reported), the routine is created with sql_mode=NO_BACKSLASH_ESCAPES. And routine is executed with the invoker sql_mode is "" (NOT SET) by executing statement "call testp('Axel\'s')". Since invoker sql_mode is "" (NOT_SET), the '\' in 'Axel\'s'(argument to function) is considered as escape character and column "a" (of table "t1") values are updated with "Axel's". The binary log generated for above update operation is as below, set sql_mode=XXXXXX (for no_backslash_escapes) update test.t1 set a= NAME_CONST('var',_latin1'Axel\'s' COLLATE 'latin1_swedish_ci'); While logging stored procedure statements, the local variables (params) used in statements are replaced with the NAME_CONST(var_name, var_value) (Internal function) (http://dev.mysql.com/doc/refman/5.6/en/miscellaneous-functions.html#function_name-const) On slave, these logs are applied. NAME_CONST is parsed to get the variable and its value. Since, stored procedure is created with sql_mode="NO_BACKSLASH_ESCAPES", the sql_mode is also logged in. So that at slave this sql_mode is set before executing the statements of routine. So at slave, sql_mode is set to "NO_BACKSLASH_ESCAPES" and then while parsing NAME_CONST of string variable, '\' is considered as NON ESCAPE character and parsing reported error for "'" (as we have only one "'" no backslash). At slave, parsing was proper with sql_mode "NO_BACKSLASH_ESCAPES". But above error reported while writing bin log, "'" (of Axel's) is escaped with "\" character. Actually, all special characters (n, r, ', ", \, 0...) are escaped while writing NAME_CONST for string variable(param, local variable) in bin log Airrespective of "NO_BACKSLASH_ESCAPES" sql_mode. So, basically, the problem is that logging string parameter does not take into account sql_mode value. Fix: ======================== So when sql_mode is set to "NO_BACKSLASH_ESCAPES", escaping characters as (n, r, ', ", \, 0...) should be avoided. To do so, added a check to not to escape such characters while writing NAME_CONST for string variables in bin log. And when sql_mode is set to NO_BACKSLASH_ESCAPES, quote character "'" is represented as ''. http://dev.mysql.com/doc/refman/5.6/en/string-literals.html (There are several ways to include quote characters within a string: ) --- mysql-test/r/sql_mode.result | 204 ++++++++++++++++++ .../suite/binlog/r/binlog_sql_mode.result | 111 ++++++++++ .../suite/binlog/t/binlog_sql_mode.test | 96 +++++++++ mysql-test/t/sql_mode.test | 154 +++++++++++++ sql/item.cc | 7 +- sql/item.h | 2 +- sql/log_event.cc | 25 ++- sql/mysql_priv.h | 2 +- sql/sp_head.cc | 2 +- sql/sql_prepare.cc | 6 +- 10 files changed, 596 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result index 0b0d5a38d0b..fb192c25087 100644 --- a/mysql-test/r/sql_mode.result +++ b/mysql-test/r/sql_mode.result @@ -527,3 +527,207 @@ SELECT * FROM mysql.columns_priv WHERE Host = 'localhost' AND User LIKE 'user_%P Host Db User Table_name Column_name Timestamp Column_priv DROP TABLE t1; DROP TABLE t2; + +# +# Test for Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES +# IGNORED AND BREAKS REPLICATION +# +DROP TABLE IF EXISTS test_table; +DROP FUNCTION IF EXISTS test_function; +CREATE TABLE test_table (c1 CHAR(50)); +SET @org_mode=@@sql_mode; +SET @@sql_mode=''; +PREPARE insert_stmt FROM 'INSERT INTO test_table VALUES (?)'; +PREPARE update_stmt FROM 'UPDATE test_table SET c1= ? WHERE c1= ?'; +CREATE FUNCTION test_function(var CHAR(50)) RETURNS CHAR(50) +BEGIN +DECLARE char_val CHAR(50); +SELECT c1 INTO char_val FROM test_table WHERE c1=var; +RETURN char_val; +END +$ +SET @var1='abcd\'ef'; +SET @var2='abcd\"ef'; +SET @var3='abcd\bef'; +SET @var4='abcd\nef'; +SET @var5='abcd\ref'; +SET @var6='abcd\tef'; +SET @var7='abcd\\ef'; +SET @var8='abcd\%ef'; +SET @var9='abcd\_ef'; +SET @to_var1='wxyz\'ef'; +SET @to_var2='wxyz\"ef'; +SET @to_var3='wxyz\bef'; +SET @to_var4='wxyz\nef'; +SET @to_var5='wxyz\ref'; +SET @to_var6='wxyz\tef'; +SET @to_var7='wxyz\\ef'; +SET @to_var8='wxyz\%ef'; +SET @to_var9='wxyz\_ef'; +# STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT +EXECUTE insert_stmt USING @var1; +EXECUTE insert_stmt USING @var2; +EXECUTE insert_stmt USING @var3; +EXECUTE insert_stmt USING @var4; +EXECUTE insert_stmt USING @var5; +EXECUTE insert_stmt USING @var6; +EXECUTE insert_stmt USING @var7; +EXECUTE insert_stmt USING @var8; +EXECUTE insert_stmt USING @var9; +SELECT * FROM test_table; +c1 +abcd'ef +abcd"ef +abcdef +abcd +ef +abcd ef +abcd ef +abcd\ef +abcd\%ef +abcd\_ef +EXECUTE update_stmt USING @to_var1, @var1; +EXECUTE update_stmt USING @to_var2, @var2; +EXECUTE update_stmt USING @to_var3, @var3; +EXECUTE update_stmt USING @to_var4, @var4; +EXECUTE update_stmt USING @to_var5, @var5; +EXECUTE update_stmt USING @to_var6, @var6; +EXECUTE update_stmt USING @to_var7, @var7; +EXECUTE update_stmt USING @to_var8, @var8; +EXECUTE update_stmt USING @to_var9, @var9; +SELECT * FROM test_table; +c1 +wxyz'ef +wxyz"ef +wxyzef +wxyz +ef +wxyz ef +wxyz ef +wxyz\ef +wxyz\%ef +wxyz\_ef + +# END OF CASE - STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT +# STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING +select test_function(@to_var1); +test_function(@to_var1) +wxyz'ef +SELECT test_function(@to_var2); +test_function(@to_var2) +wxyz"ef +SELECT test_function(@to_var3); +test_function(@to_var3) +wxyzef +SELECT test_function(@to_var4); +test_function(@to_var4) +wxyz +ef +SELECT test_function(@to_var5); +test_function(@to_var5) +wxyz ef +SELECT test_function(@to_var6); +test_function(@to_var6) +wxyz ef +SELECT test_function(@to_var7); +test_function(@to_var7) +wxyz\ef +SELECT test_function(@to_var8); +test_function(@to_var8) +wxyz\%ef +SELECT test_function(@to_var9); +test_function(@to_var9) +wxyz\_ef + +# END OF CASE - STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING +DELETE FROM test_table; +DROP FUNCTION test_function; +SET @@sql_mode='NO_BACKSLASH_ESCAPES'; +CREATE FUNCTION test_function(var CHAR(50)) RETURNS CHAR(50) +BEGIN +DECLARE char_val CHAR(50); +SELECT c1 INTO char_val FROM test_table WHERE c1=var; +RETURN char_val; +END +$ +# STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT +EXECUTE insert_stmt USING @var1; +EXECUTE insert_stmt USING @var2; +EXECUTE insert_stmt USING @var3; +EXECUTE insert_stmt USING @var4; +EXECUTE insert_stmt USING @var5; +EXECUTE insert_stmt USING @var6; +EXECUTE insert_stmt USING @var7; +EXECUTE insert_stmt USING @var8; +EXECUTE insert_stmt USING @var9; +SELECT * FROM test_table; +c1 +abcd'ef +abcd"ef +abcdef +abcd +ef +abcd ef +abcd ef +abcd\ef +abcd\%ef +abcd\_ef +EXECUTE update_stmt USING @to_var1, @var1; +EXECUTE update_stmt USING @to_var2, @var2; +EXECUTE update_stmt USING @to_var3, @var3; +EXECUTE update_stmt USING @to_var4, @var4; +EXECUTE update_stmt USING @to_var5, @var5; +EXECUTE update_stmt USING @to_var6, @var6; +EXECUTE update_stmt USING @to_var7, @var7; +EXECUTE update_stmt USING @to_var8, @var8; +EXECUTE update_stmt USING @to_var9, @var9; +SELECT * FROM test_table; +c1 +wxyz'ef +wxyz"ef +wxyzef +wxyz +ef +wxyz ef +wxyz ef +wxyz\ef +wxyz\%ef +wxyz\_ef + +# END OF CASE - STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT +# STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING +select test_function(@to_var1); +test_function(@to_var1) +wxyz'ef +SELECT test_function(@to_var2); +test_function(@to_var2) +wxyz"ef +SELECT test_function(@to_var3); +test_function(@to_var3) +wxyzef +SELECT test_function(@to_var4); +test_function(@to_var4) +wxyz +ef +SELECT test_function(@to_var5); +test_function(@to_var5) +wxyz ef +SELECT test_function(@to_var6); +test_function(@to_var6) +wxyz ef +SELECT test_function(@to_var7); +test_function(@to_var7) +wxyz\ef +SELECT test_function(@to_var8); +test_function(@to_var8) +wxyz\%ef +SELECT test_function(@to_var9); +test_function(@to_var9) +wxyz\_ef + +# END OF CASE - STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING +DROP TABLE test_table; +DROP FUNCTION test_function; +SET @@sql_mode= @org_mode; + +#End of Test for Bug#12601974 diff --git a/mysql-test/suite/binlog/r/binlog_sql_mode.result b/mysql-test/suite/binlog/r/binlog_sql_mode.result index 4477c94a95e..1aea77c4a4b 100644 --- a/mysql-test/suite/binlog/r/binlog_sql_mode.result +++ b/mysql-test/suite/binlog/r/binlog_sql_mode.result @@ -38,3 +38,114 @@ DROP VIEW testView; DROP TABLE t1; SET @@global.sql_mode= @old_sql_mode; SET @@session.binlog_format=@old_binlog_format; + +# +# Test for Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES +# IGNORED AND BREAKS REPLICATION +# +DROP DATABASE IF EXISTS mysqltest_db; +DROP TABLE IF EXISTS test_table; +CREATE DATABASE mysqltest_db; +USE mysqltest_db; +CREATE TABLE test_table (c1 CHAR(50)); +SET @org_mode=@@sql_mode; +SET @@sql_mode=''; +CREATE PROCEDURE proc_without_sql_mode (IN param1 CHAR(50), IN param2 CHAR(50)) +BEGIN +DECLARE var1 CHAR(50) DEFAULT param1; +DECLARE var2 CHAR(50) DEFAULT param2; +DECLARE var3 CHAR(50) DEFAULT 'abcd\bef'; +DECLARE var4 CHAR(50) DEFAULT 'abcd\nef'; +DECLARE var5 CHAR(50) DEFAULT 'abcd\ref'; +DECLARE var6 CHAR(50) DEFAULT 'abcd\tef'; +DECLARE var7 CHAR(50) DEFAULT 'abcd\\ef'; +DECLARE var8 CHAR(50) DEFAULT 'abcd\%ef'; +DECLARE var9 CHAR(50) DEFAULT 'abcd\_ef'; +INSERT INTO test_table VALUES (var1); +INSERT INTO test_table VALUES (var2); +INSERT INTO test_table VALUES (var3); +INSERT INTO test_table VALUES (var4); +INSERT INTO test_table VALUES (var5); +INSERT INTO test_table VALUES (var6); +INSERT INTO test_table VALUES (var7); +INSERT INTO test_table VALUES (var8); +INSERT INTO test_table VALUES (var9); +END +$ +SET @@sql_mode='NO_BACKSLASH_ESCAPES'$ +CREATE PROCEDURE proc_with_sql_mode (IN param1 CHAR(50), IN param2 CHAR(50)) +BEGIN +DECLARE var1 CHAR(50) DEFAULT param1; +DECLARE var2 CHAR(50) DEFAULT param2; +DECLARE var3 CHAR(50) DEFAULT 'wxyz\bef'; +DECLARE var4 CHAR(50) DEFAULT 'wxyz\nef'; +DECLARE var5 CHAR(50) DEFAULT 'wxyz\ref'; +DECLARE var6 CHAR(50) DEFAULT 'wxyz\tef'; +DECLARE var7 CHAR(50) DEFAULT 'wxyz\\ef'; +DECLARE var8 CHAR(50) DEFAULT 'wxyz\%ef'; +DECLARE var9 CHAR(50) DEFAULT 'wxyz\_ef'; +INSERT INTO test_table VALUES (var1); +INSERT INTO test_table VALUES (var2); +INSERT INTO test_table VALUES (var3); +INSERT INTO test_table VALUES (var4); +INSERT INTO test_table VALUES (var5); +INSERT INTO test_table VALUES (var6); +INSERT INTO test_table VALUES (var7); +INSERT INTO test_table VALUES (var8); +INSERT INTO test_table VALUES (var9); +END +$ +SET @@sql_mode=''; +CALL proc_without_sql_mode('abcd\'ef', 'abcd\"ef'); +CALL proc_with_sql_mode('wxyz\'ef', 'wxyz\"ef'); +SELECT * FROM test_table; +c1 +abcd'ef +abcd"ef +abcdef +abcd +ef +abcd ef +abcd ef +abcd\ef +abcd\%ef +abcd\_ef +wxyz'ef +wxyz"ef +wxyz\bef +wxyz\nef +wxyz\ref +wxyz\tef +wxyz\\ef +wxyz\%ef +wxyz\_ef +"Dropping table test_table" +DROP TABLE test_table; +#"test_table" content after replaying the binlog +SELECT * FROM test_table; +c1 +abcd'ef +abcd"ef +abcdef +abcd +ef +abcd ef +abcd ef +abcd\ef +abcd\%ef +abcd\_ef +wxyz'ef +wxyz"ef +wxyz\bef +wxyz\nef +wxyz\ref +wxyz\tef +wxyz\\ef +wxyz\%ef +wxyz\_ef +#Clean up +DROP DATABASE mysqltest_db; +SET @@sql_mode= @org_mode; +use test; + +#End of Test for Bug#12601974 diff --git a/mysql-test/suite/binlog/t/binlog_sql_mode.test b/mysql-test/suite/binlog/t/binlog_sql_mode.test index ab4f6450543..167c8f5a96d 100644 --- a/mysql-test/suite/binlog/t/binlog_sql_mode.test +++ b/mysql-test/suite/binlog/t/binlog_sql_mode.test @@ -73,3 +73,99 @@ DROP TABLE t1; SET @@global.sql_mode= @old_sql_mode; SET @@session.binlog_format=@old_binlog_format; + +--echo +--echo # +--echo # Test for Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES +--echo # IGNORED AND BREAKS REPLICATION +--echo # + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest_db; +DROP TABLE IF EXISTS test_table; +--enable_warnings + +CREATE DATABASE mysqltest_db; +USE mysqltest_db; +CREATE TABLE test_table (c1 CHAR(50)); + +SET @org_mode=@@sql_mode; + +SET @@sql_mode=''; +DELIMITER $; +CREATE PROCEDURE proc_without_sql_mode (IN param1 CHAR(50), IN param2 CHAR(50)) +BEGIN + DECLARE var1 CHAR(50) DEFAULT param1; + DECLARE var2 CHAR(50) DEFAULT param2; + DECLARE var3 CHAR(50) DEFAULT 'abcd\bef'; + DECLARE var4 CHAR(50) DEFAULT 'abcd\nef'; + DECLARE var5 CHAR(50) DEFAULT 'abcd\ref'; + DECLARE var6 CHAR(50) DEFAULT 'abcd\tef'; + DECLARE var7 CHAR(50) DEFAULT 'abcd\\ef'; + DECLARE var8 CHAR(50) DEFAULT 'abcd\%ef'; + DECLARE var9 CHAR(50) DEFAULT 'abcd\_ef'; + + INSERT INTO test_table VALUES (var1); + INSERT INTO test_table VALUES (var2); + INSERT INTO test_table VALUES (var3); + INSERT INTO test_table VALUES (var4); + INSERT INTO test_table VALUES (var5); + INSERT INTO test_table VALUES (var6); + INSERT INTO test_table VALUES (var7); + INSERT INTO test_table VALUES (var8); + INSERT INTO test_table VALUES (var9); +END +$ + +SET @@sql_mode='NO_BACKSLASH_ESCAPES'$ +CREATE PROCEDURE proc_with_sql_mode (IN param1 CHAR(50), IN param2 CHAR(50)) +BEGIN + DECLARE var1 CHAR(50) DEFAULT param1; + DECLARE var2 CHAR(50) DEFAULT param2; + DECLARE var3 CHAR(50) DEFAULT 'wxyz\bef'; + DECLARE var4 CHAR(50) DEFAULT 'wxyz\nef'; + DECLARE var5 CHAR(50) DEFAULT 'wxyz\ref'; + DECLARE var6 CHAR(50) DEFAULT 'wxyz\tef'; + DECLARE var7 CHAR(50) DEFAULT 'wxyz\\ef'; + DECLARE var8 CHAR(50) DEFAULT 'wxyz\%ef'; + DECLARE var9 CHAR(50) DEFAULT 'wxyz\_ef'; + + INSERT INTO test_table VALUES (var1); + INSERT INTO test_table VALUES (var2); + INSERT INTO test_table VALUES (var3); + INSERT INTO test_table VALUES (var4); + INSERT INTO test_table VALUES (var5); + INSERT INTO test_table VALUES (var6); + INSERT INTO test_table VALUES (var7); + INSERT INTO test_table VALUES (var8); + INSERT INTO test_table VALUES (var9); +END +$ + +DELIMITER ;$ +SET @@sql_mode=''; +CALL proc_without_sql_mode('abcd\'ef', 'abcd\"ef'); +CALL proc_with_sql_mode('wxyz\'ef', 'wxyz\"ef'); +SELECT * FROM test_table; + +let $MYSQLD_DATADIR= `select @@datadir`; +--exec $MYSQL_BINLOG --force-if-open -d mysqltest_db $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_bug12601974.binlog + +--echo "Dropping table test_table" +DROP TABLE test_table; + +--exec $MYSQL -e "source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_bug12601974.binlog" + +--echo #"test_table" content after replaying the binlog +SELECT * FROM test_table; + +--echo #Clean up +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_bug12601974.binlog +DROP DATABASE mysqltest_db; +SET @@sql_mode= @org_mode; +use test; + +--echo +--echo #End of Test for Bug#12601974 + + diff --git a/mysql-test/t/sql_mode.test b/mysql-test/t/sql_mode.test index 4a9f34443cb..526791d9a42 100644 --- a/mysql-test/t/sql_mode.test +++ b/mysql-test/t/sql_mode.test @@ -344,3 +344,157 @@ SELECT * FROM mysql.columns_priv WHERE Host = 'localhost' AND User LIKE 'user_%P # Cleanup DROP TABLE t1; DROP TABLE t2; + + +--echo +--echo # +--echo # Test for Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES +--echo # IGNORED AND BREAKS REPLICATION +--echo # + +--disable_warnings +DROP TABLE IF EXISTS test_table; +DROP FUNCTION IF EXISTS test_function; +--enable_warnings + +CREATE TABLE test_table (c1 CHAR(50)); + +SET @org_mode=@@sql_mode; + +SET @@sql_mode=''; + +PREPARE insert_stmt FROM 'INSERT INTO test_table VALUES (?)'; +PREPARE update_stmt FROM 'UPDATE test_table SET c1= ? WHERE c1= ?'; +DELIMITER $; +CREATE FUNCTION test_function(var CHAR(50)) RETURNS CHAR(50) +BEGIN + DECLARE char_val CHAR(50); + SELECT c1 INTO char_val FROM test_table WHERE c1=var; + RETURN char_val; +END +$ +DELIMITER ;$ + +SET @var1='abcd\'ef'; +SET @var2='abcd\"ef'; +SET @var3='abcd\bef'; +SET @var4='abcd\nef'; +SET @var5='abcd\ref'; +SET @var6='abcd\tef'; +SET @var7='abcd\\ef'; +SET @var8='abcd\%ef'; +SET @var9='abcd\_ef'; + +SET @to_var1='wxyz\'ef'; +SET @to_var2='wxyz\"ef'; +SET @to_var3='wxyz\bef'; +SET @to_var4='wxyz\nef'; +SET @to_var5='wxyz\ref'; +SET @to_var6='wxyz\tef'; +SET @to_var7='wxyz\\ef'; +SET @to_var8='wxyz\%ef'; +SET @to_var9='wxyz\_ef'; + +--echo # STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT +EXECUTE insert_stmt USING @var1; +EXECUTE insert_stmt USING @var2; +EXECUTE insert_stmt USING @var3; +EXECUTE insert_stmt USING @var4; +EXECUTE insert_stmt USING @var5; +EXECUTE insert_stmt USING @var6; +EXECUTE insert_stmt USING @var7; +EXECUTE insert_stmt USING @var8; +EXECUTE insert_stmt USING @var9; + +SELECT * FROM test_table; + +EXECUTE update_stmt USING @to_var1, @var1; +EXECUTE update_stmt USING @to_var2, @var2; +EXECUTE update_stmt USING @to_var3, @var3; +EXECUTE update_stmt USING @to_var4, @var4; +EXECUTE update_stmt USING @to_var5, @var5; +EXECUTE update_stmt USING @to_var6, @var6; +EXECUTE update_stmt USING @to_var7, @var7; +EXECUTE update_stmt USING @to_var8, @var8; +EXECUTE update_stmt USING @to_var9, @var9; + +SELECT * FROM test_table; + +--echo +--echo # END OF CASE - STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT + +--echo # STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING +select test_function(@to_var1); +SELECT test_function(@to_var2); +SELECT test_function(@to_var3); +SELECT test_function(@to_var4); +SELECT test_function(@to_var5); +SELECT test_function(@to_var6); +SELECT test_function(@to_var7); +SELECT test_function(@to_var8); +SELECT test_function(@to_var9); + +--echo +--echo # END OF CASE - STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING +DELETE FROM test_table; +DROP FUNCTION test_function; + +SET @@sql_mode='NO_BACKSLASH_ESCAPES'; +DELIMITER $; +CREATE FUNCTION test_function(var CHAR(50)) RETURNS CHAR(50) +BEGIN + DECLARE char_val CHAR(50); + SELECT c1 INTO char_val FROM test_table WHERE c1=var; + RETURN char_val; +END +$ +DELIMITER ;$ + +--echo # STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT +EXECUTE insert_stmt USING @var1; +EXECUTE insert_stmt USING @var2; +EXECUTE insert_stmt USING @var3; +EXECUTE insert_stmt USING @var4; +EXECUTE insert_stmt USING @var5; +EXECUTE insert_stmt USING @var6; +EXECUTE insert_stmt USING @var7; +EXECUTE insert_stmt USING @var8; +EXECUTE insert_stmt USING @var9; + +SELECT * FROM test_table; + +EXECUTE update_stmt USING @to_var1, @var1; +EXECUTE update_stmt USING @to_var2, @var2; +EXECUTE update_stmt USING @to_var3, @var3; +EXECUTE update_stmt USING @to_var4, @var4; +EXECUTE update_stmt USING @to_var5, @var5; +EXECUTE update_stmt USING @to_var6, @var6; +EXECUTE update_stmt USING @to_var7, @var7; +EXECUTE update_stmt USING @to_var8, @var8; +EXECUTE update_stmt USING @to_var9, @var9; + +SELECT * FROM test_table; + +--echo +--echo # END OF CASE - STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT + +--echo # STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING +select test_function(@to_var1); +SELECT test_function(@to_var2); +SELECT test_function(@to_var3); +SELECT test_function(@to_var4); +SELECT test_function(@to_var5); +SELECT test_function(@to_var6); +SELECT test_function(@to_var7); +SELECT test_function(@to_var8); +SELECT test_function(@to_var9); + +--echo +--echo # END OF CASE - STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING + +DROP TABLE test_table; +DROP FUNCTION test_function; +SET @@sql_mode= @org_mode; + +--echo +--echo #End of Test for Bug#12601974 diff --git a/sql/item.cc b/sql/item.cc index f80d862e856..2a7d2453889 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3085,7 +3085,7 @@ String *Item_param::val_str(String* str) that binary log contains wrong statement */ -const String *Item_param::query_val_str(String* str) const +const String *Item_param::query_val_str(THD *thd, String* str) const { switch (state) { case INT_VALUE: @@ -3123,7 +3123,8 @@ const String *Item_param::query_val_str(String* str) const case LONG_DATA_VALUE: { str->length(0); - append_query_string(value.cs_info.character_set_client, &str_value, str); + append_query_string(thd, value.cs_info.character_set_client, &str_value, + str); break; } case NULL_VALUE: @@ -3256,7 +3257,7 @@ void Item_param::print(String *str, enum_query_type query_type) char buffer[STRING_BUFFER_USUAL_SIZE]; String tmp(buffer, sizeof(buffer), &my_charset_bin); const String *res; - res= query_val_str(&tmp); + res= query_val_str(current_thd, &tmp); str->append(*res); } } diff --git a/sql/item.h b/sql/item.h index 8d7ad3c41d3..1934e005776 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1703,7 +1703,7 @@ public: */ void (*set_param_func)(Item_param *param, uchar **pos, ulong len); - const String *query_val_str(String *str) const; + const String *query_val_str(THD *thd, String *str) const; bool convert_str_value(THD *thd); diff --git a/sql/log_event.cc b/sql/log_event.cc index fac3e3f264b..427f27650dd 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -572,7 +572,7 @@ char *str_to_hex(char *to, const char *from, uint len) */ int -append_query_string(CHARSET_INFO *csinfo, +append_query_string(THD *thd, CHARSET_INFO *csinfo, String const *from, String *to) { char *beg, *ptr; @@ -587,9 +587,26 @@ append_query_string(CHARSET_INFO *csinfo, else { *ptr++= '\''; - ptr+= escape_string_for_mysql(csinfo, ptr, 0, - from->ptr(), from->length()); - *ptr++='\''; + if (!(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)) + { + ptr+= escape_string_for_mysql(csinfo, ptr, 0, + from->ptr(), from->length()); + } + else + { + const char *frm_str= from->ptr(); + + for (; frm_str < (from->ptr() + from->length()); frm_str++) + { + /* Using '' way to represent "'" */ + if (*frm_str == '\'') + *ptr++= *frm_str; + + *ptr++= *frm_str; + } + } + + *ptr++= '\''; } to->length(orig_len + ptr - beg); return 0; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4b89a7a7961..359e84a3370 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -799,7 +799,7 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables); bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); -int append_query_string(CHARSET_INFO *csinfo, +int append_query_string(THD *thd, CHARSET_INFO *csinfo, String const *from, String *to); void get_default_definer(THD *thd, LEX_USER *definer); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8bea0be0f56..db9142e47f5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -157,7 +157,7 @@ sp_get_item_value(THD *thd, Item *item, String *str) buf.append(result->charset()->csname); if (cs->escape_with_backslash_is_dangerous) buf.append(' '); - append_query_string(cs, result, &buf); + append_query_string(thd, cs, result, &buf); buf.append(" COLLATE '"); buf.append(item->collation.collation->name); buf.append('\''); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ec7a7fb73b8..98379dba9ba 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -795,7 +795,7 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array, */ else if (! is_param_long_data_type(param)) DBUG_RETURN(1); - res= param->query_val_str(&str); + res= param->query_val_str(thd, &str); if (param->convert_str_value(thd)) DBUG_RETURN(1); /* out of memory */ @@ -969,7 +969,7 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt, DBUG_RETURN(1); } } - res= param->query_val_str(&str); + res= param->query_val_str(thd, &str); if (param->convert_str_value(thd)) DBUG_RETURN(1); /* out of memory */ @@ -1115,7 +1115,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, setup_one_conversion_function(thd, param, param->param_type); if (param->set_from_user_var(thd, entry)) DBUG_RETURN(1); - val= param->query_val_str(&buf); + val= param->query_val_str(thd, &buf); if (param->convert_str_value(thd)) DBUG_RETURN(1); /* out of memory */ From 56093e64f17a035db29f11d835534bff3e7f8fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 6 Mar 2012 13:31:18 +0200 Subject: [PATCH 16/18] Remove a duplicate mysql-test file innodb-index.inc. --- .../suite/innodb/include/innodb-index.inc | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 mysql-test/suite/innodb/include/innodb-index.inc diff --git a/mysql-test/suite/innodb/include/innodb-index.inc b/mysql-test/suite/innodb/include/innodb-index.inc deleted file mode 100644 index 37de3162abe..00000000000 --- a/mysql-test/suite/innodb/include/innodb-index.inc +++ /dev/null @@ -1,26 +0,0 @@ ---eval create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb default charset=$charset -insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe'); -commit; ---error ER_DUP_ENTRY -alter table t1 add unique index (b); -insert into t1 values(8,9,'fff','fff'); -select * from t1; -show create table t1; -alter table t1 add index (b); -insert into t1 values(10,10,'kkk','iii'); -select * from t1; -select * from t1 force index(b) order by b; -explain select * from t1 force index(b) order by b; -show create table t1; -alter table t1 add unique index (c), add index (d); -insert into t1 values(11,11,'aaa','mmm'); -select * from t1; -select * from t1 force index(b) order by b; -select * from t1 force index(c) order by c; -select * from t1 force index(d) order by d; -explain select * from t1 force index(b) order by b; -explain select * from t1 force index(c) order by c; -explain select * from t1 force index(d) order by d; -show create table t1; -check table t1; -drop table t1; From 268bf557a998c4933d4483e1368be9ef43755f23 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Tue, 6 Mar 2012 13:30:30 +0100 Subject: [PATCH 17/18] Bug#11761576 54082: HANDLE_SEGFAULT MAKES USE OF UNSAFE FUNCTIONS Post-push fixes. --- mysys/stacktrace.c | 2 +- sql/mysqld.cc | 4 ---- sql/signal_handler.cc | 1 + 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index b2eae0fb4c6..8ef98f197b8 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -170,7 +170,7 @@ void my_print_stacktrace(uchar* stack_bottom __attribute__((unused)), my_safe_printf_stderr("%s", "Error when traversing the stack, stack appears corrupt.\n"); else - my_safe_printf_stderr("%s" + my_safe_printf_stderr("%s", "Please read " "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" "and follow instructions on how to resolve the stack trace.\n" diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c47d39721b9..b4dd3108707 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2468,10 +2468,6 @@ static void check_data_home(const char *path) #endif /*__WIN__ || __NETWARE */ -#ifdef HAVE_LINUXTHREADS -#define UNSAFE_DEFAULT_LINUX_THREADS 200 -#endif - #if BACKTRACE_DEMANGLE #include diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 54b456ec2c4..121338c31d2 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -131,6 +131,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) "Hope that's ok; if not, decrease some variables in the equation.\n\n"); #if defined(HAVE_LINUXTHREADS) +#define UNSAFE_DEFAULT_LINUX_THREADS 200 if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) { my_safe_printf_stderr( From 76e064e209247bef208c119bd7a1d01965f06149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 8 Mar 2012 14:56:22 +0200 Subject: [PATCH 18/18] Bug#13807811 BTR_PCUR_RESTORE_POSITION() CAN SKIP A RECORD This bug has been there at least since MySQL 4.0.9. (Before 4.0.9, the code probably was even more severely broken.) btr_pcur_restore_position(): When cursor restoration fails, before invoking btr_pcur_store_position() move to the previous or next record unless cursor->rel_pos==BTR_PCUR_ON or the record was not a user record. This bug can cause skipped records when btr_pcur_store_position() is called on the last record of a page. A symptom would be record count mismatch in CHECK TABLE, or failure to find a record to delete-mark or update or purge. The following operations should be affected by the bug: * row_search_for_mysql(): SELECT, UPDATE, REPLACE, CHECK TABLE, (almost anything else than INSERT) * foreign key CASCADE operations * row_merge_read_clustered_index(): index creation (since MySQL 5.1 InnoDB Plugin) * multi-threaded purge (after MySQL 5.5): not sure, but it might fail to purge some records Not all callers of btr_pcur_restore_position() should be affected. Anything that asserts or checks that restoration succeeds is unaffected. For example, cursor restoration on the change buffer tree should always succeed, because access is being protected by additional latches. Likewise, rollback, or any code accesses data dictionary tables while holding dict_sys->mutex should be safe. rb:967 approved by Jimmy Yang --- storage/innobase/btr/btr0pcur.c | 66 +++++++++++++++++++-------- storage/innodb_plugin/ChangeLog | 5 ++ storage/innodb_plugin/btr/btr0pcur.c | 68 ++++++++++++++++++++-------- 3 files changed, 99 insertions(+), 40 deletions(-) diff --git a/storage/innobase/btr/btr0pcur.c b/storage/innobase/btr/btr0pcur.c index 8d473794243..439ab3093b2 100644 --- a/storage/innobase/btr/btr0pcur.c +++ b/storage/innobase/btr/btr0pcur.c @@ -291,13 +291,20 @@ btr_pcur_restore_position( /* Save the old search mode of the cursor */ old_mode = cursor->search_mode; - if (UNIV_LIKELY(cursor->rel_pos == BTR_PCUR_ON)) { + switch (cursor->rel_pos) { + case BTR_PCUR_ON: mode = PAGE_CUR_LE; - } else if (cursor->rel_pos == BTR_PCUR_AFTER) { + break; + case BTR_PCUR_AFTER: mode = PAGE_CUR_G; - } else { - ut_ad(cursor->rel_pos == BTR_PCUR_BEFORE); + break; + case BTR_PCUR_BEFORE: mode = PAGE_CUR_L; + break; +#ifdef UNIV_DEBUG + default: + ut_error; +#endif /* UNIV_DEBUG */ } btr_pcur_open_with_no_init(index, tuple, mode, latch_mode, @@ -306,26 +313,45 @@ btr_pcur_restore_position( /* Restore the old search mode */ cursor->search_mode = old_mode; - if (cursor->rel_pos == BTR_PCUR_ON - && btr_pcur_is_on_user_rec(cursor, mtr) - && 0 == cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor), - rec_get_offsets( - btr_pcur_get_rec(cursor), index, - NULL, ULINT_UNDEFINED, &heap))) { + if (btr_pcur_is_on_user_rec(cursor, mtr)) { + switch (cursor->rel_pos) { + case BTR_PCUR_ON: + if (!cmp_dtuple_rec( + tuple, btr_pcur_get_rec(cursor), + rec_get_offsets(btr_pcur_get_rec(cursor), + index, NULL, + ULINT_UNDEFINED, &heap))) { - /* We have to store the NEW value for the modify clock, since - the cursor can now be on a different page! But we can retain - the value of old_rec */ + /* We have to store the NEW value for + the modify clock, since the cursor can + now be on a different page! But we can + retain the value of old_rec */ - cursor->block_when_stored = buf_block_align( - btr_pcur_get_page(cursor)); - cursor->modify_clock = buf_block_get_modify_clock( - cursor->block_when_stored); - cursor->old_stored = BTR_PCUR_OLD_STORED; + cursor->block_when_stored = + buf_block_align( + btr_pcur_get_page(cursor)); + cursor->modify_clock = + buf_block_get_modify_clock( + cursor->block_when_stored); + cursor->old_stored = BTR_PCUR_OLD_STORED; - mem_heap_free(heap); + mem_heap_free(heap); - return(TRUE); + return(TRUE); + } + + break; + case BTR_PCUR_BEFORE: + page_cur_move_to_next(btr_pcur_get_page_cur(cursor)); + break; + case BTR_PCUR_AFTER: + page_cur_move_to_prev(btr_pcur_get_page_cur(cursor)); + break; +#ifdef UNIV_DEBUG + default: + ut_error; +#endif /* UNIV_DEBUG */ + } } mem_heap_free(heap); diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 373335b6ff5..b43aed5cc06 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2012-03-08 The InnoDB Team + + * btr/btr0pcur.c: + Fix Bug#13807811 BTR_PCUR_RESTORE_POSITION() CAN SKIP A RECORD + 2012-02-28 The InnoDB Team * btr/btr0btr.c, dict/dict0dict.c, include/btr0btr.h, diff --git a/storage/innodb_plugin/btr/btr0pcur.c b/storage/innodb_plugin/btr/btr0pcur.c index 57d9752649f..6cbfd8a64b7 100644 --- a/storage/innodb_plugin/btr/btr0pcur.c +++ b/storage/innodb_plugin/btr/btr0pcur.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -121,6 +121,8 @@ btr_pcur_store_position( ut_a(btr_page_get_next(page, mtr) == FIL_NULL); ut_a(btr_page_get_prev(page, mtr) == FIL_NULL); + ut_ad(page_is_leaf(page)); + ut_ad(page_get_page_no(page) == index->page); cursor->old_stored = BTR_PCUR_OLD_STORED; @@ -313,13 +315,20 @@ btr_pcur_restore_position_func( /* Save the old search mode of the cursor */ old_mode = cursor->search_mode; - if (UNIV_LIKELY(cursor->rel_pos == BTR_PCUR_ON)) { + switch (cursor->rel_pos) { + case BTR_PCUR_ON: mode = PAGE_CUR_LE; - } else if (cursor->rel_pos == BTR_PCUR_AFTER) { + break; + case BTR_PCUR_AFTER: mode = PAGE_CUR_G; - } else { - ut_ad(cursor->rel_pos == BTR_PCUR_BEFORE); + break; + case BTR_PCUR_BEFORE: mode = PAGE_CUR_L; + break; +#ifdef UNIV_DEBUG + default: + ut_error; +#endif /* UNIV_DEBUG */ } btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode, @@ -328,25 +337,44 @@ btr_pcur_restore_position_func( /* Restore the old search mode */ cursor->search_mode = old_mode; - if (cursor->rel_pos == BTR_PCUR_ON - && btr_pcur_is_on_user_rec(cursor) - && 0 == cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor), - rec_get_offsets( - btr_pcur_get_rec(cursor), index, - NULL, ULINT_UNDEFINED, &heap))) { + if (btr_pcur_is_on_user_rec(cursor)) { + switch (cursor->rel_pos) { + case BTR_PCUR_ON: + if (!cmp_dtuple_rec( + tuple, btr_pcur_get_rec(cursor), + rec_get_offsets(btr_pcur_get_rec(cursor), + index, NULL, + ULINT_UNDEFINED, &heap))) { - /* We have to store the NEW value for the modify clock, since - the cursor can now be on a different page! But we can retain - the value of old_rec */ + /* We have to store the NEW value for + the modify clock, since the cursor can + now be on a different page! But we can + retain the value of old_rec */ - cursor->block_when_stored = btr_pcur_get_block(cursor); - cursor->modify_clock = buf_block_get_modify_clock( - cursor->block_when_stored); - cursor->old_stored = BTR_PCUR_OLD_STORED; + cursor->block_when_stored = + btr_pcur_get_block(cursor); + cursor->modify_clock = + buf_block_get_modify_clock( + cursor->block_when_stored); + cursor->old_stored = BTR_PCUR_OLD_STORED; - mem_heap_free(heap); + mem_heap_free(heap); - return(TRUE); + return(TRUE); + } + + break; + case BTR_PCUR_BEFORE: + page_cur_move_to_next(btr_pcur_get_page_cur(cursor)); + break; + case BTR_PCUR_AFTER: + page_cur_move_to_prev(btr_pcur_get_page_cur(cursor)); + break; +#ifdef UNIV_DEBUG + default: + ut_error; +#endif /* UNIV_DEBUG */ + } } mem_heap_free(heap);