From 6a396fd35f1be1a54ae96ca27744ecee1ca4831f Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Tue, 8 Feb 2011 11:21:14 +0100 Subject: [PATCH 01/64] Bug #58455 Starting mysqld with defaults file without extension cause segmentation fault Bug occurs because fn_expand calls fn_format with NULL as ext. This is a backport of the patch from 5.6. Patch solve this problem by using an empty string as extension, and adding assertions to fn_format that correct arguments are passed. It also add a test tests several variations of using non-existing defaults files. --- mysql-test/r/mysqld--defaults-file.result | 12 ++++++ mysql-test/t/mysqld--defaults-file.test | 47 +++++++++++++++++++++++ mysys/default.c | 2 +- mysys/mf_format.c | 2 + 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/mysqld--defaults-file.result create mode 100644 mysql-test/t/mysqld--defaults-file.test diff --git a/mysql-test/r/mysqld--defaults-file.result b/mysql-test/r/mysqld--defaults-file.result new file mode 100644 index 00000000000..5fd5ca8d95a --- /dev/null +++ b/mysql-test/r/mysqld--defaults-file.result @@ -0,0 +1,12 @@ +Could not open required defaults file: /path/with/no/extension +Fatal error in defaults handling. Program aborted +Could not open required defaults file: /path/with.ext +Fatal error in defaults handling. Program aborted +Could not open required defaults file: MYSQL_TEST_DIR/relative/path/with.ext +Fatal error in defaults handling. Program aborted +Could not open required defaults file: MYSQL_TEST_DIR/relative/path/without/extension +Fatal error in defaults handling. Program aborted +Could not open required defaults file: MYSQL_TEST_DIR/with.ext +Fatal error in defaults handling. Program aborted +Could not open required defaults file: MYSQL_TEST_DIR/no_extension +Fatal error in defaults handling. Program aborted diff --git a/mysql-test/t/mysqld--defaults-file.test b/mysql-test/t/mysqld--defaults-file.test new file mode 100644 index 00000000000..065436c38aa --- /dev/null +++ b/mysql-test/t/mysqld--defaults-file.test @@ -0,0 +1,47 @@ +# BUG#58455 +# Starting mysqld with defaults file without extension cause +# segmentation fault + +source include/not_embedded.inc; +source include/not_windows.inc; + +# We need to use a plain "mysqld" without any other options to trigger +# the bug. In particular, it seems that passing --bootstrap does not +# trigger the bug. To do that, we extract the "command name" from the +# MYSQLD_BOOTSTRAP_CMD variable and store that in a file, which we +# then load into the test case. + +perl; + my ($mysqld)= split " ", $ENV{MYSQLD_BOOTSTRAP_CMD}; + open(FILE, ">", "$ENV{MYSQL_TMP_DIR}/mysqld.inc") or die; + print FILE "let \$MYSQLD= $mysqld;\n"; + close FILE; +EOF + +source $MYSQL_TMP_DIR/mysqld.inc; + +# All these tests refer to configuration files that do not exist + +--error 1 +exec $MYSQLD --defaults-file=/path/with/no/extension --print-defaults 2>&1; + +--error 1 +exec $MYSQLD --defaults-file=/path/with.ext --print-defaults 2>&1; + +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--error 1 +exec $MYSQLD --defaults-file=relative/path/with.ext --print-defaults 2>&1; + +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--error 1 +exec $MYSQLD --defaults-file=relative/path/without/extension --print-defaults 2>&1; + +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--error 1 +exec $MYSQLD --defaults-file=with.ext --print-defaults 2>&1; + +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--error 1 +exec $MYSQLD --defaults-file=no_extension --print-defaults 2>&1; + +remove_file $MYSQL_TMP_DIR/mysqld.inc; diff --git a/mysys/default.c b/mysys/default.c index 432c3086254..9a4b990f003 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -179,7 +179,7 @@ fn_expand(const char *filename, char *result_buf) if (my_getwd(dir, sizeof(dir), MYF(0))) DBUG_RETURN(3); DBUG_PRINT("debug", ("dir: %s", dir)); - if (fn_format(result_buf, filename, dir, NULL, flags) == NULL) + if (fn_format(result_buf, filename, dir, "", flags) == NULL) DBUG_RETURN(2); DBUG_PRINT("return", ("result: %s", result_buf)); DBUG_RETURN(0); diff --git a/mysys/mf_format.c b/mysys/mf_format.c index 6afa2938fa3..74f114a966f 100644 --- a/mysys/mf_format.c +++ b/mysys/mf_format.c @@ -31,6 +31,8 @@ char * fn_format(char * to, const char *name, const char *dir, reg1 size_t length; size_t dev_length; DBUG_ENTER("fn_format"); + DBUG_ASSERT(name != NULL); + DBUG_ASSERT(extension != NULL); DBUG_PRINT("enter",("name: %s dir: %s extension: %s flag: %d", name,dir,extension,flag)); From b8d8d914fee8b992a7ecbd4d92e47803546efd53 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 9 Feb 2011 16:25:34 +0100 Subject: [PATCH 02/64] Silly mistake in 59148, forgot .exe on Windows --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index a1e02a56164..5e61b4f1867 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2022,7 +2022,7 @@ sub read_plugin_defs($) # Need to check if we will be running mysqld-debug if ($opt_debug_server) { - $running_debug= 1 if find_mysqld($basedir) =~ /-debug$/; + $running_debug= 1 if find_mysqld($basedir) =~ /mysqld-debug/; } while () { From e6ff7b63f9a2d4bff26dbfcbfb85c6aaa13a32a7 Mon Sep 17 00:00:00 2001 From: Jonathan Perkin Date: Wed, 9 Feb 2011 19:41:12 +0000 Subject: [PATCH 03/64] Combine test-bt and test-bt-debug into a single default.release collection, utilising the new --debug-server option for the debug run to ensure the correct binaries are used. Remove test-bt-*fast, they are unused. --- mysql-test/collections/default.release | 11 +++++++++++ mysql-test/collections/test-bt | 10 ---------- mysql-test/collections/test-bt-debug | 1 - mysql-test/collections/test-bt-debug-fast | 0 mysql-test/collections/test-bt-fast | 2 -- 5 files changed, 11 insertions(+), 13 deletions(-) create mode 100644 mysql-test/collections/default.release delete mode 100644 mysql-test/collections/test-bt delete mode 100644 mysql-test/collections/test-bt-debug delete mode 100644 mysql-test/collections/test-bt-debug-fast delete mode 100644 mysql-test/collections/test-bt-fast diff --git a/mysql-test/collections/default.release b/mysql-test/collections/default.release new file mode 100644 index 00000000000..108e1032ca2 --- /dev/null +++ b/mysql-test/collections/default.release @@ -0,0 +1,11 @@ +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=debug --vardir=var-debug --skip-ndbcluster --skip-rpl --report-features --debug-server +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=normal --vardir=var-normal --skip-ndbcluster --report-features +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=ps --vardir=var-ps --skip-ndbcluster --ps-protocol +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=funcs1+ps --vardir=var-funcs_1_ps --suite=funcs_1 --ps-protocol +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=funcs2 --vardir=var-funcs2 --suite=funcs_2 +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=partitions --vardir=var-parts --suite=parts +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=stress --vardir=var-stress --suite=stress +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=jp --vardir=var-jp --suite=jp +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=embedded --vardir=var-embedded --embedded-server --skip-rpl --skip-ndbcluster +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=nist --vardir=var-nist --suite=nist +perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=nist+ps --vardir=var-nist_ps --suite=nist --ps-protocol diff --git a/mysql-test/collections/test-bt b/mysql-test/collections/test-bt deleted file mode 100644 index 58f0c2c3cde..00000000000 --- a/mysql-test/collections/test-bt +++ /dev/null @@ -1,10 +0,0 @@ -perl mysql-test-run.pl --force --timer --parallel=auto --comment=normal --skip-ndbcluster --report-features --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=ps --skip-ndbcluster --ps-protocol --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=funcs1+ps --suite=funcs_1 --ps-protocol --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=funcs2 --suite=funcs_2 --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=partitions --suite=parts --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=stress --suite=stress --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=jp --suite=jp --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=embedded --embedded-server --skip-rpl --skip-ndbcluster --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=nist --suite=nist --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=nist+ps --suite=nist --ps-protocol --experimental=collections/default.experimental diff --git a/mysql-test/collections/test-bt-debug b/mysql-test/collections/test-bt-debug deleted file mode 100644 index 5fc1e15e0d2..00000000000 --- a/mysql-test/collections/test-bt-debug +++ /dev/null @@ -1 +0,0 @@ -perl mysql-test-run.pl --force --timer --parallel=auto --comment=debug --skip-ndbcluster --skip-rpl --report-features --experimental=collections/default.experimental diff --git a/mysql-test/collections/test-bt-debug-fast b/mysql-test/collections/test-bt-debug-fast deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/mysql-test/collections/test-bt-fast b/mysql-test/collections/test-bt-fast deleted file mode 100644 index 7978c8082f0..00000000000 --- a/mysql-test/collections/test-bt-fast +++ /dev/null @@ -1,2 +0,0 @@ -perl mysql-test-run.pl --force --timer --parallel=auto --comment=ps --skip-ndbcluster --ps-protocol --report-features --experimental=collections/default.experimental -perl mysql-test-run.pl --force --timer --parallel=auto --comment=stress --suite=stress --experimental=collections/default.experimental From cb6b340b0f51c48e0d7f294f85933d540387bb2b Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 10 Feb 2011 11:18:08 +0300 Subject: [PATCH 04/64] Bug#31384 DATE_ADD() and DATE_SUB() return binary data Problem: DATE_ADD() is a hybrid function and can return DATE, DATETIME or VARCHAR data type depending on arguments. In case of VARCHAR data type, DATE_ADD() reported "binary" character set, which was wrong. Fix: make DATE_ADD() return @character_set_connection in VARCHAR context. @ mysql-test/include/ctype_numconv.inc Adding tests @ mysql-test/r/ctype_binary.result Adding tests @ mysql-test/r/ctype_cp1251.result Adding tests @ mysql-test/r/ctype_latin1.result Adding tests @ mysql-test/r/ctype_ucs.result Adding tests @ mysql-test/r/ctype_utf8.result Adding tests @ sql/item_strfunc.cc - Moving code from Item_str_ascii_func::val_str() to Item_str_func::val_str_from_val_str_ascii(), as this code needs to be shared by Item_date_add_interval. - Adding str2 parameter to be used as a buffer, instead of using private ascii_buf member. @ sql/item_strfunc.h - Moving code from Item_str_ascii_func::val_str() to Item_str_func::val_str_from_val_str_ascii() - Removing "String *val_str_convert_from_ascii(String *str, String *ascii_buf)" prototype as it was neither used nor declared. @ sql/item_timefunc.h - Overwriting parent's charset_for_protocol() method, becase we need to behave differenlty in VARCHAR and DATE/DATETYPE context. - Adding ascii_buf for conversion. - Adding val_str_ascii() prototype. - Adding val_str() which uses newly added Item_str_func::val_str_from_val_str_ascii(), passing ascii_buf as a conversion buffer. --- mysql-test/include/ctype_numconv.inc | 26 ++++++++++++++++++ mysql-test/r/ctype_binary.result | 40 ++++++++++++++++++++++++++++ mysql-test/r/ctype_cp1251.result | 40 ++++++++++++++++++++++++++++ mysql-test/r/ctype_latin1.result | 40 ++++++++++++++++++++++++++++ mysql-test/r/ctype_ucs.result | 40 ++++++++++++++++++++++++++++ mysql-test/r/ctype_utf8.result | 40 ++++++++++++++++++++++++++++ sql/item_strfunc.cc | 14 +++++----- sql/item_strfunc.h | 7 +++-- sql/item_timefunc.cc | 19 ++++++++++--- sql/item_timefunc.h | 20 ++++++++++++-- 10 files changed, 272 insertions(+), 14 deletions(-) diff --git a/mysql-test/include/ctype_numconv.inc b/mysql-test/include/ctype_numconv.inc index 83d69cfa40d..974951b6b6f 100644 --- a/mysql-test/include/ctype_numconv.inc +++ b/mysql-test/include/ctype_numconv.inc @@ -1737,6 +1737,32 @@ EXPLAIN SELECT * FROM t1 WHERE date_column BETWEEN '2010-09-01' AND '2010-10-01' DROP TABLE t1; +--echo # +--echo # Bug #31384 DATE_ADD() and DATE_SUB() return binary data +--echo # +SELECT @@collation_connection, @@character_set_results; +CREATE TABLE t1 AS +SELECT + DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, + DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, + DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, + DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +SHOW CREATE TABLE t1; +DROP TABLE t1; +--enable_metadata +SELECT + DATE_SUB('2007-08-03', INTERVAL 1 DAY) AS field_str1, + DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, + DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, + DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +--disable_metadata +SELECT + HEX(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, + HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, + HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, + HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; + + --echo # --echo # Bug#52159 returning time type from function and empty left join causes debug assertion --echo # diff --git a/mysql-test/r/ctype_binary.result b/mysql-test/r/ctype_binary.result index 4d526a86ade..58cd4b3524b 100644 --- a/mysql-test/r/ctype_binary.result +++ b/mysql-test/r/ctype_binary.result @@ -2767,6 +2767,46 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range date_column date_column 9 NULL 1 Using where DROP TABLE t1; # +# Bug #31384 DATE_ADD() and DATE_SUB() return binary data +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +binary binary +CREATE TABLE t1 AS +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `field_str1` varbinary(29) DEFAULT NULL, + `field1_str2` varbinary(29) DEFAULT NULL, + `field_date` date DEFAULT NULL, + `field_datetime` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 DAY) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def field_str1 254 29 10 Y 128 31 63 +def field1_str2 254 29 19 Y 128 31 63 +def field_date 10 29 10 Y 128 31 63 +def field_datetime 12 29 19 Y 128 31 63 +field_str1 field1_str2 field_date field_datetime +2007-08-02 2007-08-03 17:32:00 2007-08-02 2007-08-03 17:32:00 +SELECT +HEX(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, +HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field1_str2 field_date field_datetime +323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 +# # Bug#52159 returning time type from function and empty left join causes debug assertion # CREATE FUNCTION f1() RETURNS TIME RETURN 1; diff --git a/mysql-test/r/ctype_cp1251.result b/mysql-test/r/ctype_cp1251.result index b65230e94b4..9a781c61e91 100644 --- a/mysql-test/r/ctype_cp1251.result +++ b/mysql-test/r/ctype_cp1251.result @@ -3157,6 +3157,46 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range date_column date_column 9 NULL 1 Using where DROP TABLE t1; # +# Bug #31384 DATE_ADD() and DATE_SUB() return binary data +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +cp1251_general_ci cp1251 +CREATE TABLE t1 AS +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `field_str1` varchar(29) CHARACTER SET cp1251 DEFAULT NULL, + `field1_str2` varchar(29) CHARACTER SET cp1251 DEFAULT NULL, + `field_date` date DEFAULT NULL, + `field_datetime` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 DAY) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def field_str1 254 29 10 Y 0 31 51 +def field1_str2 254 29 19 Y 0 31 51 +def field_date 10 29 10 Y 128 31 63 +def field_datetime 12 29 19 Y 128 31 63 +field_str1 field1_str2 field_date field_datetime +2007-08-02 2007-08-03 17:32:00 2007-08-02 2007-08-03 17:32:00 +SELECT +HEX(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, +HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field1_str2 field_date field_datetime +323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 +# # Bug#52159 returning time type from function and empty left join causes debug assertion # CREATE FUNCTION f1() RETURNS TIME RETURN 1; diff --git a/mysql-test/r/ctype_latin1.result b/mysql-test/r/ctype_latin1.result index ac84b169ec3..6f773270dff 100644 --- a/mysql-test/r/ctype_latin1.result +++ b/mysql-test/r/ctype_latin1.result @@ -3186,6 +3186,46 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range date_column date_column 9 NULL 1 Using where DROP TABLE t1; # +# Bug #31384 DATE_ADD() and DATE_SUB() return binary data +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +latin1_swedish_ci latin1 +CREATE TABLE t1 AS +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `field_str1` varchar(29) DEFAULT NULL, + `field1_str2` varchar(29) DEFAULT NULL, + `field_date` date DEFAULT NULL, + `field_datetime` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 DAY) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def field_str1 254 29 10 Y 0 31 8 +def field1_str2 254 29 19 Y 0 31 8 +def field_date 10 29 10 Y 128 31 63 +def field_datetime 12 29 19 Y 128 31 63 +field_str1 field1_str2 field_date field_datetime +2007-08-02 2007-08-03 17:32:00 2007-08-02 2007-08-03 17:32:00 +SELECT +HEX(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, +HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field1_str2 field_date field_datetime +323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 +# # Bug#52159 returning time type from function and empty left join causes debug assertion # CREATE FUNCTION f1() RETURNS TIME RETURN 1; diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 11d5117bbe1..0ee80b921a1 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -4009,6 +4009,46 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where DROP TABLE t1; # +# Bug #31384 DATE_ADD() and DATE_SUB() return binary data +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +ucs2_general_ci latin1 +CREATE TABLE t1 AS +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `field_str1` varchar(29) CHARACTER SET ucs2 DEFAULT NULL, + `field1_str2` varchar(29) CHARACTER SET ucs2 DEFAULT NULL, + `field_date` date DEFAULT NULL, + `field_datetime` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 DAY) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def field_str1 254 29 10 Y 0 31 8 +def field1_str2 254 29 19 Y 0 31 8 +def field_date 10 29 10 Y 128 31 63 +def field_datetime 12 29 19 Y 128 31 63 +field_str1 field1_str2 field_date field_datetime +2007-08-02 2007-08-03 17:32:00 2007-08-02 2007-08-03 17:32:00 +SELECT +HEX(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, +HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field1_str2 field_date field_datetime +0032003000300037002D00300038002D00300032002000320033003A00350039003A00300030 0032003000300037002D00300038002D00300033002000310037003A00330032003A00300030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 +# # Bug#52159 returning time type from function and empty left join causes debug assertion # CREATE FUNCTION f1() RETURNS TIME RETURN 1; diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 5763885d5f3..ababfe51621 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -4898,6 +4898,46 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range date_column date_column 9 NULL 1 Using where DROP TABLE t1; # +# Bug #31384 DATE_ADD() and DATE_SUB() return binary data +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +utf8_general_ci utf8 +CREATE TABLE t1 AS +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `field_str1` varchar(29) CHARACTER SET utf8 DEFAULT NULL, + `field1_str2` varchar(29) CHARACTER SET utf8 DEFAULT NULL, + `field_date` date DEFAULT NULL, + `field_datetime` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SELECT +DATE_SUB('2007-08-03', INTERVAL 1 DAY) AS field_str1, +DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, +DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, +DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def field_str1 254 87 10 Y 0 31 33 +def field1_str2 254 87 19 Y 0 31 33 +def field_date 10 29 10 Y 128 31 63 +def field_datetime 12 29 19 Y 128 31 63 +field_str1 field1_str2 field_date field_datetime +2007-08-02 2007-08-03 17:32:00 2007-08-02 2007-08-03 17:32:00 +SELECT +HEX(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, +HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field1_str2 field_date field_datetime +323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 +# # Bug#52159 returning time type from function and empty left join causes debug assertion # CREATE FUNCTION f1() RETURNS TIME RETURN 1; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 25eb08ffa75..0248c133f60 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -70,7 +70,7 @@ String my_empty_string("",default_charset_info); Normally conversion does not happen, and val_str_ascii() is immediately returned instead. */ -String *Item_str_ascii_func::val_str(String *str) +String *Item_str_func::val_str_from_val_str_ascii(String *str, String *str2) { DBUG_ASSERT(fixed == 1); @@ -82,19 +82,19 @@ String *Item_str_ascii_func::val_str(String *str) return res; } - DBUG_ASSERT(str != &ascii_buf); + DBUG_ASSERT(str != str2); uint errors; - String *res= val_str_ascii(&ascii_buf); + String *res= val_str_ascii(str); if (!res) return 0; - if ((null_value= str->copy(res->ptr(), res->length(), - &my_charset_latin1, collation.collation, - &errors))) + if ((null_value= str2->copy(res->ptr(), res->length(), + &my_charset_latin1, collation.collation, + &errors))) return 0; - return str; + return str2; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index e26f9b4467a..fef4f9f975d 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -51,6 +51,7 @@ public: enum Item_result result_type () const { return STRING_RESULT; } void left_right_max_length(); bool fix_fields(THD *thd, Item **ref); + String *val_str_from_val_str_ascii(String *str, String *str2); }; @@ -66,8 +67,10 @@ public: Item_str_ascii_func(Item *a) :Item_str_func(a) {} Item_str_ascii_func(Item *a,Item *b) :Item_str_func(a,b) {} Item_str_ascii_func(Item *a,Item *b,Item *c) :Item_str_func(a,b,c) {} - String *val_str_convert_from_ascii(String *str, String *ascii_buf); - String *val_str(String *str); + String *val_str(String *str) + { + return val_str_from_val_str_ascii(str, &ascii_buf); + } virtual String *val_str_ascii(String *)= 0; }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 73b8b6047e0..3d587ace335 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2205,8 +2205,6 @@ void Item_date_add_interval::fix_length_and_dec() enum_field_types arg0_field_type; maybe_null=1; - fix_length_and_charset_datetime(MAX_DATETIME_FULL_WIDTH); - value.alloc(max_length); /* The field type for the result of an Item_date function is defined as @@ -2231,6 +2229,21 @@ void Item_date_add_interval::fix_length_and_dec() else cached_field_type= MYSQL_TYPE_DATETIME; } + + if (cached_field_type == MYSQL_TYPE_STRING) + { + /* Behave as a usual string function when return type is VARCHAR. */ + fix_length_and_charset(MAX_DATETIME_FULL_WIDTH, default_charset()); + } + else + { + /* + Follow the "Number-to-string conversion" rules as in WorkLog 2649 + when return type is DATE or DATETIME. + */ + fix_length_and_charset_datetime(MAX_DATETIME_FULL_WIDTH); + } + value.alloc(max_length); } @@ -2253,7 +2266,7 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date) } -String *Item_date_add_interval::val_str(String *str) +String *Item_date_add_interval::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index b8c6b1d38e4..9732e8dc360 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -773,16 +773,32 @@ class Item_date_add_interval :public Item_date_func { String value; enum_field_types cached_field_type; - + String ascii_buf; public: const interval_type int_type; // keep it public const bool date_sub_interval; // keep it public Item_date_add_interval(Item *a,Item *b,interval_type type_arg,bool neg_arg) :Item_date_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} - String *val_str(String *); + String *val_str_ascii(String *str); + String *val_str(String *str) + { + return val_str_from_val_str_ascii(str, &ascii_buf); + } const char *func_name() const { return "date_add_interval"; } void fix_length_and_dec(); enum_field_types field_type() const { return cached_field_type; } + CHARSET_INFO *charset_for_protocol(void) const + { + /* + DATE_ADD() can return DATE, DATETIME or VARCHAR depending on arguments. + Send using "binary" when DATE or DATETIME, + or using collation.collation when VARCHAR + (which was fixed from @collation_connection in fix_length_and_dec). + */ + DBUG_ASSERT(fixed == 1); + return cached_field_type == MYSQL_TYPE_STRING ? + collation.collation : &my_charset_bin; + } longlong val_int(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); bool eq(const Item *item, bool binary_cmp) const; From 64c82a68ccb2a2d300820b7f3bde9317f41d9902 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 10 Feb 2011 09:34:22 +0200 Subject: [PATCH 05/64] Bug #59884: setting charset to 2048 crashes The retrieval of a charset by number was not doing bounds checking before accessing the internal character sets array. Added checks for valid charset number. Added asserts for valid charset number to some of the internal functions. Removed one superfluous check for charset_number 0 (since the all_charsets_array[0] is set to 0 anyway) for uniformity. Test suite added. --- mysql-test/r/variables.result | 22 ++++++++++++++++++++++ mysql-test/t/variables.test | 26 ++++++++++++++++++++++++++ mysys/charset.c | 20 +++++++++++++------- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index ee180169916..5d6caf21182 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -1732,3 +1732,25 @@ set @@session.autocommit=t1_min(), @@session.autocommit=t1_max(), drop table t1; drop function t1_min; drop function t1_max; +# +# Bug #59884: setting charset to 2048 crashes +# +set session character_set_results = 2048; +ERROR 42000: Unknown character set: '2048' +set session character_set_client=2048; +ERROR 42000: Unknown character set: '2048' +set session character_set_connection=2048; +ERROR 42000: Unknown character set: '2048' +set session character_set_server=2048; +ERROR 42000: Unknown character set: '2048' +set session collation_server=2048; +ERROR HY000: Unknown collation: '2048' +set session character_set_filesystem=2048; +ERROR 42000: Unknown character set: '2048' +set session character_set_database=2048; +ERROR 42000: Unknown character set: '2048' +set session collation_connection=2048; +ERROR HY000: Unknown collation: '2048' +set session collation_database=2048; +ERROR HY000: Unknown collation: '2048' +End of 5.5 tests diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index b766c1c7dc6..2b20bbdc13b 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -1476,3 +1476,29 @@ drop function t1_max; ########################################################################### + + +--echo # +--echo # Bug #59884: setting charset to 2048 crashes +--echo # + +--error ER_UNKNOWN_CHARACTER_SET +set session character_set_results = 2048; +--error ER_UNKNOWN_CHARACTER_SET +set session character_set_client=2048; +--error ER_UNKNOWN_CHARACTER_SET +set session character_set_connection=2048; +--error ER_UNKNOWN_CHARACTER_SET +set session character_set_server=2048; +--error ER_UNKNOWN_COLLATION +set session collation_server=2048; +--error ER_UNKNOWN_CHARACTER_SET +set session character_set_filesystem=2048; +--error ER_UNKNOWN_CHARACTER_SET +set session character_set_database=2048; +--error ER_UNKNOWN_COLLATION +set session collation_connection=2048; +--error ER_UNKNOWN_COLLATION +set session collation_database=2048; + +--echo End of 5.5 tests diff --git a/mysys/charset.c b/mysys/charset.c index 167d6b8ff6e..c2c46ba3fb4 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -419,6 +419,7 @@ CHARSET_INFO *default_charset_info = &my_charset_latin1; void add_compiled_collation(CHARSET_INFO *cs) { + DBUG_ASSERT(cs->number < array_elements(all_charsets)); all_charsets[cs->number]= cs; cs->state|= MY_CS_AVAILABLE; } @@ -529,14 +530,17 @@ uint get_charset_number(const char *charset_name, uint cs_flags) const char *get_charset_name(uint charset_number) { - CHARSET_INFO *cs; my_pthread_once(&charsets_initialized, init_available_charsets); - cs=all_charsets[charset_number]; - if (cs && (cs->number == charset_number) && cs->name ) - return (char*) cs->name; + if (charset_number < array_elements(all_charsets)) + { + CHARSET_INFO *cs= all_charsets[charset_number]; + + if (cs && (cs->number == charset_number) && cs->name) + return (char*) cs->name; + } - return (char*) "?"; /* this mimics find_type() */ + return "?"; /* this mimics find_type() */ } @@ -545,6 +549,8 @@ static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags) char buf[FN_REFLEN]; CHARSET_INFO *cs; + DBUG_ASSERT(cs_number < array_elements(all_charsets)); + if ((cs= all_charsets[cs_number])) { if (cs->state & MY_CS_READY) /* if CS is already initialized */ @@ -589,8 +595,8 @@ CHARSET_INFO *get_charset(uint cs_number, myf flags) return default_charset_info; my_pthread_once(&charsets_initialized, init_available_charsets); - - if (!cs_number || cs_number > array_elements(all_charsets)) + + if (cs_number >= array_elements(all_charsets)) return NULL; cs=get_internal_charset(cs_number, flags); From 91a2afcbcba8af28d2f10f80830b543654151556 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Tue, 11 Jan 2011 16:53:14 +0200 Subject: [PATCH 06/64] Bug #59017: Auth plugin socket_peercred fails to load in 5.5.8 Due to how the server checks for option names the name of the plugin "socket_peercred" collides with the existing server side option "socket". Renamed the plugin from "socket_peercred" to "auth_socket" to avoid the name clash. --- plugin/auth/auth_socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/auth/auth_socket.c b/plugin/auth/auth_socket.c index dbd440025d7..b8ae00b6949 100644 --- a/plugin/auth/auth_socket.c +++ b/plugin/auth/auth_socket.c @@ -79,7 +79,7 @@ mysql_declare_plugin(socket_auth) { MYSQL_AUTHENTICATION_PLUGIN, &socket_auth_handler, - "socket_peercred", + "auth_socket", "Sergei Golubchik", "Unix Socket based authentication", PLUGIN_LICENSE_GPL, From 796672c44b350a856621193a5b67e5e91aef38e3 Mon Sep 17 00:00:00 2001 From: Joerg Bruehe Date: Thu, 3 Feb 2011 18:16:22 +0100 Subject: [PATCH 07/64] Fix Bug #56581 RPM scripts use hardcoded datadir location When fixing the 27072 bug, the shell snippets running before/after a RPM upgrade got expanded to look at files in the data directory and at the PID file. In this expansion, the standard locations were used. There are users who configure their installations to use non-standard locations for the data directory, the PID file, and other objects. For these users, the fix of 27072 did not work. As a result, the fact that a server was running at upgrade start was not noticed, and the new server was not started after the upgrade. With this patch, the shell snippets now try to get these locations from "my_print_defaults" before falling back to the defaults. Now, the fact that the old server is running is again noticed (even with non-standard locations), and the new server is started. Also, the upgrade log is written to the correct data directory. --- support-files/mysql.spec.sh | 58 ++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index c69ae6afce0..33c1254bc15 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2011, 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 @@ -610,7 +610,25 @@ touch $RBR%{_sysconfdir}/mysqlmanager.passwd ############################################################################## %pre server -mysql_datadir=%{mysqldatadir} +# This is the code running at the beginning of a RPM upgrade action, +# before replacing the old files with the new ones. + +# There are users who deviate from the default file system layout. +# Check local settings to support them. +if [ -x %{_bindir}/my_print_defaults ] +then + mysql_datadir=`%{_bindir}/my_print_defaults server mysqld | grep '^--datadir=' | sed -n 's/--datadir=//p'` + PID_FILE_PATT=`%{_bindir}/my_print_defaults server mysqld | grep '^--pid-file=' | sed -n 's/--pid-file=//p'` +fi +if [ -z "$mysql_datadir" ] +then + mysql_datadir=%{mysqldatadir} +fi +if [ -z "$PID_FILE_PATT" ] +then + PID_FILE_PATT="$mysql_datadir/*.pid" +fi + # Check if we can safely upgrade. An upgrade is only safe if it's from one # of our RPMs in the same version family. @@ -681,7 +699,7 @@ fi # We assume that if there is exactly one ".pid" file, # it contains the valid PID of a running MySQL server. -NR_PID_FILES=`ls $mysql_datadir/*.pid 2>/dev/null | wc -l` +NR_PID_FILES=`ls $PID_FILE_PATT 2>/dev/null | wc -l` case $NR_PID_FILES in 0 ) SERVER_TO_START='' ;; # No "*.pid" file == no running server 1 ) SERVER_TO_START='true' ;; @@ -703,8 +721,8 @@ if [ -f $STATUS_FILE ]; then echo "before repeating the MySQL upgrade." exit 1 elif [ -n "$SEVERAL_PID_FILES" ] ; then - echo "Your MySQL directory '$mysql_datadir' has more than one PID file:" - ls -ld $mysql_datadir/*.pid + echo "You have more than one PID file:" + ls -ld $PID_FILE_PATT echo "Please check which one (if any) corresponds to a running server" echo "and delete all others before repeating the MySQL upgrade." exit 1 @@ -729,17 +747,17 @@ if [ -d $mysql_datadir ] ; then if [ -n "$SERVER_TO_START" ] ; then # There is only one PID file, race possibility ignored echo "PID file:" >> $STATUS_FILE - ls -l $mysql_datadir/*.pid >> $STATUS_FILE - cat $mysql_datadir/*.pid >> $STATUS_FILE + ls -l $PID_FILE_PATT >> $STATUS_FILE + cat $PID_FILE_PATT >> $STATUS_FILE echo >> $STATUS_FILE echo "Server process:" >> $STATUS_FILE - ps -fp `cat $mysql_datadir/*.pid` >> $STATUS_FILE + ps -fp `cat $PID_FILE_PATT` >> $STATUS_FILE echo >> $STATUS_FILE echo "SERVER_TO_START=$SERVER_TO_START" >> $STATUS_FILE else # Take a note we checked it ... echo "PID file:" >> $STATUS_FILE - ls -l $mysql_datadir/*.pid >> $STATUS_FILE 2>&1 + ls -l $PID_FILE_PATT >> $STATUS_FILE 2>&1 fi fi @@ -754,7 +772,20 @@ if [ -x %{_sysconfdir}/init.d/mysql ] ; then fi %post server -mysql_datadir=%{mysqldatadir} +# This is the code running at the end of a RPM install or upgrade action, +# after the (new) files have been written. + +# There are users who deviate from the default file system layout. +# Check local settings to support them. +if [ -x %{_bindir}/my_print_defaults ] +then + mysql_datadir=`%{_bindir}/my_print_defaults server mysqld | grep '^--datadir=' | sed -n 's/--datadir=//p'` +fi +if [ -z "$mysql_datadir" ] +then + mysql_datadir=%{mysqldatadir} +fi + NEW_VERSION=%{mysql_version}-%{release} STATUS_FILE=$mysql_datadir/RPM_UPGRADE_MARKER @@ -1133,6 +1164,13 @@ fi ############################################################################## %changelog +* Thu Feb 03 2011 Joerg Bruehe + +- Fix bug#56581: If an installation deviates from the default file locations + ("datadir" and "pid-file"), the mechanism to detect a running server (on upgrade) + should still work, and use these locations. + The problem was that the fix for bug#27072 did not check for local settings. + * Wed Nov 24 2010 Alexander Nozdrin - EXCEPTIONS-CLIENT has been deleted, remove it from here too. From 154fc1e364e7c139803741784a48f36dbeb55859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 8 Feb 2011 12:56:23 +0200 Subject: [PATCH 08/64] Implement UNIV_BLOB_DEBUG. An early version of this caught Bug #55284. This option is known to be broken when tablespaces contain off-page columns after crash recovery. It has only been tested when creating the data files from the scratch. btr_blob_dbg_t: A map from page_no:heap_no:field_no to first_blob_page_no. This map is instantiated for every clustered index in index->blobs. It is protected by index->blobs_mutex. btr_blob_dbg_msg_issue(): Issue a diagnostic message. Invoked when btr_blob_dbg_msg is set. btr_blob_dbg_rbt_insert(): Insert a btr_blob_dbg_t into index->blobs. btr_blob_dbg_rbt_delete(): Remove a btr_blob_dbg_t from index->blobs. btr_blob_dbg_cmp(): Comparator for btr_blob_dbg_t. btr_blob_dbg_add_blob(): Add a BLOB reference to the map. btr_blob_dbg_add_rec(): Add all BLOB references from a record to the map. btr_blob_dbg_print(): Display the map of BLOB references in an index. btr_blob_dbg_remove_rec(): Remove all BLOB references of a record from the map. btr_blob_dbg_is_empty(): Check that no BLOB references exist to or from a page. Disowned references from delete-marked records are tolerated. btr_blob_dbg_op(): Perform an operation on all BLOB references on a B-tree page. btr_blob_dbg_add(): Add all BLOB references from a B-tree page to the map. btr_blob_dbg_remove(): Remove all BLOB references from a B-tree page from the map. btr_blob_dbg_restore(): Restore the BLOB references after a failed page reorganize. btr_blob_dbg_set_deleted_flag(): Modify the 'deleted' flag in the BLOB references of a record. btr_blob_dbg_owner(): Own or disown a BLOB reference. btr_page_create(), btr_page_free_low(): Assert that no BLOB references exist. btr_create(): Create index->blobs for clustered indexes. btr_page_reorganize_low(): Invoke btr_blob_dbg_remove() before copying the records. Invoke btr_blob_dbg_restore() if the operation fails. btr_page_empty(), btr_lift_page_up(), btr_compress(), btr_discard_page(): Invoke btr_blob_dbg_remove(). btr_cur_del_mark_set_clust_rec(): Invoke btr_blob_dbg_set_deleted_flag(). Other cases of modifying the delete mark are either in the secondary index or during crash recovery, which we do not promise to support. btr_cur_set_ownership_of_extern_field(): Invoke btr_blob_dbg_owner(). btr_store_big_rec_extern_fields(): Invoke btr_blob_dbg_add_blob(). btr_free_externally_stored_field(): Invoke btr_blob_dbg_assert_empty() on the first BLOB page. page_cur_insert_rec_low(), page_cur_insert_rec_zip(), page_copy_rec_list_end_to_created_page(): Invoke btr_blob_dbg_add_rec(). page_cur_insert_rec_zip_reorg(), page_copy_rec_list_end(), page_copy_rec_list_start(): After failure, invoke btr_blob_dbg_remove() and btr_blob_dbg_add(). page_cur_delete_rec(): Invoke btr_blob_dbg_remove_rec(). page_delete_rec_list_end(): Invoke btr_blob_dbg_op(btr_blob_dbg_remove_rec). page_zip_reorganize(): Invoke btr_blob_dbg_remove() before copying the records. page_zip_copy_recs(): Invoke btr_blob_dbg_add(). row_upd_rec_in_place(): Invoke btr_blob_dbg_rbt_delete() and btr_blob_dbg_rbt_insert(). innobase_start_or_create_for_mysql(): Warn when UNIV_BLOB_DEBUG is enabled. rb://550 approved by Jimmy Yang --- storage/innodb_plugin/btr/btr0btr.c | 571 ++++++++++++++++++++++ storage/innodb_plugin/btr/btr0cur.c | 44 ++ storage/innodb_plugin/dict/dict0mem.c | 9 + storage/innodb_plugin/include/btr0btr.h | 85 ++++ storage/innodb_plugin/include/btr0types.h | 125 +++++ storage/innodb_plugin/include/dict0mem.h | 7 + storage/innodb_plugin/include/page0zip.h | 2 +- storage/innodb_plugin/include/univ.i | 2 + storage/innodb_plugin/page/page0cur.c | 10 + storage/innodb_plugin/page/page0page.c | 11 + storage/innodb_plugin/page/page0zip.c | 5 +- storage/innodb_plugin/row/row0upd.c | 35 ++ storage/innodb_plugin/srv/srv0start.c | 6 + 13 files changed, 910 insertions(+), 2 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index 3d8d6048603..46810c011c4 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -42,6 +42,560 @@ Created 6/2/1994 Heikki Tuuri #include "ibuf0ibuf.h" #include "trx0trx.h" +#ifdef UNIV_BLOB_DEBUG +# include "srv0srv.h" +# include "ut0rbt.h" + +/** TRUE when messages about index->blobs modification are enabled. */ +static ibool btr_blob_dbg_msg; + +/** Issue a message about an operation on index->blobs. +@param op operation +@param b the entry being subjected to the operation +@param ctx the context of the operation */ +#define btr_blob_dbg_msg_issue(op, b, ctx) \ + fprintf(stderr, op " %u:%u:%u->%u %s(%u,%u,%u)\n", \ + (b)->ref_page_no, (b)->ref_heap_no, \ + (b)->ref_field_no, (b)->blob_page_no, ctx, \ + (b)->owner, (b)->always_owner, (b)->del) + +/** Insert to index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_insert( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ +{ + if (btr_blob_dbg_msg) { + btr_blob_dbg_msg_issue("insert", b, ctx); + } + mutex_enter(&index->blobs_mutex); + rbt_insert(index->blobs, b, b); + mutex_exit(&index->blobs_mutex); +} + +/** Remove from index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_delete( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ +{ + if (btr_blob_dbg_msg) { + btr_blob_dbg_msg_issue("delete", b, ctx); + } + mutex_enter(&index->blobs_mutex); + ut_a(rbt_delete(index->blobs, b)); + mutex_exit(&index->blobs_mutex); +} + +/**************************************************************//** +Comparator for items (btr_blob_dbg_t) in index->blobs. +The key in index->blobs is (ref_page_no, ref_heap_no, ref_field_no). +@return negative, 0 or positive if *a<*b, *a=*b, *a>*b */ +static +int +btr_blob_dbg_cmp( +/*=============*/ + const void* a, /*!< in: first btr_blob_dbg_t to compare */ + const void* b) /*!< in: second btr_blob_dbg_t to compare */ +{ + const btr_blob_dbg_t* aa = a; + const btr_blob_dbg_t* bb = b; + + ut_ad(aa != NULL); + ut_ad(bb != NULL); + + if (aa->ref_page_no != bb->ref_page_no) { + return(aa->ref_page_no < bb->ref_page_no ? -1 : 1); + } + if (aa->ref_heap_no != bb->ref_heap_no) { + return(aa->ref_heap_no < bb->ref_heap_no ? -1 : 1); + } + if (aa->ref_field_no != bb->ref_field_no) { + return(aa->ref_field_no < bb->ref_field_no ? -1 : 1); + } + return(0); +} + +/**************************************************************//** +Add a reference to an off-page column to the index->blobs map. */ +UNIV_INTERN +void +btr_blob_dbg_add_blob( +/*==================*/ + const rec_t* rec, /*!< in: clustered index record */ + ulint field_no, /*!< in: off-page column number */ + ulint page_no, /*!< in: start page of the column */ + dict_index_t* index, /*!< in/out: index tree */ + const char* ctx) /*!< in: context (for logging) */ +{ + btr_blob_dbg_t b; + const page_t* page = page_align(rec); + + ut_a(index->blobs); + + b.blob_page_no = page_no; + b.ref_page_no = page_get_page_no(page); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = field_no; + ut_a(b.ref_field_no >= index->n_uniq); + b.always_owner = b.owner = TRUE; + b.del = FALSE; + ut_a(!rec_get_deleted_flag(rec, page_is_comp(page))); + btr_blob_dbg_rbt_insert(index, &b, ctx); +} + +/**************************************************************//** +Add to index->blobs any references to off-page columns from a record. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add_rec( +/*=================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint count = 0; + ulint i; + btr_blob_dbg_t b; + ibool del; + + ut_ad(rec_offs_validate(rec, index, offsets)); + + if (!rec_offs_any_extern(offsets)) { + return(0); + } + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + del = (rec_get_deleted_flag(rec, rec_offs_comp(offsets)) != 0); + + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i)) { + ulint len; + const byte* field_ref = rec_get_nth_field( + rec, offsets, i, &len); + + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + if (!memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)) { + /* the column has not been stored yet */ + continue; + } + + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + ut_a(b.ref_field_no >= index->n_uniq); + b.always_owner = b.owner + = !(field_ref[BTR_EXTERN_LEN] + & BTR_EXTERN_OWNER_FLAG); + b.del = del; + + btr_blob_dbg_rbt_insert(index, &b, ctx); + count++; + } + } + + return(count); +} + +/**************************************************************//** +Display the references to off-page columns. +This function is to be called from a debugger, +for example when a breakpoint on ut_dbg_assertion_failed is hit. */ +UNIV_INTERN +void +btr_blob_dbg_print( +/*===============*/ + const dict_index_t* index) /*!< in: index tree */ +{ + const ib_rbt_node_t* node; + + if (!index->blobs) { + return; + } + + /* We intentionally do not acquire index->blobs_mutex here. + This function is to be called from a debugger, and the caller + should make sure that the index->blobs_mutex is held. */ + + for (node = rbt_first(index->blobs); + node != NULL; node = rbt_next(index->blobs, node)) { + const btr_blob_dbg_t* b + = rbt_value(btr_blob_dbg_t, node); + fprintf(stderr, "%u:%u:%u->%u%s%s%s\n", + b->ref_page_no, b->ref_heap_no, b->ref_field_no, + b->blob_page_no, + b->owner ? "" : "(disowned)", + b->always_owner ? "" : "(has disowned)", + b->del ? "(deleted)" : ""); + } +} + +/**************************************************************//** +Remove from index->blobs any references to off-page columns from a record. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove_rec( +/*====================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint i; + ulint count = 0; + btr_blob_dbg_t b; + + ut_ad(rec_offs_validate(rec, index, offsets)); + + if (!rec_offs_any_extern(offsets)) { + return(0); + } + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i)) { + ulint len; + const byte* field_ref = rec_get_nth_field( + rec, offsets, i, &len); + + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + + switch (b.blob_page_no) { + case 0: + /* The column has not been stored yet. + The BLOB pointer must be all zero. + There cannot be a BLOB starting at + page 0, because page 0 is reserved for + the tablespace header. */ + ut_a(!memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + /* fall through */ + case FIL_NULL: + /* the column has been freed already */ + continue; + } + + btr_blob_dbg_rbt_delete(index, &b, ctx); + count++; + } + } + + return(count); +} + +/**************************************************************//** +Check that there are no references to off-page columns from or to +the given page. Invoked when freeing or clearing a page. +@return TRUE when no orphan references exist */ +UNIV_INTERN +ibool +btr_blob_dbg_is_empty( +/*==================*/ + dict_index_t* index, /*!< in: index */ + ulint page_no) /*!< in: page number */ +{ + const ib_rbt_node_t* node; + ibool success = TRUE; + + if (!index->blobs) { + return(success); + } + + mutex_enter(&index->blobs_mutex); + + for (node = rbt_first(index->blobs); + node != NULL; node = rbt_next(index->blobs, node)) { + const btr_blob_dbg_t* b + = rbt_value(btr_blob_dbg_t, node); + + if (b->ref_page_no != page_no && b->blob_page_no != page_no) { + continue; + } + + fprintf(stderr, + "InnoDB: orphan BLOB ref%s%s%s %u:%u:%u->%u\n", + b->owner ? "" : "(disowned)", + b->always_owner ? "" : "(has disowned)", + b->del ? "(deleted)" : "", + b->ref_page_no, b->ref_heap_no, b->ref_field_no, + b->blob_page_no); + + if (b->blob_page_no != page_no || b->owner || !b->del) { + success = FALSE; + } + } + + mutex_exit(&index->blobs_mutex); + return(success); +} + +/**************************************************************//** +Count and process all references to off-page columns on a page. +@return number of references processed */ +UNIV_INTERN +ulint +btr_blob_dbg_op( +/*============*/ + const page_t* page, /*!< in: B-tree leaf page */ + const rec_t* rec, /*!< in: record to start from + (NULL to process the whole page) */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx, /*!< in: context (for logging) */ + const btr_blob_dbg_op_f op) /*!< in: operation on records */ +{ + ulint count = 0; + mem_heap_t* heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + rec_offs_init(offsets_); + + ut_a(fil_page_get_type(page) == FIL_PAGE_INDEX); + ut_a(!rec || page_align(rec) == page); + + if (!index->blobs || !page_is_leaf(page) + || !dict_index_is_clust(index)) { + return(0); + } + + if (rec == NULL) { + rec = page_get_infimum_rec(page); + } + + do { + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); + count += op(rec, index, offsets, ctx); + rec = page_rec_get_next_const(rec); + } while (!page_rec_is_supremum(rec)); + + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + + return(count); +} + +/**************************************************************//** +Count and add to index->blobs any references to off-page columns +from records on a page. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add( +/*=============*/ + const page_t* page, /*!< in: rewritten page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ +{ + btr_blob_dbg_assert_empty(index, page_get_page_no(page)); + + return(btr_blob_dbg_op(page, NULL, index, ctx, btr_blob_dbg_add_rec)); +} + +/**************************************************************//** +Count and remove from index->blobs any references to off-page columns +from records on a page. +Used when reorganizing a page, before copying the records. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove( +/*================*/ + const page_t* page, /*!< in: b-tree page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint count; + + count = btr_blob_dbg_op(page, NULL, index, ctx, + btr_blob_dbg_remove_rec); + + /* Check that no references exist. */ + btr_blob_dbg_assert_empty(index, page_get_page_no(page)); + + return(count); +} + +/**************************************************************//** +Restore in index->blobs any references to off-page columns +Used when page reorganize fails due to compressed page overflow. */ +UNIV_INTERN +void +btr_blob_dbg_restore( +/*=================*/ + const page_t* npage, /*!< in: page that failed to compress */ + const page_t* page, /*!< in: copy of original page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint removed; + ulint added; + + ut_a(page_get_page_no(npage) == page_get_page_no(page)); + ut_a(page_get_space_id(npage) == page_get_space_id(page)); + + removed = btr_blob_dbg_remove(npage, index, ctx); + added = btr_blob_dbg_add(page, index, ctx); + ut_a(added == removed); +} + +/**************************************************************//** +Modify the 'deleted' flag of a record. */ +UNIV_INTERN +void +btr_blob_dbg_set_deleted_flag( +/*==========================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ibool del) /*!< in: TRUE=deleted, FALSE=exists */ +{ + const ib_rbt_node_t* node; + btr_blob_dbg_t b; + btr_blob_dbg_t* c; + ulint i; + + ut_ad(rec_offs_validate(rec, index, offsets)); + ut_a(dict_index_is_clust(index)); + ut_a(del == !!del);/* must be FALSE==0 or TRUE==1 */ + + if (!rec_offs_any_extern(offsets) || !index->blobs) { + + return; + } + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i)) { + ulint len; + const byte* field_ref = rec_get_nth_field( + rec, offsets, i, &len); + + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + + switch (b.blob_page_no) { + case 0: + ut_a(memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + /* page number 0 is for the + page allocation bitmap */ + case FIL_NULL: + /* the column has been freed already */ + ut_error; + } + + mutex_enter(&index->blobs_mutex); + node = rbt_lookup(index->blobs, &b); + ut_a(node); + + c = rbt_value(btr_blob_dbg_t, node); + /* The flag should be modified. */ + c->del = del; + if (btr_blob_dbg_msg) { + b = *c; + mutex_exit(&index->blobs_mutex); + btr_blob_dbg_msg_issue("del_mk", &b, ""); + } else { + mutex_exit(&index->blobs_mutex); + } + } + } +} + +/**************************************************************//** +Change the ownership of an off-page column. */ +UNIV_INTERN +void +btr_blob_dbg_owner( +/*===============*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ulint i, /*!< in: ith field in rec */ + ibool own) /*!< in: TRUE=owned, FALSE=disowned */ +{ + const ib_rbt_node_t* node; + btr_blob_dbg_t b; + const byte* field_ref; + ulint len; + + ut_ad(rec_offs_validate(rec, index, offsets)); + ut_a(rec_offs_nth_extern(offsets, i)); + + field_ref = rec_get_nth_field(rec, offsets, i, &len); + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = i; + b.owner = !(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG); + b.blob_page_no = mach_read_from_4(field_ref + BTR_EXTERN_PAGE_NO); + + ut_a(b.owner == own); + + mutex_enter(&index->blobs_mutex); + node = rbt_lookup(index->blobs, &b); + /* row_ins_clust_index_entry_by_modify() invokes + btr_cur_unmark_extern_fields() also for the newly inserted + references, which are all zero bytes until the columns are stored. + The node lookup must fail if and only if that is the case. */ + ut_a(!memcmp(field_ref, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE) + == !node); + + if (node) { + btr_blob_dbg_t* c = rbt_value(btr_blob_dbg_t, node); + /* Some code sets ownership from TRUE to TRUE. + We do not allow changing ownership from FALSE to FALSE. */ + ut_a(own || c->owner); + + c->owner = own; + if (!own) { + c->always_owner = FALSE; + } + } + + mutex_exit(&index->blobs_mutex); +} +#endif /* UNIV_BLOB_DEBUG */ + /* Latching strategy of the InnoDB B-tree -------------------------------------- @@ -296,6 +850,7 @@ btr_page_create( page_t* page = buf_block_get_frame(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); + btr_blob_dbg_assert_empty(index, buf_block_get_page_no(block)); if (UNIV_LIKELY_NULL(page_zip)) { page_create_zip(block, index, level, mtr); @@ -489,6 +1044,7 @@ btr_page_free_low( modify clock */ buf_block_modify_clock_inc(block); + btr_blob_dbg_assert_empty(index, buf_block_get_page_no(block)); if (dict_index_is_ibuf(index)) { @@ -773,6 +1329,13 @@ btr_create( block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); } else { +#ifdef UNIV_BLOB_DEBUG + if ((type & DICT_CLUSTERED) && !index->blobs) { + mutex_create(&index->blobs_mutex, SYNC_ANY_LATCH); + index->blobs = rbt_create(sizeof(btr_blob_dbg_t), + btr_blob_dbg_cmp); + } +#endif /* UNIV_BLOB_DEBUG */ block = fseg_create(space, 0, PAGE_HEADER + PAGE_BTR_SEG_TOP, mtr); } @@ -996,6 +1559,7 @@ btr_page_reorganize_low( block->check_index_page_at_flush = TRUE; #endif /* !UNIV_HOTBACKUP */ + btr_blob_dbg_remove(page, index, "btr_page_reorganize"); /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ @@ -1024,6 +1588,8 @@ btr_page_reorganize_low( (!page_zip_compress(page_zip, page, index, NULL))) { /* Restore the old page and exit. */ + btr_blob_dbg_restore(page, temp_page, index, + "btr_page_reorganize_compress_fail"); #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /* Check that the bytes that we skip are identical. */ @@ -1157,6 +1723,7 @@ btr_page_empty( #endif /* UNIV_ZIP_DEBUG */ btr_search_drop_page_hash_index(block); + btr_blob_dbg_remove(page, index, "btr_page_empty"); /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ @@ -2497,6 +3064,7 @@ btr_lift_page_up( index); } + btr_blob_dbg_remove(page, index, "btr_lift_page_up"); lock_update_copy_and_discard(father_block, block); /* Go upward to root page, decrementing levels by one. */ @@ -2758,6 +3326,7 @@ err_exit: lock_update_merge_right(merge_block, orig_succ, block); } + btr_blob_dbg_remove(page, index, "btr_compress"); mem_heap_free(heap); if (!dict_index_is_clust(index) && page_is_leaf(merge_page)) { @@ -2988,6 +3557,8 @@ btr_discard_page( block); } + btr_blob_dbg_remove(page, index, "btr_discard_page"); + /* Free the file page */ btr_page_free(index, block, mtr); diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 704cc606a5f..86d77c79e7b 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -2572,6 +2572,7 @@ btr_cur_del_mark_set_clust_rec( page_zip = buf_block_get_page_zip(block); + btr_blob_dbg_set_deleted_flag(rec, index, offsets, val); btr_rec_set_deleted_flag(rec, page_zip, val); trx = thr_get_trx(thr); @@ -3595,6 +3596,8 @@ btr_cur_set_ownership_of_extern_field( } else { mach_write_to_1(data + local_len + BTR_EXTERN_LEN, byte_val); } + + btr_blob_dbg_owner(rec, index, offsets, i, val); } /*******************************************************************//** @@ -4094,6 +4097,11 @@ btr_store_big_rec_extern_fields_func( } if (prev_page_no == FIL_NULL) { + btr_blob_dbg_add_blob( + rec, big_rec_vec->fields[i] + .field_no, page_no, index, + "store"); + mach_write_to_4(field_ref + BTR_EXTERN_SPACE_ID, space_id); @@ -4169,6 +4177,11 @@ next_zip_page: MLOG_4BYTES, &mtr); if (prev_page_no == FIL_NULL) { + btr_blob_dbg_add_blob( + rec, big_rec_vec->fields[i] + .field_no, page_no, index, + "store"); + mlog_write_ulint(field_ref + BTR_EXTERN_SPACE_ID, space_id, @@ -4337,6 +4350,37 @@ btr_free_externally_stored_field( rec_zip_size = 0; } +#ifdef UNIV_BLOB_DEBUG + if (!(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG) + && !((field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_INHERITED_FLAG) + && (rb_ctx == RB_NORMAL || rb_ctx == RB_RECOVERY))) { + /* This off-page column will be freed. + Check that no references remain. */ + + btr_blob_dbg_t b; + + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + + if (rec) { + /* Remove the reference from the record to the + BLOB. If the BLOB were not freed, the + reference would be removed when the record is + removed. Freeing the BLOB will overwrite the + BTR_EXTERN_PAGE_NO in the field_ref of the + record with FIL_NULL, which would make the + btr_blob_dbg information inconsistent with the + record. */ + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = i; + btr_blob_dbg_rbt_delete(index, &b, "free"); + } + + btr_blob_dbg_assert_empty(index, b.blob_page_no); + } +#endif /* UNIV_BLOB_DEBUG */ + for (;;) { #ifdef UNIV_SYNC_DEBUG buf_block_t* rec_block; diff --git a/storage/innodb_plugin/dict/dict0mem.c b/storage/innodb_plugin/dict/dict0mem.c index 3287247029f..aef815dd2f6 100644 --- a/storage/innodb_plugin/dict/dict0mem.c +++ b/storage/innodb_plugin/dict/dict0mem.c @@ -36,6 +36,9 @@ Created 1/8/1996 Heikki Tuuri #ifndef UNIV_HOTBACKUP # include "lock0lock.h" #endif /* !UNIV_HOTBACKUP */ +#ifdef UNIV_BLOB_DEBUG +# include "ut0rbt.h" +#endif /* UNIV_BLOB_DEBUG */ #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when creating a table or index object */ @@ -316,6 +319,12 @@ dict_mem_index_free( { ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); +#ifdef UNIV_BLOB_DEBUG + if (index->blobs) { + mutex_free(&index->blobs_mutex); + rbt_free(index->blobs); + } +#endif /* UNIV_BLOB_DEBUG */ mem_heap_free(index->heap); } diff --git a/storage/innodb_plugin/include/btr0btr.h b/storage/innodb_plugin/include/btr0btr.h index dde3a0bab69..5aa02694e0e 100644 --- a/storage/innodb_plugin/include/btr0btr.h +++ b/storage/innodb_plugin/include/btr0btr.h @@ -81,6 +81,91 @@ UNIQUE definition on secondary indexes when we decide if we can use the insert buffer to speed up inserts */ #define BTR_IGNORE_SEC_UNIQUE 2048 +#ifdef UNIV_BLOB_DEBUG +# include "ut0rbt.h" +/** An index->blobs entry for keeping track of off-page column references */ +struct btr_blob_dbg_struct +{ + unsigned blob_page_no:32; /*!< first BLOB page number */ + unsigned ref_page_no:32; /*!< referring page number */ + unsigned ref_heap_no:16; /*!< referring heap number */ + unsigned ref_field_no:10; /*!< referring field number */ + unsigned owner:1; /*!< TRUE if BLOB owner */ + unsigned always_owner:1; /*!< TRUE if always + has been the BLOB owner; + reset to TRUE on B-tree + page splits and merges */ + unsigned del:1; /*!< TRUE if currently + delete-marked */ +}; + +/**************************************************************//** +Add a reference to an off-page column to the index->blobs map. */ +UNIV_INTERN +void +btr_blob_dbg_add_blob( +/*==================*/ + const rec_t* rec, /*!< in: clustered index record */ + ulint field_no, /*!< in: number of off-page column */ + ulint page_no, /*!< in: start page of the column */ + dict_index_t* index, /*!< in/out: index tree */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Display the references to off-page columns. +This function is to be called from a debugger, +for example when a breakpoint on ut_dbg_assertion_failed is hit. */ +UNIV_INTERN +void +btr_blob_dbg_print( +/*===============*/ + const dict_index_t* index) /*!< in: index tree */ + __attribute__((nonnull)); +/**************************************************************//** +Check that there are no references to off-page columns from or to +the given page. Invoked when freeing or clearing a page. +@return TRUE when no orphan references exist */ +UNIV_INTERN +ibool +btr_blob_dbg_is_empty( +/*==================*/ + dict_index_t* index, /*!< in: index */ + ulint page_no) /*!< in: page number */ + __attribute__((nonnull, warn_unused_result)); + +/**************************************************************//** +Modify the 'deleted' flag of a record. */ +UNIV_INTERN +void +btr_blob_dbg_set_deleted_flag( +/*==========================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ibool del) /*!< in: TRUE=deleted, FALSE=exists */ + __attribute__((nonnull)); +/**************************************************************//** +Change the ownership of an off-page column. */ +UNIV_INTERN +void +btr_blob_dbg_owner( +/*===============*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ulint i, /*!< in: ith field in rec */ + ibool own) /*!< in: TRUE=owned, FALSE=disowned */ + __attribute__((nonnull)); +/** Assert that there are no BLOB references to or from the given page. */ +# define btr_blob_dbg_assert_empty(index, page_no) \ + ut_a(btr_blob_dbg_is_empty(index, page_no)) +#else /* UNIV_BLOB_DEBUG */ +# define btr_blob_dbg_add_blob(rec, field_no, page, index, ctx) ((void) 0) +# define btr_blob_dbg_set_deleted_flag(rec, index, offsets, del)((void) 0) +# define btr_blob_dbg_owner(rec, index, offsets, i, val) ((void) 0) +# define btr_blob_dbg_assert_empty(index, page_no) ((void) 0) +#endif /* UNIV_BLOB_DEBUG */ + /**************************************************************//** Gets the root node of a tree and x-latches it. @return root page, x-latched */ diff --git a/storage/innodb_plugin/include/btr0types.h b/storage/innodb_plugin/include/btr0types.h index ef4a6b04b34..07c06fb18d7 100644 --- a/storage/innodb_plugin/include/btr0types.h +++ b/storage/innodb_plugin/include/btr0types.h @@ -38,6 +38,131 @@ typedef struct btr_cur_struct btr_cur_t; /** B-tree search information for the adaptive hash index */ typedef struct btr_search_struct btr_search_t; +#ifdef UNIV_BLOB_DEBUG +# include "buf0types.h" +/** An index->blobs entry for keeping track of off-page column references */ +typedef struct btr_blob_dbg_struct btr_blob_dbg_t; + +/** Insert to index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_insert( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); + +/** Remove from index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_delete( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); + +/**************************************************************//** +Add to index->blobs any references to off-page columns from a record. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add_rec( +/*=================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Remove from index->blobs any references to off-page columns from a record. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove_rec( +/*====================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Count and add to index->blobs any references to off-page columns +from records on a page. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add( +/*=============*/ + const page_t* page, /*!< in: rewritten page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Count and remove from index->blobs any references to off-page columns +from records on a page. +Used when reorganizing a page, before copying the records. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove( +/*================*/ + const page_t* page, /*!< in: b-tree page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Restore in index->blobs any references to off-page columns +Used when page reorganize fails due to compressed page overflow. */ +UNIV_INTERN +void +btr_blob_dbg_restore( +/*=================*/ + const page_t* npage, /*!< in: page that failed to compress */ + const page_t* page, /*!< in: copy of original page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); + +/** Operation that processes the BLOB references of an index record +@param[in] rec record on index page +@param[in/out] index the index tree of the record +@param[in] offsets rec_get_offsets(rec,index) +@param[in] ctx context (for logging) +@return number of BLOB references processed */ +typedef ulint (*btr_blob_dbg_op_f) +(const rec_t* rec,dict_index_t* index,const ulint* offsets,const char* ctx); + +/**************************************************************//** +Count and process all references to off-page columns on a page. +@return number of references processed */ +UNIV_INTERN +ulint +btr_blob_dbg_op( +/*============*/ + const page_t* page, /*!< in: B-tree leaf page */ + const rec_t* rec, /*!< in: record to start from + (NULL to process the whole page) */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx, /*!< in: context (for logging) */ + const btr_blob_dbg_op_f op) /*!< in: operation on records */ + __attribute__((nonnull(1,3,4,5))); +#else /* UNIV_BLOB_DEBUG */ +# define btr_blob_dbg_add_rec(rec, index, offsets, ctx) ((void) 0) +# define btr_blob_dbg_add(page, index, ctx) ((void) 0) +# define btr_blob_dbg_remove_rec(rec, index, offsets, ctx) ((void) 0) +# define btr_blob_dbg_remove(page, index, ctx) ((void) 0) +# define btr_blob_dbg_restore(npage, page, index, ctx) ((void) 0) +# define btr_blob_dbg_op(page, rec, index, ctx, op) ((void) 0) +#endif /* UNIV_BLOB_DEBUG */ + /** The size of a reference to data stored on a different page. The reference is stored at the end of the prefix of the field in the index record. */ diff --git a/storage/innodb_plugin/include/dict0mem.h b/storage/innodb_plugin/include/dict0mem.h index 09a068ccb93..bd32a239cfd 100644 --- a/storage/innodb_plugin/include/dict0mem.h +++ b/storage/innodb_plugin/include/dict0mem.h @@ -340,6 +340,13 @@ struct dict_index_struct{ index, or 0 if the index existed when InnoDB was started up */ #endif /* !UNIV_HOTBACKUP */ +#ifdef UNIV_BLOB_DEBUG + mutex_t blobs_mutex; + /*!< mutex protecting blobs */ + void* blobs; /*!< map of (page_no,heap_no,field_no) + to first_blob_page_no; protected by + blobs_mutex; @see btr_blob_dbg_t */ +#endif /* UNIV_BLOB_DEBUG */ #ifdef UNIV_DEBUG ulint magic_n;/*!< magic number */ /** Value of dict_index_struct::magic_n */ diff --git a/storage/innodb_plugin/include/page0zip.h b/storage/innodb_plugin/include/page0zip.h index 574809e5227..00c1d0516e6 100644 --- a/storage/innodb_plugin/include/page0zip.h +++ b/storage/innodb_plugin/include/page0zip.h @@ -420,7 +420,7 @@ page_zip_copy_recs( const page_t* src, /*!< in: page */ dict_index_t* index, /*!< in: index of the B-tree */ mtr_t* mtr) /*!< in: mini-transaction */ - __attribute__((nonnull(1,2,3,4))); + __attribute__((nonnull)); #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index 690bfd5d6a9..6dd38df3782 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -194,6 +194,8 @@ this will break redo log file compatibility, but it may be useful when debugging redo log application problems. */ #define UNIV_MEM_DEBUG /* detect memory leaks etc */ #define UNIV_IBUF_DEBUG /* debug the insert buffer */ +#define UNIV_BLOB_DEBUG /* track BLOB ownership; +assumes that no BLOBs survive server restart */ #define UNIV_IBUF_COUNT_DEBUG /* debug the insert buffer; this limits the database to IBUF_COUNT_N_SPACES and IBUF_COUNT_N_PAGES, and the insert buffer must be empty when the database is started */ diff --git a/storage/innodb_plugin/page/page0cur.c b/storage/innodb_plugin/page/page0cur.c index f10f16a7dd9..936762b986a 100644 --- a/storage/innodb_plugin/page/page0cur.c +++ b/storage/innodb_plugin/page/page0cur.c @@ -1149,6 +1149,8 @@ use_heap: current_rec, index, mtr); } + btr_blob_dbg_add_rec(insert_rec, index, offsets, "insert"); + return(insert_rec); } @@ -1195,10 +1197,12 @@ page_cur_insert_rec_zip_reorg( } /* Out of space: restore the page */ + btr_blob_dbg_remove(page, index, "insert_zip_fail"); if (!page_zip_decompress(page_zip, page, FALSE)) { ut_error; /* Memory corrupted? */ } ut_ad(page_validate(page, index)); + btr_blob_dbg_add(page, index, "insert_zip_fail"); return(NULL); } @@ -1490,6 +1494,8 @@ use_heap: page_zip_write_rec(page_zip, insert_rec, index, offsets, 1); + btr_blob_dbg_add_rec(insert_rec, index, offsets, "insert_zip_ok"); + /* 9. Write log record of the insert */ if (UNIV_LIKELY(mtr != NULL)) { page_cur_insert_rec_write_log(insert_rec, rec_size, @@ -1697,6 +1703,9 @@ page_copy_rec_list_end_to_created_page( heap_top += rec_size; + rec_offs_make_valid(insert_rec, index, offsets); + btr_blob_dbg_add_rec(insert_rec, index, offsets, "copy_end"); + page_cur_insert_rec_write_log(insert_rec, rec_size, prev_rec, index, mtr); prev_rec = insert_rec; @@ -1944,6 +1953,7 @@ page_cur_delete_rec( page_dir_slot_set_n_owned(cur_dir_slot, page_zip, cur_n_owned - 1); /* 6. Free the memory occupied by the record */ + btr_blob_dbg_remove_rec(current_rec, index, offsets, "delete"); page_mem_free(page, page_zip, current_rec, index, offsets); /* 7. Now we have decremented the number of owned records of the slot. diff --git a/storage/innodb_plugin/page/page0page.c b/storage/innodb_plugin/page/page0page.c index 10008f9ac25..6cae03e8829 100644 --- a/storage/innodb_plugin/page/page0page.c +++ b/storage/innodb_plugin/page/page0page.c @@ -685,12 +685,16 @@ page_copy_rec_list_end( if (UNIV_UNLIKELY (!page_zip_reorganize(new_block, index, mtr))) { + btr_blob_dbg_remove(new_page, index, + "copy_end_reorg_fail"); if (UNIV_UNLIKELY (!page_zip_decompress(new_page_zip, new_page, FALSE))) { ut_error; } ut_ad(page_validate(new_page, index)); + btr_blob_dbg_add(new_page, index, + "copy_end_reorg_fail"); return(NULL); } else { /* The page was reorganized: @@ -803,12 +807,16 @@ page_copy_rec_list_start( if (UNIV_UNLIKELY (!page_zip_reorganize(new_block, index, mtr))) { + btr_blob_dbg_remove(new_page, index, + "copy_start_reorg_fail"); if (UNIV_UNLIKELY (!page_zip_decompress(new_page_zip, new_page, FALSE))) { ut_error; } ut_ad(page_validate(new_page, index)); + btr_blob_dbg_add(new_page, index, + "copy_start_reorg_fail"); return(NULL); } else { /* The page was reorganized: @@ -1080,6 +1088,9 @@ page_delete_rec_list_end( /* Remove the record chain segment from the record chain */ page_rec_set_next(prev_rec, page_get_supremum_rec(page)); + btr_blob_dbg_op(page, rec, index, "delete_end", + btr_blob_dbg_remove_rec); + /* Catenate the deleted chain segment to the page free list */ page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE)); diff --git a/storage/innodb_plugin/page/page0zip.c b/storage/innodb_plugin/page/page0zip.c index bb9b0995c72..a1dd4177ba8 100644 --- a/storage/innodb_plugin/page/page0zip.c +++ b/storage/innodb_plugin/page/page0zip.c @@ -4451,6 +4451,8 @@ page_zip_reorganize( /* Copy the old page to temporary space */ buf_frame_copy(temp_page, page); + btr_blob_dbg_remove(page, index, "zip_reorg"); + /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ @@ -4509,7 +4511,7 @@ page_zip_copy_recs( mtr_t* mtr) /*!< in: mini-transaction */ { ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); - ut_ad(mtr_memo_contains_page(mtr, (page_t*) src, MTR_MEMO_PAGE_X_FIX)); + ut_ad(mtr_memo_contains_page(mtr, src, MTR_MEMO_PAGE_X_FIX)); ut_ad(!dict_index_is_ibuf(index)); #ifdef UNIV_ZIP_DEBUG /* The B-tree operations that call this function may set @@ -4579,6 +4581,7 @@ page_zip_copy_recs( #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ + btr_blob_dbg_add(page, index, "page_zip_copy_recs"); page_zip_compress_write_log(page_zip, page, index, mtr); } diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 9ded3d68018..3a6de4b94a7 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -498,14 +498,49 @@ row_upd_rec_in_place( n_fields = upd_get_n_fields(update); for (i = 0; i < n_fields; i++) { +#ifdef UNIV_BLOB_DEBUG + btr_blob_dbg_t b; + const byte* field_ref = NULL; +#endif /* UNIV_BLOB_DEBUG */ + upd_field = upd_get_nth_field(update, i); new_val = &(upd_field->new_val); ut_ad(!dfield_is_ext(new_val) == !rec_offs_nth_extern(offsets, upd_field->field_no)); +#ifdef UNIV_BLOB_DEBUG + if (dfield_is_ext(new_val)) { + ulint len; + field_ref = rec_get_nth_field(rec, offsets, i, &len); + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + ut_a(b.ref_field_no >= index->n_uniq); + btr_blob_dbg_rbt_delete(index, &b, "upd_in_place"); + } +#endif /* UNIV_BLOB_DEBUG */ rec_set_nth_field(rec, offsets, upd_field->field_no, dfield_get_data(new_val), dfield_get_len(new_val)); + +#ifdef UNIV_BLOB_DEBUG + if (dfield_is_ext(new_val)) { + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + b.always_owner = b.owner = !(field_ref[BTR_EXTERN_LEN] + & BTR_EXTERN_OWNER_FLAG); + b.del = rec_get_deleted_flag( + rec, rec_offs_comp(offsets)); + + btr_blob_dbg_rbt_insert(index, &b, "upd_in_place"); + } +#endif /* UNIV_BLOB_DEBUG */ } if (UNIV_LIKELY_NULL(page_zip)) { diff --git a/storage/innodb_plugin/srv/srv0start.c b/storage/innodb_plugin/srv/srv0start.c index 73f8f319704..f8b5049ca65 100644 --- a/storage/innodb_plugin/srv/srv0start.c +++ b/storage/innodb_plugin/srv/srv0start.c @@ -1061,6 +1061,12 @@ innobase_start_or_create_for_mysql(void) ); #endif +#ifdef UNIV_BLOB_DEBUG + fprintf(stderr, + "InnoDB: !!!!!!!! UNIV_BLOB_DEBUG switched on !!!!!!!!!\n" + "InnoDB: Server restart may fail with UNIV_BLOB_DEBUG\n"); +#endif /* UNIV_BLOB_DEBUG */ + #ifdef UNIV_SYNC_DEBUG fprintf(stderr, "InnoDB: !!!!!!!! UNIV_SYNC_DEBUG switched on !!!!!!!!!\n"); From 107b46070df5ee7c0d8ca7424f44a75538d07d14 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 9 Feb 2011 12:46:12 +0600 Subject: [PATCH 09/64] Follow up fix for bug#57450. batch_readline_init() was modified - return an error if the input source is a directory or a block device. This follow-up is necessary because on some platforms, such as Solaris, call to read() from directory may be successful. --- client/mysql.cc | 2 ++ client/readline.cc | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/client/mysql.cc b/client/mysql.cc index b19aaf61e58..e7e7b244a49 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1131,6 +1131,8 @@ int main(int argc,char *argv[]) if (status.batch && !status.line_buff && !(status.line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, stdin))) { + put_info("Can't initialize batch_readline - may be the input source is " + "a directory or a block device.", INFO_ERROR, 0); free_defaults(defaults_argv); my_end(0); exit(1); diff --git a/client/readline.cc b/client/readline.cc index 7f283502ecb..9ac9ea5824e 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "my_readline.h" static bool init_line_buffer(LINE_BUFFER *buffer,File file,ulong size, @@ -30,6 +31,13 @@ static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length); LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) { LINE_BUFFER *line_buff; + MY_STAT input_file_stat; + + if (my_fstat(fileno(file), &input_file_stat, MYF(MY_WME)) || + MY_S_ISDIR(input_file_stat.st_mode) || + MY_S_ISBLK(input_file_stat.st_mode)) + return 0; + if (!(line_buff=(LINE_BUFFER*) my_malloc(sizeof(*line_buff),MYF(MY_WME | MY_ZEROFILL)))) return 0; From 77838ae2cd89d95ffca6a295dc888551b40254e7 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 9 Feb 2011 01:15:06 -0800 Subject: [PATCH 10/64] Fix Bug #59048 truncate table or create index could leave index->page to be FIL_NULL rb://545 approved by Sunny Bains --- storage/innobase/dict/dict0load.c | 34 ++++++++++++++++++++------- storage/innobase/include/dict0dict.h | 12 ++++++++++ storage/innobase/include/dict0dict.ic | 30 ++++++++++++++++++++++- storage/innobase/include/dict0load.h | 5 +++- storage/innobase/include/dict0mem.h | 4 ++++ storage/innobase/include/dict0types.h | 14 +++++++++++ storage/innobase/row/row0mysql.c | 6 ++--- 7 files changed, 92 insertions(+), 13 deletions(-) diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index 3561e7220ab..ca22132a360 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -1323,7 +1323,10 @@ ulint dict_load_indexes( /*==============*/ dict_table_t* table, /*!< in/out: table */ - mem_heap_t* heap) /*!< in: memory heap for temporary storage */ + mem_heap_t* heap, /*!< in: memory heap for temporary storage */ + dict_err_ignore_t ignore_err) + /*!< in: error to be ignored when + loading the index definition */ { dict_table_t* sys_indexes; dict_index_t* sys_index; @@ -1406,10 +1409,22 @@ dict_load_indexes( "InnoDB: but the index tree has been freed!\n", index->name, table->name); + if (ignore_err & DICT_ERR_IGNORE_INDEX_ROOT) { + /* If caller can tolerate this error, + we will continue to load the index and + let caller deal with this error. However + mark the index and table corrupted */ + index->corrupted = TRUE; + table->corrupted = TRUE; + fprintf(stderr, + "InnoDB: Index is corrupt but forcing" + " load into data dictionary\n"); + } else { corrupted: - dict_mem_index_free(index); - error = DB_CORRUPTION; - goto func_exit; + dict_mem_index_free(index); + error = DB_CORRUPTION; + goto func_exit; + } } else if (!dict_index_is_clust(index) && NULL == dict_table_get_first_index(table)) { @@ -1618,7 +1633,10 @@ dict_load_table( /*============*/ const char* name, /*!< in: table name in the databasename/tablename format */ - ibool cached) /*!< in: TRUE=add to cache, FALSE=do not */ + ibool cached, /*!< in: TRUE=add to cache, FALSE=do not */ + dict_err_ignore_t ignore_err) + /*!< in: error to be ignored when loading + table and its indexes' definition */ { dict_table_t* table; dict_table_t* sys_tables; @@ -1733,7 +1751,7 @@ err_exit: mem_heap_empty(heap); - err = dict_load_indexes(table, heap); + err = dict_load_indexes(table, heap, ignore_err); /* Initialize table foreign_child value. Its value could be changed when dict_load_foreigns() is called below */ @@ -1869,7 +1887,7 @@ dict_load_table_on_id( field = rec_get_nth_field_old(rec, 1, &len); /* Load the table definition to memory */ table = dict_load_table(mem_heap_strdupl(heap, (char*) field, len), - TRUE); + TRUE, DICT_ERR_IGNORE_NONE); btr_pcur_close(&pcur); mtr_commit(&mtr); @@ -1894,7 +1912,7 @@ dict_load_sys_table( heap = mem_heap_create(1000); - dict_load_indexes(table, heap); + dict_load_indexes(table, heap, DICT_ERR_IGNORE_NONE); mem_heap_free(heap); } diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 033c435bf16..d6f2bebae3a 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -441,6 +441,18 @@ function. @return table, NULL if not found */ UNIV_INLINE dict_table_t* +dict_table_get_low_ignore_err( +/*===========================*/ + const char* table_name, /*!< in: table name */ + dict_err_ignore_t + ignore_err); /*!< in: error to be ignored when + loading a table definition */ +/**********************************************************************//** +Gets a table; loads it to the dictionary cache if necessary. A low-level +function. +@return table, NULL if not found */ +UNIV_INLINE +dict_table_t* dict_table_get_low( /*===============*/ const char* table_name); /*!< in: table name */ diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index 42f124dedfc..59606af7056 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -827,6 +827,34 @@ dict_table_check_if_in_cache_low( return(table); } +/**********************************************************************//** +load a table into dictionary cache, ignore any error specified during load; +@return table, NULL if not found */ +UNIV_INLINE +dict_table_t* +dict_table_get_low_ignore_err( +/*==========================*/ + const char* table_name, /*!< in: table name */ + dict_err_ignore_t + ignore_err) /*!< in: error to be ignored when + loading a table definition */ +{ + dict_table_t* table; + + ut_ad(table_name); + ut_ad(mutex_own(&(dict_sys->mutex))); + + table = dict_table_check_if_in_cache_low(table_name); + + if (table == NULL) { + table = dict_load_table(table_name, TRUE, ignore_err); + } + + ut_ad(!table || table->cached); + + return(table); +} + /**********************************************************************//** Gets a table; loads it to the dictionary cache if necessary. A low-level function. @@ -845,7 +873,7 @@ dict_table_get_low( table = dict_table_check_if_in_cache_low(table_name); if (table == NULL) { - table = dict_load_table(table_name, TRUE); + table = dict_load_table(table_name, TRUE, DICT_ERR_IGNORE_NONE); } ut_ad(!table || table->cached); diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index f009f221f32..51d07f43446 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -170,7 +170,10 @@ dict_load_table( /*============*/ const char* name, /*!< in: table name in the databasename/tablename format */ - ibool cached);/*!< in: TRUE=add to cache, FALSE=do not */ + ibool cached, /*!< in: TRUE=add to cache, FALSE=do not */ + dict_err_ignore_t ignore_err); + /*!< in: error to be ignored when loading + table and its indexes' definition */ /***********************************************************************//** Loads a table object based on the table id. @return table; NULL if table does not exist */ diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 95bcb0cab2d..75d3b2c3302 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -361,6 +361,8 @@ struct dict_index_struct{ /*!< TRUE if this index is marked to be dropped in ha_innobase::prepare_drop_index(), otherwise FALSE */ + unsigned corrupted:1; + /*!< TRUE if the index object is corrupted */ dict_field_t* fields; /*!< array of field descriptions */ #ifndef UNIV_HOTBACKUP UT_LIST_NODE_T(dict_index_t) @@ -494,6 +496,8 @@ struct dict_table_struct{ to the dictionary cache */ unsigned n_def:10;/*!< number of columns defined so far */ unsigned n_cols:10;/*!< number of columns */ + unsigned corrupted:1; + /*!< TRUE if table is corrupted */ dict_col_t* cols; /*!< array of column descriptions */ const char* col_names; /*!< Column names packed in a character string diff --git a/storage/innobase/include/dict0types.h b/storage/innobase/include/dict0types.h index 687209575c9..8cbd7cd5783 100644 --- a/storage/innobase/include/dict0types.h +++ b/storage/innobase/include/dict0types.h @@ -43,4 +43,18 @@ typedef struct tab_node_struct tab_node_t; typedef ib_id_t table_id_t; typedef ib_id_t index_id_t; +/** Error to ignore when we load table dictionary into memory. However, +the table and index will be marked as "corrupted", and caller will +be responsible to deal with corrupted table or index. +Note: please define the IGNORE_ERR_* as bits, so their value can +be or-ed together */ +enum dict_err_ignore { + DICT_ERR_IGNORE_NONE = 0, /*!< no error to ignore */ + DICT_ERR_IGNORE_INDEX_ROOT = 1, /*!< ignore error if index root + page is FIL_NUL or incorrect value */ + DICT_ERR_IGNORE_ALL = 0xFFFF /*!< ignore all errors */ +}; + +typedef enum dict_err_ignore dict_err_ignore_t; + #endif diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index a7b0cabfa4b..5fb4b4ac8c3 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -3132,7 +3132,7 @@ row_drop_table_for_mysql( ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ - table = dict_table_get_low(name); + table = dict_table_get_low_ignore_err(name, DICT_ERR_IGNORE_INDEX_ROOT); if (!table) { err = DB_TABLE_NOT_FOUND; @@ -3367,7 +3367,7 @@ check_next_foreign: dict_table_remove_from_cache(table); - if (dict_load_table(name, TRUE) != NULL) { + if (dict_load_table(name, TRUE, DICT_ERR_IGNORE_NONE) != NULL) { ut_print_timestamp(stderr); fputs(" InnoDB: Error: not able to remove table ", stderr); @@ -3513,7 +3513,7 @@ row_mysql_drop_temp_tables(void) btr_pcur_store_position(&pcur, &mtr); btr_pcur_commit_specify_mtr(&pcur, &mtr); - table = dict_load_table(table_name, TRUE); + table = dict_load_table(table_name, TRUE, DICT_ERR_IGNORE_NONE); if (table) { row_drop_table_for_mysql(table_name, trx, FALSE); From bdb4df0f266c7d7de794692f9579f4b1c8761ed1 Mon Sep 17 00:00:00 2001 From: "John H. Embretsen" Date: Wed, 9 Feb 2011 10:50:09 +0100 Subject: [PATCH 11/64] Backport to 5.0: Fix for Bug#52060 - test udf fails on Snow Leopard. Reverse DNS lookup of "localhost" returns "broadcasthost" on Snow Leopard (Mac), and NULL on most others. Simply ignore the output, as this is not an essential part of UDF testing. --- mysql-test/r/udf.result | 2 -- mysql-test/t/udf.test | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 92185962d1f..27cd8e640f3 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -40,8 +40,6 @@ ERROR HY000: Wrong number of arguments to reverse_lookup; Use the source select reverse_lookup("127.0.0.1"); select reverse_lookup(127,0,0,1); select reverse_lookup("localhost"); -reverse_lookup("localhost") -NULL select avgcost(); ERROR HY000: wrong number of arguments: AVGCOST() requires two arguments select avgcost(100,23.76); diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 648494b3df9..3633c77be0d 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -51,14 +51,17 @@ select lookup("localhost"); --error 0 select reverse_lookup(); -# These two functions should return "localhost", but it's +# These two function calls should return "localhost", but it's # depending on configuration, so just call them and don't log the result --disable_result_log select reverse_lookup("127.0.0.1"); select reverse_lookup(127,0,0,1); + +# This function call may return different results depending on platform, +# so ignore results (see Bug#52060). +select reverse_lookup("localhost"); --enable_result_log -select reverse_lookup("localhost"); --error 0 select avgcost(); --error 0 From 784e74235ae3c43bf5616198227412bc856d8bbc Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 9 Feb 2011 17:13:17 +0600 Subject: [PATCH 12/64] Follow up fix for bug#57450. batch_readline_init() was modified - make check for type of file for input stream unless target platform is WINDOWS since on this platform S_IFBLK is undefined. --- client/readline.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/readline.cc b/client/readline.cc index 9ac9ea5824e..fb8cde577b5 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -33,10 +33,12 @@ LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) LINE_BUFFER *line_buff; MY_STAT input_file_stat; +#ifndef __WIN__ if (my_fstat(fileno(file), &input_file_stat, MYF(MY_WME)) || MY_S_ISDIR(input_file_stat.st_mode) || MY_S_ISBLK(input_file_stat.st_mode)) return 0; +#endif if (!(line_buff=(LINE_BUFFER*) my_malloc(sizeof(*line_buff),MYF(MY_WME | MY_ZEROFILL)))) From 801d018411fd774c5e0a2165f125d605308cae04 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 10 Feb 2011 00:58:23 -0800 Subject: [PATCH 13/64] Merge a fix for Bug #53756 ALTER TABLE ADD PRIMARY KEY affects crash recovery This was already pushed to mysql-5.1-innodb some time ago (revision id jimmy.yang@oracle.com-20100907054137-tpuior7hez4f5ghl) but it was not merged to the 5.5 trees. --- .../suite/innodb/r/innodb_bug53756.result | 118 +++++++++++ .../suite/innodb/t/innodb_bug53756-master.opt | 1 + .../suite/innodb/t/innodb_bug53756.test | 184 ++++++++++++++++++ storage/innobase/dict/dict0load.c | 27 ++- 4 files changed, 316 insertions(+), 14 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug53756.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug53756-master.opt create mode 100644 mysql-test/suite/innodb/t/innodb_bug53756.test diff --git a/mysql-test/suite/innodb/r/innodb_bug53756.result b/mysql-test/suite/innodb/r/innodb_bug53756.result new file mode 100644 index 00000000000..37453be8201 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug53756.result @@ -0,0 +1,118 @@ +DROP TABLE IF EXISTS bug_53756 ; +CREATE TABLE bug_53756 (pk INT, c1 INT) ENGINE=InnoDB; +ALTER TABLE bug_53756 ADD PRIMARY KEY (pk); +INSERT INTO bug_53756 VALUES(1, 11), (2, 22), (3, 33), (4, 44); + +# Select a less restrictive isolation level. +SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +COMMIT; + +# Start a transaction in the default connection for isolation. +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +SELECT * FROM bug_53756; +pk c1 +1 11 +2 22 +3 33 +4 44 + +# connection con1 deletes row 1 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +DELETE FROM bug_53756 WHERE pk=1; + +# connection con2 deletes row 2 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +DELETE FROM bug_53756 WHERE pk=2; + +# connection con3 updates row 3 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +UPDATE bug_53756 SET c1=77 WHERE pk=3; + +# connection con4 updates row 4 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +UPDATE bug_53756 SET c1=88 WHERE pk=4; + +# connection con5 inserts row 5 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +INSERT INTO bug_53756 VALUES(5, 55); + +# connection con6 inserts row 6 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +INSERT INTO bug_53756 VALUES(6, 66); + +# connection con1 commits. +COMMIT; + +# connection con3 commits. +COMMIT; + +# connection con4 rolls back. +ROLLBACK; + +# connection con6 rolls back. +ROLLBACK; + +# The connections 2 and 5 stay open. + +# connection default selects resulting data. +# Delete of row 1 was committed. +# Update of row 3 was committed. +# Due to isolation level read committed, these should be included. +# All other changes should not be included. +SELECT * FROM bug_53756; +pk c1 +2 22 +3 77 +4 44 + +# connection default +# +# Crash server. +START TRANSACTION; +INSERT INTO bug_53756 VALUES (666,666); +SET SESSION debug="+d,crash_commit_before"; +COMMIT; +ERROR HY000: Lost connection to MySQL server during query + +# +# disconnect con1, con2, con3, con4, con5, con6. +# +# Restart server. + +# +# Select recovered data. +# Delete of row 1 was committed. +# Update of row 3 was committed. +# These should be included. +# All other changes should not be included. +# Delete of row 2 and insert of row 5 should be rolled back +SELECT * FROM bug_53756; +pk c1 +2 22 +3 77 +4 44 + +# Clean up. +DROP TABLE bug_53756; diff --git a/mysql-test/suite/innodb/t/innodb_bug53756-master.opt b/mysql-test/suite/innodb/t/innodb_bug53756-master.opt new file mode 100644 index 00000000000..425fda95086 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug53756-master.opt @@ -0,0 +1 @@ +--skip-stack-trace --skip-core-file diff --git a/mysql-test/suite/innodb/t/innodb_bug53756.test b/mysql-test/suite/innodb/t/innodb_bug53756.test new file mode 100644 index 00000000000..67a95371821 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug53756.test @@ -0,0 +1,184 @@ +# This is the test case for bug #53756. Alter table operation could +# leave a deleted record for the temp table (later renamed to the altered +# table) in the SYS_TABLES secondary index, we should ignore this row and +# find the first non-deleted row for the specified table_id when load table +# metadata in the function dict_load_table_on_id() during crash recovery. + +# +# innobackup needs to connect to the server. Not supported in embedded. +--source include/not_embedded.inc +# +# This test case needs to crash the server. Needs a debug server. +--source include/have_debug.inc +# +# Don't test this under valgrind, memory leaks will occur. +--source include/not_valgrind.inc +# +# This test case needs InnoDB. +-- source include/have_innodb.inc + +# +# Precautionary clean up. +# +--disable_warnings +DROP TABLE IF EXISTS bug_53756 ; +--enable_warnings + +# +# Create test data. +# +CREATE TABLE bug_53756 (pk INT, c1 INT) ENGINE=InnoDB; +ALTER TABLE bug_53756 ADD PRIMARY KEY (pk); +INSERT INTO bug_53756 VALUES(1, 11), (2, 22), (3, 33), (4, 44); + +--echo +--echo # Select a less restrictive isolation level. +# Don't use user variables. They won't survive server crash. +--let $global_isolation= `SELECT @@global.tx_isolation` +--let $session_isolation= `SELECT @@session.tx_isolation` +SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +COMMIT; + +--echo +--echo # Start a transaction in the default connection for isolation. +START TRANSACTION; +SELECT @@tx_isolation; +SELECT * FROM bug_53756; + +--echo +--echo # connection con1 deletes row 1 +--connect (con1,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +DELETE FROM bug_53756 WHERE pk=1; + +--echo +--echo # connection con2 deletes row 2 +--connect (con2,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +DELETE FROM bug_53756 WHERE pk=2; + +--echo +--echo # connection con3 updates row 3 +--connect (con3,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +UPDATE bug_53756 SET c1=77 WHERE pk=3; + +--echo +--echo # connection con4 updates row 4 +--connect (con4,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +UPDATE bug_53756 SET c1=88 WHERE pk=4; + +--echo +--echo # connection con5 inserts row 5 +--connect (con5,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +INSERT INTO bug_53756 VALUES(5, 55); + +--echo +--echo # connection con6 inserts row 6 +--connect (con6,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +INSERT INTO bug_53756 VALUES(6, 66); + +--echo +--echo # connection con1 commits. +--connection con1 +COMMIT; + +--echo +--echo # connection con3 commits. +--connection con3 +COMMIT; + +--echo +--echo # connection con4 rolls back. +--connection con4 +ROLLBACK; + +--echo +--echo # connection con6 rolls back. +--connection con6 +ROLLBACK; + +--echo +--echo # The connections 2 and 5 stay open. + +--echo +--echo # connection default selects resulting data. +--echo # Delete of row 1 was committed. +--echo # Update of row 3 was committed. +--echo # Due to isolation level read committed, these should be included. +--echo # All other changes should not be included. +--connection default +SELECT * FROM bug_53756; + +--echo +--echo # connection default +--connection default +--echo # +--echo # Crash server. +# +# Write file to make mysql-test-run.pl expect the "crash", but don't start +# it until it's told to +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +# +START TRANSACTION; +INSERT INTO bug_53756 VALUES (666,666); +# +# Request a crash on next execution of commit. +SET SESSION debug="+d,crash_commit_before"; +# +# Execute the statement that causes the crash. +--error 2013 +COMMIT; +--echo +--echo # +--echo # disconnect con1, con2, con3, con4, con5, con6. +--disconnect con1 +--disconnect con2 +--disconnect con3 +--disconnect con4 +--disconnect con5 +--disconnect con6 +--echo # +--echo # Restart server. +# +# Write file to make mysql-test-run.pl start up the server again +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +# +# Turn on reconnect +--enable_reconnect +# +# Call script that will poll the server waiting for it to be back online again +--source include/wait_until_connected_again.inc +# +# Turn off reconnect again +--disable_reconnect +--echo + +--echo # +--echo # Select recovered data. +--echo # Delete of row 1 was committed. +--echo # Update of row 3 was committed. +--echo # These should be included. +--echo # All other changes should not be included. +--echo # Delete of row 2 and insert of row 5 should be rolled back +SELECT * FROM bug_53756; + +--echo +--echo # Clean up. +DROP TABLE bug_53756; + +--disable_query_log +eval SET GLOBAL tx_isolation= '$global_isolation'; +eval SET SESSION tx_isolation= '$session_isolation'; +--enable_query_log + diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index ca22132a360..14490980bb6 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -1828,6 +1828,8 @@ dict_load_table_on_id( ut_ad(mutex_own(&(dict_sys->mutex))); + table = NULL; + /* NOTE that the operation of this function is protected by the dictionary mutex, and therefore no deadlocks can occur with other dictionary operations. */ @@ -1854,15 +1856,17 @@ dict_load_table_on_id( BTR_SEARCH_LEAF, &pcur, &mtr); rec = btr_pcur_get_rec(&pcur); - if (!btr_pcur_is_on_user_rec(&pcur) - || rec_get_deleted_flag(rec, 0)) { + if (!btr_pcur_is_on_user_rec(&pcur)) { /* Not found */ + goto func_exit; + } - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(NULL); + /* Find the first record that is not delete marked */ + while (rec_get_deleted_flag(rec, 0)) { + if (!btr_pcur_move_to_next_user_rec(&pcur, &mtr)) { + goto func_exit; + } + rec = btr_pcur_get_rec(&pcur); } /*---------------------------------------------------*/ @@ -1875,12 +1879,7 @@ dict_load_table_on_id( /* Check if the table id in record is the one searched for */ if (table_id != mach_read_from_8(field)) { - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(NULL); + goto func_exit; } /* Now we get the table name from the record */ @@ -1888,7 +1887,7 @@ dict_load_table_on_id( /* Load the table definition to memory */ table = dict_load_table(mem_heap_strdupl(heap, (char*) field, len), TRUE, DICT_ERR_IGNORE_NONE); - +func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); From d1aba4edc03bb258e7667f575823b043e16bc5fb Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 10 Feb 2011 11:50:53 +0100 Subject: [PATCH 14/64] bug#59085 recommit --- sql/sql_load.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sql/sql_load.cc b/sql/sql_load.cc index e5e539b766a..a2d443fbbc8 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -402,10 +402,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, // if we are not in slave thread, the file must be: if (!thd->slave_thread && - !((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others - (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink - ((stat_info.st_mode & S_IFREG) == S_IFREG || - (stat_info.st_mode & S_IFIFO) == S_IFIFO))) + !((stat_info.st_mode & S_IFLNK) != S_IFLNK && // symlink + ((stat_info.st_mode & S_IFREG) == S_IFREG || // regular file + (stat_info.st_mode & S_IFIFO) == S_IFIFO))) // named pipe { my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name); DBUG_RETURN(TRUE); From 2ae1cdf06516b84b2b0c13cda80afb04ce57f96f Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 13:21:22 +0200 Subject: [PATCH 15/64] Increment InnoDB Plugin version from 1.0.15 to 1.0.16. InnoDB Plugin 1.0.15 has been released with MySQL 5.1.55. --- storage/innodb_plugin/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index 6dd38df3782..22ed765e680 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -46,7 +46,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 0 -#define INNODB_VERSION_BUGFIX 15 +#define INNODB_VERSION_BUGFIX 16 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From 8a20a73a7c5fe9cbea53f869192136ebcb294928 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 13:25:12 +0200 Subject: [PATCH 16/64] Increment InnoDB version from 1.1.5 to 1.1.6 InnoDB 1.1.5 was released with MySQL 5.5.9 --- storage/innobase/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index f18673552b6..d82249fd6a8 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -51,7 +51,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 1 -#define INNODB_VERSION_BUGFIX 5 +#define INNODB_VERSION_BUGFIX 6 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From 2095b63b847144dfdb16b5faf5cf322b234a9668 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 13:28:58 +0200 Subject: [PATCH 17/64] Extend the valgrind suppression to cover one more code path: ==3307== Invalid read of size 1 ==3307== at 0xA8389A: mach_read_from_4 (mach0data.ic:182) ==3307== by 0xA833E3: buf_buddy_relocate (buf0buddy.c:450) ==3307== by 0xA8451E: buf_buddy_free_low (buf0buddy.c:619) ==3307== by 0x999326: buf_buddy_free (buf0buddy.ic:131) ==3307== by 0x998B66: buf_page_get_gen (buf0buf.c:2968) ==3307== by 0x9765D4: btr_cur_open_at_rnd_pos_func (btr0cur.c:985) ==3307== by 0x97F5EB: btr_estimate_number_of_different_key_vals (btr0cur.c:3621) ==3307== by 0x9C8694: dict_update_statistics (dict0dict.c:4312) ==3307== by 0x8FCF85: row_update_statistics_if_needed (row0mysql.c:933) ==3307== by 0x8FCEF8: row_insert_for_mysql (row0mysql.c:1240) ==3307== by 0x8E8134: ha_innobase::write_row(unsigned char*) (ha_innodb.cc:4988) ==3307== by 0x73CE6D: handler::ha_write_row(unsigned char*) (handler.cc:4776) ==3307== by 0x6512DC: copy_data_between_tables(TABLE*, TABLE*, List&, bool, unsigned int, st_order*, unsigned long long*, unsigned long long*, enum_enable_or_disable, bool) (sql_table.cc:7017) ==3307== by 0x65E1E1: mysql_alter_table(THD*, char*, char*, st_ha_create_information*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) (sql_table.cc:6416) ==3307== by 0x893920: Alter_table_statement::execute(THD*) (sql_alter.cc:106) ==3307== by 0x5DDB3B: mysql_execute_command(THD*) (sql_parse.cc:4321) --- mysql-test/valgrind.supp | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 6bb1308320f..923d0cf5351 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -876,5 +876,4 @@ fun:buf_buddy_relocate fun:buf_buddy_free_low fun:buf_buddy_free - fun:buf_LRU_block_remove_hashed_page } From adef5550eb216607f6be171e761200366c1e65eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 10 Feb 2011 14:07:58 +0200 Subject: [PATCH 18/64] Bug #59877 Wrong buffer pool selected in buf_read_ibuf_merge_pages() --- storage/innobase/buf/buf0rea.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/buf/buf0rea.c b/storage/innobase/buf/buf0rea.c index 82b45172988..17704a4b65d 100644 --- a/storage/innobase/buf/buf0rea.c +++ b/storage/innobase/buf/buf0rea.c @@ -530,7 +530,7 @@ buf_read_ibuf_merge_pages( buf_pool_t* buf_pool; ulint zip_size = fil_space_get_zip_size(space_ids[i]); - buf_pool = buf_pool_get(space_ids[i], space_versions[i]); + buf_pool = buf_pool_get(space_ids[i], page_nos[i]); while (buf_pool->n_pend_reads > buf_pool->curr_size / BUF_READ_AHEAD_PEND_LIMIT) { From a726555523a0ce00d250224b726a9332f593c0a7 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 10 Feb 2011 16:38:18 +0300 Subject: [PATCH 19/64] Bug#31384 DATE_ADD() and DATE_SUB() return binary data An after-fix to workaround different metadata in "mtr --ps" output. --- mysql-test/include/ctype_numconv.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/include/ctype_numconv.inc b/mysql-test/include/ctype_numconv.inc index 974951b6b6f..571b8207107 100644 --- a/mysql-test/include/ctype_numconv.inc +++ b/mysql-test/include/ctype_numconv.inc @@ -1750,12 +1750,15 @@ SELECT SHOW CREATE TABLE t1; DROP TABLE t1; --enable_metadata +# PS protocol gives different "Max length" value for DATETIME. +--disable_ps_protocol SELECT DATE_SUB('2007-08-03', INTERVAL 1 DAY) AS field_str1, DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; --disable_metadata +--enable_ps_protocol SELECT HEX(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, From 07116bbf8b9c56a7fbf307376d5455e19b788005 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 17:16:32 +0200 Subject: [PATCH 20/64] Fix Bug#59307 Valgrind: uninitialized value in rw_lock_set_writer_id_and_recursion_flag() by silencing a bogus Valgrind warning: ==4392== Conditional jump or move depends on uninitialised value(s) ==4392== at 0x5A18416: rw_lock_set_writer_id_and_recursion_flag (sync0rw.ic:283) ==4392== by 0x5A1865C: rw_lock_x_lock_low (sync0rw.c:558) ==4392== by 0x5A18481: rw_lock_x_lock_func (sync0rw.c:617) ==4392== by 0x597EEE6: mtr_x_lock_func (mtr0mtr.ic:271) ==4392== by 0x597EBBD: fsp_header_init (fsp0fsp.c:970) ==4392== by 0x5A15E78: innobase_start_or_create_for_mysql (srv0start.c:1508) ==4392== by 0x598B789: innobase_init(void*) (ha_innodb.cc:2282) os_compare_and_swap_thread_id() is defined as __sync_bool_compare_and_swap(). From the GCC doc: `bool __sync_bool_compare_and_swap (TYPE *ptr, TYPE oldval TYPE newval, ...)' ... The "bool" version returns true if the comparison is successful and NEWVAL was written. So it is not possible that the return value is uninitialized, no matter what the arguments to os_compare_and_swap_thread_id() are. Probably Valgrind gets confused by the implementation of the GCC internal function __sync_bool_compare_and_swap(). --- storage/innodb_plugin/include/sync0rw.ic | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/innodb_plugin/include/sync0rw.ic b/storage/innodb_plugin/include/sync0rw.ic index 7116f1b7c9b..4110a0a7e0c 100644 --- a/storage/innodb_plugin/include/sync0rw.ic +++ b/storage/innodb_plugin/include/sync0rw.ic @@ -280,6 +280,7 @@ rw_lock_set_writer_id_and_recursion_flag( local_thread = lock->writer_thread; success = os_compare_and_swap_thread_id( &lock->writer_thread, local_thread, curr_thread); + UNIV_MEM_VALID(&success, sizeof(success)); ut_a(success); lock->recursive = recursive; From 56bca19e9a29175db9c3bf4abe76fc4f2bf546d1 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 11 Feb 2011 12:09:04 +0200 Subject: [PATCH 21/64] version bump to 5.1.57 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 6d5bc07ba9a..dc944386f22 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.1.56], [], [mysql]) +AC_INIT([MySQL Server], [5.1.57], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM From fd21e07d6bc53594ba4e50663b2afe857c4f2dfb Mon Sep 17 00:00:00 2001 From: Joerg Bruehe Date: Fri, 11 Feb 2011 15:55:25 +0100 Subject: [PATCH 22/64] Fix bug#42969 Please add a MANIFEST to each build With this change, there will be new files "INFO_SRC" and "INFO_BIN", which describe the source and the binaries. They will be contained in all packages: - in "tar.gz" and derived packages, in "docs/", - in RPMs, in "/usr/share/doc/packages/MySQL-server". "INFO_SRC" is also part of a source tarball. It gives the version as exact as possible, preferably by calling "bzr version-info" on the source tree. If that is not possible, it just contains the three level version number. "INFO_BIN" contains some info when and where the binaries were built, the options given to the compiler, and the flags controlling the included features. The tests (test "mysql" in the main suite) are extended to verify the existence of both "INFO_SRC" and "INFO_BIN", as well as some of the expected contents. --- CMakeLists.txt | 21 ++++- cmake/info_bin.cmake | 30 +++++++ cmake/info_macros.cmake.in | 132 ++++++++++++++++++++++++++++++ cmake/info_src.cmake | 31 +++++++ cmake/make_dist.cmake.in | 8 +- mysql-test/r/file_contents.result | 6 ++ mysql-test/t/file_contents.test | 56 +++++++++++++ support-files/mysql.spec.sh | 8 +- 8 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 cmake/info_bin.cmake create mode 100644 cmake/info_macros.cmake.in create mode 100644 cmake/info_src.cmake create mode 100644 mysql-test/r/file_contents.result create mode 100644 mysql-test/t/file_contents.test diff --git a/CMakeLists.txt b/CMakeLists.txt index 059d68e0a3c..235a65a6437 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2011, 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 @@ -317,6 +317,24 @@ CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/include/mysql_version.h.in ${CMAKE_BINARY_DIR}/include/mysql_version.h ) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc.in ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc) +CONFIGURE_FILE( + ${CMAKE_SOURCE_DIR}/cmake/info_macros.cmake.in ${CMAKE_BINARY_DIR}/info_macros.cmake @ONLY) + +# Handle the "INFO_*" files. +INCLUDE(${CMAKE_BINARY_DIR}/info_macros.cmake) +# Source: This can be done during the cmake phase, all information is +# available, but should be repeated on each "make" just in case someone +# does "cmake ; make ; bzr pull ; make". +CREATE_INFO_SRC(${CMAKE_BINARY_DIR}/Docs) +ADD_CUSTOM_TARGET(INFO_SRC ALL + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/info_src.cmake + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) +# Build flags: This must be postponed to the make phase. +ADD_CUSTOM_TARGET(INFO_BIN ALL + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/info_bin.cmake + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) # Packaging IF(WIN32) @@ -344,6 +362,7 @@ IF(NOT INSTALL_LAYOUT MATCHES "RPM") OPTIONAL ) INSTALL(FILES README DESTINATION ${INSTALL_DOCREADMEDIR} COMPONENT Readme) + INSTALL(FILES ${CMAKE_BINARY_DIR}/Docs/INFO_SRC ${CMAKE_BINARY_DIR}/Docs/INFO_BIN DESTINATION ${INSTALL_DOCDIR}) IF(UNIX) INSTALL(FILES Docs/INSTALL-BINARY DESTINATION ${INSTALL_DOCREADMEDIR} COMPONENT Readme) ENDIF() diff --git a/cmake/info_bin.cmake b/cmake/info_bin.cmake new file mode 100644 index 00000000000..9cec3de0e0f --- /dev/null +++ b/cmake/info_bin.cmake @@ -0,0 +1,30 @@ +# Copyright (c) 2011, 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 Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +# The sole purpose of this cmake control file is to create the "INFO_BIN" file. + +# By having a separate cmake file for this, it is ensured this happens +# only in the build (Unix: "make") phase, not when cmake runs. +# This, in turn, avoids creating stuff in the source directory - +# it should get into the binary directory only. + + +# Get the macros which the "INFO_*" files. +INCLUDE(${CMAKE_BINARY_DIR}/info_macros.cmake) + +# Here is where the action is. +CREATE_INFO_BIN() + diff --git a/cmake/info_macros.cmake.in b/cmake/info_macros.cmake.in new file mode 100644 index 00000000000..9e08cffb2bf --- /dev/null +++ b/cmake/info_macros.cmake.in @@ -0,0 +1,132 @@ +# Copyright (c) 2011, 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 Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +# Handle/create the "INFO_*" files describing a MySQL (server) binary. +# This is part of the fix for bug#42969. + + +# Several of cmake's variables need to be translated from '@' notation +# to '${}', this is done by the "configure" call in top level "CMakeLists.txt". +# If further variables are used in this file, add them to this list. + +SET(VERSION "@VERSION@") +SET(CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@") +SET(CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@") +SET(CMAKE_GENERATOR "@CMAKE_GENERATOR@") +SET(CMAKE_SIZEOF_VOID_P "@CMAKE_SIZEOF_VOID_P@") +SET(BZR_EXECUTABLE "@BZR_EXECUTABLE@") +SET(CMAKE_CROSSCOMPILING "@CMAKE_CROSSCOMPILING@") +SET(CMAKE_HOST_SYSTEM "@CMAKE_HOST_SYSTEM@") +SET(CMAKE_HOST_SYSTEM_PROCESSOR "@CMAKE_HOST_SYSTEM_PROCESSOR@") +SET(CMAKE_SYSTEM "@CMAKE_SYSTEM@") +SET(CMAKE_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@") + + +# Create an "INFO_SRC" file with information about the source (only). +# We use "bzr version-info", if possible, and the "VERSION" contents. +# +# Outside development (BZR tree), the "INFO_SRC" file will not be modified +# provided it exists (from "make dist" or a source tarball creation). + +MACRO(CREATE_INFO_SRC target_dir) + SET(INFO_SRC "${target_dir}/INFO_SRC") + + IF(EXISTS ${CMAKE_SOURCE_DIR}/.bzr) + # Sources are in a BZR repository: Always update. + EXECUTE_PROCESS( + COMMAND ${BZR_EXECUTABLE} version-info ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE VERSION_INFO + RESULT_VARIABLE RESULT + ) + FILE(WRITE ${INFO_SRC} "${VERSION_INFO}\n") + # to debug, add: FILE(APPEND ${INFO_SRC} "\nResult ${RESULT}\n") + # For better readability ... + FILE(APPEND ${INFO_SRC} "\nMySQL source ${VERSION}\n") + ELSEIF(EXISTS ${INFO_SRC}) + # Outside a BZR tree, there is no need to change an existing "INFO_SRC", + # it cannot be improved. + ELSEIF(EXISTS ${CMAKE_SOURCE_DIR}/Docs/INFO_SRC) + # If we are building from a source distribution, it also contains "INFO_SRC". + # Similar, the export used for a release build already has the file. + FILE(READ ${CMAKE_SOURCE_DIR}/Docs/INFO_SRC SOURCE_INFO) + FILE(WRITE ${INFO_SRC} "${SOURCE_INFO}\n") + ELSEIF(EXISTS ${CMAKE_SOURCE_DIR}/INFO_SRC) + # This is not the proper location, but who knows ... + FILE(READ ${CMAKE_SOURCE_DIR}/INFO_SRC SOURCE_INFO) + FILE(WRITE ${INFO_SRC} "${SOURCE_INFO}\n") + ELSE() + # This is a fall-back. + FILE(WRITE ${INFO_SRC} "\nMySQL source ${VERSION}\n") + ENDIF() +ENDMACRO(CREATE_INFO_SRC) + + +# This is for the "real" build, must be run again with each cmake run +# to make sure we report the current flags (not those of some previous run). + +MACRO(CREATE_INFO_BIN) + SET(INFO_BIN "Docs/INFO_BIN") + + FILE(WRITE ${INFO_BIN} "===== Information about the build process: =====\n") + IF (WIN32) + EXECUTE_PROCESS(COMMAND cmd /c date /T OUTPUT_VARIABLE TMP_DATE) + ELSEIF(UNIX) + EXECUTE_PROCESS(COMMAND date "+%Y-%m-%d %H:%M:%S" OUTPUT_VARIABLE TMP_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) + ELSE() + SET(TMP_DATE "(no date command known for this platform)") + ENDIF() + SITE_NAME(HOSTNAME) + FILE(APPEND ${INFO_BIN} "Build was run at ${TMP_DATE} on host '${HOSTNAME}'\n\n") + + # According to the cmake docs, these variables should always be set. + # However, they are empty in my tests, using cmake 2.6.4 on Linux, various Unix, and Windows. + # Still, include this code, so we will profit if a build environment does provide that info. + IF(CMAKE_HOST_SYSTEM) + FILE(APPEND ${INFO_BIN} "Build was done on ${CMAKE_HOST_SYSTEM} using ${CMAKE_HOST_SYSTEM_PROCESSOR}\n") + ENDIF() + IF(CMAKE_CROSSCOMPILING) + FILE(APPEND ${INFO_BIN} "Build was done for ${CMAKE_SYSTEM} using ${CMAKE_SYSTEM_PROCESSOR}\n") + ENDIF() + + # ${CMAKE_VERSION} doesn't work in 2.6.0, use the separate components. + FILE(APPEND ${INFO_BIN} "Build was done using cmake ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} \n\n") + + IF (WIN32) + FILE(APPEND ${INFO_BIN} "===== Compiler / generator used: =====\n") + FILE(APPEND ${INFO_BIN} ${CMAKE_GENERATOR} "\n\n") + ELSEIF(UNIX) + FILE(APPEND ${INFO_BIN} "===== Compiler flags used (from the 'sql/' subdirectory): =====\n") + IF(EXISTS sql/CMakeFiles/sql.dir/flags.make) + EXECUTE_PROCESS(COMMAND egrep "^# compile|^C_|^CXX_" sql/CMakeFiles/sql.dir/flags.make OUTPUT_VARIABLE COMPILE_FLAGS) + FILE(APPEND ${INFO_BIN} ${COMPILE_FLAGS} "\n") + ELSE() + FILE(APPEND ${INFO_BIN} "File 'sql/CMakeFiles/sql.dir/flags.make' is not yet found.\n\n") + ENDIF() + ENDIF() + FILE(APPEND ${INFO_BIN} "Pointer size: ${CMAKE_SIZEOF_VOID_P}\n\n") + + FILE(APPEND ${INFO_BIN} "===== Feature flags used: =====\n") + IF(EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt) + # Attention: "-N" prevents cmake from entering a recursion, and it must be a separate flag from "-L". + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -N -L ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE FEATURE_FLAGS) + FILE(APPEND ${INFO_BIN} ${FEATURE_FLAGS} "\n") + ELSE() + FILE(APPEND ${INFO_BIN} "File 'CMakeCache.txt' is not yet found.\n\n") + ENDIF() + + FILE(APPEND ${INFO_BIN} "===== EOF =====\n") +ENDMACRO(CREATE_INFO_BIN) + diff --git a/cmake/info_src.cmake b/cmake/info_src.cmake new file mode 100644 index 00000000000..97776b70901 --- /dev/null +++ b/cmake/info_src.cmake @@ -0,0 +1,31 @@ +# Copyright (c) 2011, 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 Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +# The sole purpose of this cmake control file is to create the "INFO_SRC" file. + +# As long as and "bzr pull" (or "bzr commit") is followed by a "cmake", +# the call in top level "CMakeLists.txt" is sufficient. +# This file is to provide a separate target for the "make" phase, +# to ensure the BZR revision-id is correct even after a sequence +# cmake ; make ; bzr pull ; make + + +# Get the macros which handle the "INFO_*" files. +INCLUDE(${CMAKE_BINARY_DIR}/info_macros.cmake) + +# Here is where the action is. +CREATE_INFO_SRC(${CMAKE_BINARY_DIR}/Docs) + diff --git a/cmake/make_dist.cmake.in b/cmake/make_dist.cmake.in index 95412370c28..7c4778112df 100644 --- a/cmake/make_dist.cmake.in +++ b/cmake/make_dist.cmake.in @@ -1,4 +1,4 @@ -# Copyright (C) 2009 Sun Microsystems, Inc +# Copyright (c) 2009, 2011, 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 @@ -106,6 +106,12 @@ IF(MYSQL_DOCS_LOCATION) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_directory "${MYSQL_DOCS_LOCATION}" "${PACKAGE_DIR}") ENDIF() +# Ensure there is an "INFO_SRC" file. +INCLUDE(${CMAKE_BINARY_DIR}/info_macros.cmake) +IF(NOT EXISTS ${PACKAGE_DIR}/Docs/INFO_SRC) + CREATE_INFO_SRC(${PACKAGE_DIR}/Docs) +ENDIF() + # In case we used CPack, it could have copied some # extra files that are not usable on different machines. FILE(REMOVE ${PACKAGE_DIR}/CMakeCache.txt) diff --git a/mysql-test/r/file_contents.result b/mysql-test/r/file_contents.result new file mode 100644 index 00000000000..110769e12f8 --- /dev/null +++ b/mysql-test/r/file_contents.result @@ -0,0 +1,6 @@ + +Checking 'INFO_SRC' and 'INFO_BIN' +INFO_SRC: Found MySQL version number / Found BZR revision id +INFO_BIN: Found 'Compiler ... used' line / Found 'Feature flags' line + +End of tests diff --git a/mysql-test/t/file_contents.test b/mysql-test/t/file_contents.test new file mode 100644 index 00000000000..61d684436f2 --- /dev/null +++ b/mysql-test/t/file_contents.test @@ -0,0 +1,56 @@ +# +# Testing files that were built to be packaged, both for existence and for contents +# + +# +# Bug #42969: Create MANIFEST files +# +# Use a Perl script to verify that files "docs/INFO_BIN" and "docs/INFO_SRC" do exist +# and have the expected contents. + +--perl +print "\nChecking 'INFO_SRC' and 'INFO_BIN'\n"; +$dir_docs = $ENV{'MYSQL_LIBDIR'}; +if($dir_docs =~ m|/usr/|) { + # RPM package + $dir_docs =~ s|/lib|/share/doc|; + if(-d "$dir_docs/packages/MySQL-server") { + # SuSE + $dir_docs = "$dir_docs/packages/MySQL-server"; + } else { + # RedHat: version number in directory name + $dir_docs = glob "$dir_docs/MySQL-server*"; + } +} else { + # tar.gz package, Windows, or developer work (in BZR) + $dir_docs =~ s|/lib||; + if(-d "$dir_docs/docs") { + $dir_docs = "$dir_docs/docs"; # package + } else { + $dir_docs = "$dir_docs/Docs"; # development tree + } +} +$found_version = "No line 'MySQL source #.#.#'"; +$found_revision = "No line 'revision-id: .....'"; +open(I_SRC,"<","$dir_docs/INFO_SRC") or print "Cannot open 'INFO_SRC' in '$dir_docs'\n"; +while(defined ($line = )) { + if ($line =~ m|^MySQL source \d\.\d\.\d+|) {$found_version = "Found MySQL version number";} + if ($line =~ m|^revision-id: .*@.*-2\d{13}-\w+$|) {$found_revision = "Found BZR revision id";} +} +close I_SRC; +print "INFO_SRC: $found_version / $found_revision\n"; +$found_compiler = "No line about compiler information"; +$found_features = "No line 'Feature flags'"; +open(I_BIN,"<","$dir_docs/INFO_BIN") or print "Cannot open 'INFO_BIN' in '$dir_docs'\n"; +while(defined ($line = )) { + # "generator" on Windows, "flags" on Unix: + if (($line =~ m| Compiler / generator used: |) || + ($line =~ m| Compiler flags used |)) {$found_compiler = "Found 'Compiler ... used' line";} + if ($line =~ m| Feature flags used:|) {$found_features = "Found 'Feature flags' line";} +} +close I_BIN; +print "INFO_BIN: $found_compiler / $found_features\n"; +EOF + +--echo +--echo End of tests diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index aa861609a5f..2177e4aa0ec 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2011, 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 @@ -908,6 +908,8 @@ echo "=====" >> $STATUS_HISTORY %doc %{license_files_server} %endif %doc %{src_dir}/Docs/ChangeLog +%doc %{src_dir}/Docs/INFO_SRC* +%doc release/Docs/INFO_BIN* %doc release/support-files/my-*.cnf %doc %attr(644, root, root) %{_infodir}/mysql.info* @@ -1085,6 +1087,10 @@ echo "=====" >> $STATUS_HISTORY # merging BK trees) ############################################################################## %changelog +* Mon Jan 31 2011 Joerg Bruehe + +- Install the new "manifest" files: "INFO_SRC" and "INFO_BIN". + * Tue Nov 23 2010 Jonathan Perkin - EXCEPTIONS-CLIENT has been deleted, remove it from here too From d06bb27fd10d1d729095cfae3fc41e9a29a0470c Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Mon, 14 Feb 2011 02:07:59 -0800 Subject: [PATCH 23/64] Fix Bug #59749 Enabling concurrent reads while creating non-primary unique index gives failures. Approved by Marko --- storage/innodb_plugin/ChangeLog | 6 ++++++ storage/innodb_plugin/handler/handler0alter.cc | 12 ++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 0cbdc8ed9d2..9fa00ac8e6f 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-02-14 The InnoDB Team + + * handler/handler0alter.cc: + Bug#59749 Enabling concurrent reads while creating non-primary + unique index gives failures + 2011-01-31 The InnoDB Team * btr/btr0cur.c, include/row0upd.h, diff --git a/storage/innodb_plugin/handler/handler0alter.cc b/storage/innodb_plugin/handler/handler0alter.cc index 517445f7e69..dc1317d5c5a 100644 --- a/storage/innodb_plugin/handler/handler0alter.cc +++ b/storage/innodb_plugin/handler/handler0alter.cc @@ -782,10 +782,6 @@ err_exit: ut_ad(error == DB_SUCCESS); - /* We will need to rebuild index translation table. Set - valid index entry count in the translation table to zero */ - share->idx_trans_tbl.index_count = 0; - /* Commit the data dictionary transaction in order to release the table locks on the system tables. This means that if MySQL crashes while creating a new primary key inside @@ -911,6 +907,14 @@ error: } convert_error: + if (error == DB_SUCCESS) { + /* Build index is successful. We will need to + rebuild index translation table. Reset the + index entry count in the translation table + to zero, so that translation table will be rebuilt */ + share->idx_trans_tbl.index_count = 0; + } + error = convert_error_code_to_mysql(error, innodb_table->flags, user_thd); From 6688a4f25cd43ff4e6e70a920e3f148b778ba847 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Mon, 14 Feb 2011 14:16:31 +0100 Subject: [PATCH 24/64] Bug #11766788 (former bug 59986) Assert in Diagnostics_area::set_ok_status() for XA COMMIT This assert was triggered if XA COMMIT was issued when an XA transaction already had encountered an error (e.g. a deadlock) which required the XA transaction to be rolled back. In general, the assert is triggered if a statement tries to send OK to the client when an error has already been reported. It was triggered in this case because the trans_xa_commit() function first reported an error, then rolled back the transaction and finally returned FALSE, indicating success. Since trans_xa_commit() reported success, mysql_execute_command() tried to report OK, triggering the assert. This patch fixes the problem by fixing trans_xa_commit() to return TRUE if it encounters an error that requires rollback, even if the rollback itself is successful. Test case added to xa.test. --- mysql-test/r/xa.result | 24 ++++++++++++++++++++++++ mysql-test/t/xa.test | 42 ++++++++++++++++++++++++++++++++++++++++++ sql/transaction.cc | 5 +++-- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/xa.result b/mysql-test/r/xa.result index 6ef3bf392d9..ad0d103c1e0 100644 --- a/mysql-test/r/xa.result +++ b/mysql-test/r/xa.result @@ -142,3 +142,27 @@ XA PREPARE 'x'; XA PREPARE 'x'; ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state XA ROLLBACK 'x'; +# +# Bug#59986 Assert in Diagnostics_area::set_ok_status() for XA COMMIT +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a)) engine=InnoDB; +INSERT INTO t1 VALUES (1, 1), (2, 2); +# Connection con1 +XA START 'a'; +UPDATE t1 SET b= 3 WHERE a=1; +# Connection default +XA START 'b'; +UPDATE t1 SET b=4 WHERE a=2; +# Sending: +UPDATE t1 SET b=5 WHERE a=1; +# Connection con1 +UPDATE t1 SET b=6 WHERE a=2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +XA COMMIT 'a'; +ERROR XA102: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected +# Connection default +# Reaping: UPDATE t1 SET b=5 WHERE a=1 +XA END 'b'; +XA ROLLBACK 'b'; +DROP TABLE t1; diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index 3fd243398a9..47ea4981314 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -245,6 +245,48 @@ XA PREPARE 'x'; XA ROLLBACK 'x'; +--echo # +--echo # Bug#59986 Assert in Diagnostics_area::set_ok_status() for XA COMMIT +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a)) engine=InnoDB; +INSERT INTO t1 VALUES (1, 1), (2, 2); + +--echo # Connection con1 +connect (con1, localhost, root); +XA START 'a'; +UPDATE t1 SET b= 3 WHERE a=1; + +--echo # Connection default +connection default; +XA START 'b'; +UPDATE t1 SET b=4 WHERE a=2; +--echo # Sending: +--send UPDATE t1 SET b=5 WHERE a=1 + +--echo # Connection con1 +connection con1; +--sleep 1 +--error ER_LOCK_DEADLOCK +UPDATE t1 SET b=6 WHERE a=2; +# This used to trigger the assert +--error ER_XA_RBDEADLOCK +XA COMMIT 'a'; + +--echo # Connection default +connection default; +--echo # Reaping: UPDATE t1 SET b=5 WHERE a=1 +--reap +XA END 'b'; +XA ROLLBACK 'b'; +DROP TABLE t1; +disconnect con1; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/transaction.cc b/sql/transaction.cc index b331fea89fe..85686810893 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2008 Sun/MySQL +/* Copyright (c) 2000, 2011, 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 @@ -635,8 +635,9 @@ bool trans_xa_commit(THD *thd) if (xa_trans_rolled_back(&thd->transaction.xid_state)) { - if ((res= test(ha_rollback_trans(thd, TRUE)))) + if (ha_rollback_trans(thd, TRUE)) my_error(ER_XAER_RMERR, MYF(0)); + res= thd->is_error(); } else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE) { From 6ae8c575f356e498f48c3ad80e8efcbc3bc90d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 15 Feb 2011 10:51:33 +0200 Subject: [PATCH 25/64] Bug#59307 Valgrind: uninitialized value in rw_lock_set_writer_id_and_recursion_flag() rw_lock_create_func(): Initialize lock->writer_thread, so that Valgrind will not complain even when Valgrind instrumentation is not enabled. Flag lock->writer_thread uninitialized, so that Valgrind can complain when it is used uninitialized. rw_lock_set_writer_id_and_recursion_flag(): Revert the bogus Valgrind instrumentation that was pushed in the first attempt to fix this bug. --- .../innodb_plugin/r/innodb_bug59307.result | 28 ++++++++++++++++ .../innodb_plugin/t/innodb_bug59307.test | 32 +++++++++++++++++++ storage/innodb_plugin/ChangeLog | 6 ++++ storage/innodb_plugin/include/sync0rw.ic | 1 - storage/innodb_plugin/sync/sync0rw.c | 3 ++ 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug59307.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug59307.test diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug59307.result b/mysql-test/suite/innodb_plugin/r/innodb_bug59307.result new file mode 100644 index 00000000000..0d726e83708 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug59307.result @@ -0,0 +1,28 @@ +CREATE TABLE t1 ( +t1_int INT, +t1_time TIME +) ENGINE=innodb; +CREATE TABLE t2 ( +t2_int int PRIMARY KEY, +t2_int2 INT +) ENGINE=INNODB; +INSERT INTO t2 VALUES (); +Warnings: +Warning 1364 Field 't2_int' doesn't have a default value +INSERT INTO t1 VALUES (); +SELECT * +FROM t1 AS t1a +WHERE NOT EXISTS +(SELECT * +FROM t1 AS t1b +WHERE t1b.t1_int NOT IN +(SELECT t2.t2_int +FROM t2 +WHERE t1b.t1_time LIKE t1b.t1_int +OR t1b.t1_time <> t2.t2_int2 +AND 6=7 +) +) +; +t1_int t1_time +DROP TABLE t1,t2; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug59307.test b/mysql-test/suite/innodb_plugin/t/innodb_bug59307.test new file mode 100644 index 00000000000..9c68adf36cf --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug59307.test @@ -0,0 +1,32 @@ +-- source include/have_innodb_plugin.inc +# Bug #59307 uninitialized value in rw_lock_set_writer_id_and_recursion_flag() +# when Valgrind instrumentation (UNIV_DEBUG_VALGRIND) is not enabled + +CREATE TABLE t1 ( + t1_int INT, + t1_time TIME +) ENGINE=innodb; + +CREATE TABLE t2 ( + t2_int int PRIMARY KEY, + t2_int2 INT +) ENGINE=INNODB; + +INSERT INTO t2 VALUES (); +INSERT INTO t1 VALUES (); + +SELECT * +FROM t1 AS t1a +WHERE NOT EXISTS + (SELECT * + FROM t1 AS t1b + WHERE t1b.t1_int NOT IN + (SELECT t2.t2_int + FROM t2 + WHERE t1b.t1_time LIKE t1b.t1_int + OR t1b.t1_time <> t2.t2_int2 + AND 6=7 + ) +) +; +DROP TABLE t1,t2; diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 9fa00ac8e6f..1b2747ab012 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-02-15 The InnoDB Team + + * sync/sync0rw.c, innodb_bug59307.test: + Bug#59307 Valgrind: uninitialized value in + rw_lock_set_writer_id_and_recursion_flag() + 2011-02-14 The InnoDB Team * handler/handler0alter.cc: diff --git a/storage/innodb_plugin/include/sync0rw.ic b/storage/innodb_plugin/include/sync0rw.ic index 4110a0a7e0c..7116f1b7c9b 100644 --- a/storage/innodb_plugin/include/sync0rw.ic +++ b/storage/innodb_plugin/include/sync0rw.ic @@ -280,7 +280,6 @@ rw_lock_set_writer_id_and_recursion_flag( local_thread = lock->writer_thread; success = os_compare_and_swap_thread_id( &lock->writer_thread, local_thread, curr_thread); - UNIV_MEM_VALID(&success, sizeof(success)); ut_a(success); lock->recursive = recursive; diff --git a/storage/innodb_plugin/sync/sync0rw.c b/storage/innodb_plugin/sync/sync0rw.c index 00e0324becd..a5da606ad80 100644 --- a/storage/innodb_plugin/sync/sync0rw.c +++ b/storage/innodb_plugin/sync/sync0rw.c @@ -260,6 +260,9 @@ rw_lock_create_func( contains garbage at initialization and cannot be used for recursive x-locking. */ lock->recursive = FALSE; + /* Silence Valgrind when UNIV_DEBUG_VALGRIND is not enabled. */ + memset((void*) &lock->writer_thread, 0, sizeof lock->writer_thread); + UNIV_MEM_INVALID(&lock->writer_thread, sizeof lock->writer_thread); #ifdef UNIV_SYNC_DEBUG UT_LIST_INIT(lock->debug_list); From 518a4440ea2cc2ddd690f6b0dca5e5a107a9f16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 15 Feb 2011 12:12:27 +0200 Subject: [PATCH 26/64] Add a test for suspected Bug#60049. --- .../suite/innodb/r/innodb_bug60049.result | 8 ++++ .../suite/innodb/t/innodb_bug60049-master.opt | 1 + .../suite/innodb/t/innodb_bug60049.test | 38 +++++++++++++++++++ .../innodb_plugin/r/innodb_bug60049.result | 8 ++++ .../t/innodb_bug60049-master.opt | 1 + .../innodb_plugin/t/innodb_bug60049.test | 38 +++++++++++++++++++ 6 files changed, 94 insertions(+) create mode 100644 mysql-test/suite/innodb/r/innodb_bug60049.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug60049-master.opt create mode 100644 mysql-test/suite/innodb/t/innodb_bug60049.test create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug60049.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug60049.test diff --git a/mysql-test/suite/innodb/r/innodb_bug60049.result b/mysql-test/suite/innodb/r/innodb_bug60049.result new file mode 100644 index 00000000000..bec0e05a897 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug60049.result @@ -0,0 +1,8 @@ +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +@@innodb_fast_shutdown +0 +Last record of ID_IND root page (9): +1808000018050074000000000000000c5359535f464f524549474e5f434f4c53 diff --git a/mysql-test/suite/innodb/t/innodb_bug60049-master.opt b/mysql-test/suite/innodb/t/innodb_bug60049-master.opt new file mode 100644 index 00000000000..22a5d4ed221 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug60049-master.opt @@ -0,0 +1 @@ +--innodb_fast_shutdown=0 diff --git a/mysql-test/suite/innodb/t/innodb_bug60049.test b/mysql-test/suite/innodb/t/innodb_bug60049.test new file mode 100644 index 00000000000..1e0feaf0c89 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug60049.test @@ -0,0 +1,38 @@ +# Bug #60049 Verify that purge leaves no garbage in unique secondary indexes +# This test requires a fresh server start-up and a slow shutdown. +# This was a suspected bug (not a bug). + +-- source include/have_innodb.inc + +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +let $MYSQLD_DATADIR=`select @@datadir`; + +# Shut down the server +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 10 +-- source include/wait_until_disconnected.inc + +# Check the tail of ID_IND (SYS_TABLES.ID) +let IBDATA1=$MYSQLD_DATADIR/ibdata1; +perl; +my $file = $ENV{'IBDATA1'}; +open(FILE, "<$file") || die "Unable to open $file"; +# Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). +seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; +die unless read(FILE, $_, 4) == 4; +my $sys_tables_id_root = unpack "N"; +print "Last record of ID_IND root page ($sys_tables_id_root):\n"; +# This should be the last record in ID_IND. Dump it in hexadecimal. +seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; +read(FILE, $_, 32) || die "Unable to read $file"; +close(FILE); +print unpack("H*"),"\n"; +EOF + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug60049.result b/mysql-test/suite/innodb_plugin/r/innodb_bug60049.result new file mode 100644 index 00000000000..bec0e05a897 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug60049.result @@ -0,0 +1,8 @@ +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +@@innodb_fast_shutdown +0 +Last record of ID_IND root page (9): +1808000018050074000000000000000c5359535f464f524549474e5f434f4c53 diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt b/mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt new file mode 100644 index 00000000000..22a5d4ed221 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt @@ -0,0 +1 @@ +--innodb_fast_shutdown=0 diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test new file mode 100644 index 00000000000..0c093ac94b1 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test @@ -0,0 +1,38 @@ +# Bug #60049 Verify that purge leaves no garbage in unique secondary indexes +# This test requires a fresh server start-up and a slow shutdown. +# This was a suspected bug (not a bug). + +-- source include/have_innodb_plugin.inc + +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +let $MYSQLD_DATADIR=`select @@datadir`; + +# Shut down the server +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 10 +-- source include/wait_until_disconnected.inc + +# Check the tail of ID_IND (SYS_TABLES.ID) +let IBDATA1=$MYSQLD_DATADIR/ibdata1; +perl; +my $file = $ENV{'IBDATA1'}; +open(FILE, "<$file") || die "Unable to open $file"; +# Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). +seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; +die unless read(FILE, $_, 4) == 4; +my $sys_tables_id_root = unpack "N"; +print "Last record of ID_IND root page ($sys_tables_id_root):\n"; +# This should be the last record in ID_IND. Dump it in hexadecimal. +seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; +read(FILE, $_, 32) || die "Unable to read $file"; +close(FILE); +print unpack("H*"),"\n"; +EOF + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc From 7e1cd4ae0d57cd4335167482b16e9f6eb871b1bf Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Tue, 15 Feb 2011 14:03:05 +0300 Subject: [PATCH 27/64] Fix for bug#11766714 (former bug @59888) "debug assertion when attempt to create spatial index on char > 31 bytes". Attempt to create spatial index on char field with length greater than 31 byte led to assertion failure on server compiled with safemutex support. The problem occurred in mi_create() function which was called to create a new version of table being altered. This function failed since it detected an attempt to create a spatial key on non-binary column and tried to return an error. On its error path it tried to unlock THR_LOCK_myisam mutex which has not been not locked at this point. Indeed such an incorrect behavior was caught by safemutex wrapper and caused assertion failure. This patch fixes the problem by ensuring that mi_create() doesn't releases THR_LOCK_myisam mutex on error path if it was not acquired. --- mysql-test/r/gis.result | 8 ++++++++ mysql-test/t/gis.test | 12 ++++++++++++ storage/myisam/mi_create.c | 16 +++++++++------- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index a9beb9631ae..d82a86a6423 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -1034,4 +1034,12 @@ p NULL NULL drop table t1; +# +# Test for bug #59888 "debug assertion when attempt to create spatial index +# on char > 31 bytes". +# +create table t1(a char(32) not null) engine=myisam; +create spatial index i on t1 (a); +ERROR HY000: Can't create table '#sql-temporary' (errno: 140) +drop table t1; End of 5.1 tests diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test index bdbbfc7c064..94cec60944a 100644 --- a/mysql-test/t/gis.test +++ b/mysql-test/t/gis.test @@ -754,4 +754,16 @@ insert into t1 values (geomfromtext("point(1 0)")); select * from (select polygon(t1.a) as p from t1 order by t1.a) d; drop table t1; + +--echo # +--echo # Test for bug #59888 "debug assertion when attempt to create spatial index +--echo # on char > 31 bytes". +--echo # +create table t1(a char(32) not null) engine=myisam; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +create spatial index i on t1 (a); +drop table t1; + + --echo End of 5.1 tests diff --git a/storage/myisam/mi_create.c b/storage/myisam/mi_create.c index 42bd8e26a94..8c83996cadf 100644 --- a/storage/myisam/mi_create.c +++ b/storage/myisam/mi_create.c @@ -272,7 +272,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, keyseg->type != HA_KEYTYPE_VARBINARY2) { my_errno=HA_WRONG_CREATE_OPTION; - goto err; + goto err_no_lock; } } keydef->keysegs+=sp_segs; @@ -281,7 +281,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, min_key_length_skip+=SPLEN*2*SPDIMS; #else my_errno= HA_ERR_UNSUPPORTED; - goto err; + goto err_no_lock; #endif /*HAVE_SPATIAL*/ } else if (keydef->flag & HA_FULLTEXT) @@ -297,7 +297,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, keyseg->type != HA_KEYTYPE_VARTEXT2) { my_errno=HA_WRONG_CREATE_OPTION; - goto err; + goto err_no_lock; } if (!(keyseg->flag & HA_BLOB_PART) && (keyseg->type == HA_KEYTYPE_VARTEXT1 || @@ -422,7 +422,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, if (keydef->keysegs > MI_MAX_KEY_SEG) { my_errno=HA_WRONG_CREATE_OPTION; - goto err; + goto err_no_lock; } /* key_segs may be 0 in the case when we only want to be able to @@ -447,7 +447,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, length >= MI_MAX_KEY_BUFF) { my_errno=HA_WRONG_CREATE_OPTION; - goto err; + goto err_no_lock; } set_if_bigger(max_key_block_length,keydef->block_length); keydef->keylength= (uint16) key_length; @@ -494,7 +494,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, "indexes and/or unique constraints.", MYF(0), name + dirname_length(name)); my_errno= HA_WRONG_CREATE_OPTION; - goto err; + goto err_no_lock; } bmove(share.state.header.file_version,(uchar*) myisam_file_magic,4); @@ -827,12 +827,14 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, errpos=0; pthread_mutex_unlock(&THR_LOCK_myisam); if (my_close(file,MYF(0))) - goto err; + goto err_no_lock; my_free((char*) rec_per_key_part,MYF(0)); DBUG_RETURN(0); err: pthread_mutex_unlock(&THR_LOCK_myisam); + +err_no_lock: save_errno=my_errno; switch (errpos) { case 3: From 1c4ddcf0aa120d4d1f0ef78ba8df15e39808ba54 Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Tue, 15 Feb 2011 11:59:12 +0000 Subject: [PATCH 28/64] BUG#11754075: BUG#45621: A FEW TEST FILES ARE DISABLED DUE TO WL#4284 Backporting the patch from BUG#11753489 into mysql-5.5 as it is already in mysql-trunk but not in mysql-5.5. --- .../extra/rpl_tests/rpl_failed_optimize.test | 14 ++++++++------ mysql-test/suite/rpl/r/rpl_failed_optimize.result | 7 ------- mysql-test/suite/rpl/t/disabled.def | 1 - .../suite/rpl/t/rpl_failed_optimize-master.opt | 1 - 4 files changed, 8 insertions(+), 15 deletions(-) delete mode 100644 mysql-test/suite/rpl/t/rpl_failed_optimize-master.opt diff --git a/mysql-test/extra/rpl_tests/rpl_failed_optimize.test b/mysql-test/extra/rpl_tests/rpl_failed_optimize.test index 6817405b2d9..af048aeb92a 100644 --- a/mysql-test/extra/rpl_tests/rpl_failed_optimize.test +++ b/mysql-test/extra/rpl_tests/rpl_failed_optimize.test @@ -2,22 +2,24 @@ # # BUG#5551 "Failed OPTIMIZE TABLE is logged to binary log" -# Replication should work when OPTIMIZE TABLE timeouts, and -# when OPTIMIZE TABLE is executed on a non-existing table +# Replication should work when when OPTIMIZE TABLE is +# executed on a non-existing table. # +# Due to patch for BUG#989, checking that an OPTIMIZE +# that fails due to a lock wait timeout on an InnoDB table +# is not valid anymore, as an mdl lock is hit before, +# thence no timeout occurs, but instead a deadlock. +# + eval CREATE TABLE t1 ( a int ) ENGINE=$engine_type; BEGIN; INSERT INTO t1 VALUES (1); connection master1; -OPTIMIZE TABLE t1; - OPTIMIZE TABLE non_existing; sync_slave_with_master; -# End of 4.1 tests - connection master; select * from t1; commit; diff --git a/mysql-test/suite/rpl/r/rpl_failed_optimize.result b/mysql-test/suite/rpl/r/rpl_failed_optimize.result index b0c331fa557..0d46f78c150 100644 --- a/mysql-test/suite/rpl/r/rpl_failed_optimize.result +++ b/mysql-test/suite/rpl/r/rpl_failed_optimize.result @@ -3,13 +3,6 @@ include/master-slave.inc CREATE TABLE t1 ( a int ) ENGINE=InnoDB; BEGIN; INSERT INTO t1 VALUES (1); -OPTIMIZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 optimize note Table does not support optimize, doing recreate + analyze instead -test.t1 optimize error Lock wait timeout exceeded; try restarting transaction -test.t1 optimize status Operation failed -Warnings: -Error 1205 Lock wait timeout exceeded; try restarting transaction OPTIMIZE TABLE non_existing; Table Op Msg_type Msg_text test.non_existing optimize Error Table 'test.non_existing' doesn't exist diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index 1aa596ec783..deb32e7d504 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -10,7 +10,6 @@ # ############################################################################## -rpl_failed_optimize : WL#4284: Can't optimize table used by a pending transaction (there is metadata lock on the table). rpl_read_only : WL#4284: Setting Read only won't succeed until all metadata locks are released. rpl_row_create_table : Bug#51574 2010-02-27 andrei failed different way than earlier with bug#45576 rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux diff --git a/mysql-test/suite/rpl/t/rpl_failed_optimize-master.opt b/mysql-test/suite/rpl/t/rpl_failed_optimize-master.opt deleted file mode 100644 index 3f82baff598..00000000000 --- a/mysql-test/suite/rpl/t/rpl_failed_optimize-master.opt +++ /dev/null @@ -1 +0,0 @@ ---loose-innodb-lock-wait-timeout=1 From b9819e703ecbfbdca9cdb3871b73e4307662546c Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Tue, 15 Feb 2011 12:01:52 +0000 Subject: [PATCH 29/64] BUG#11754075: BUG#45621: A FEW TEST FILES ARE DISABLED DUE TO WL#4284 Backporting the patch from BUG#11753506 into mysql-5.5 as it is already in mysql-trunk but not in mysql-5.5. --- mysql-test/suite/rpl/r/rpl_read_only.result | 25 +++++++++++++-------- mysql-test/suite/rpl/t/disabled.def | 1 - mysql-test/suite/rpl/t/rpl_read_only.test | 23 +++++++++++++------ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_read_only.result b/mysql-test/suite/rpl/r/rpl_read_only.result index f75ddd62d76..2f1f8cc1efb 100644 --- a/mysql-test/suite/rpl/r/rpl_read_only.result +++ b/mysql-test/suite/rpl/r/rpl_read_only.result @@ -27,15 +27,26 @@ a 2001 set global read_only=0; BEGIN; +BEGIN; +select @@read_only; +@@read_only +0 +set global read_only=1; +*** On SUPER USER connection *** insert into t1 values(1002); insert into t2 values(2002); -BEGIN; +*** On regular USER connection *** insert into t1 values(1003); -insert into t2 values(2003); -set global read_only=1; -COMMIT; -COMMIT; ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement +insert into t2 values(2003); +ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement +*** SUPER USER COMMIT (must succeed) *** +COMMIT; +*** regular USER COMMIT (must succeed - nothing to commit) *** +COMMIT; +select @@read_only; +@@read_only +1 set global read_only=0; insert into t1 values(1004); insert into t2 values(2004); @@ -48,7 +59,6 @@ select * from t2; a 2001 2002 -2003 2004 select * from t1; a @@ -59,7 +69,6 @@ select * from t2; a 2001 2002 -2003 2004 set global read_only=1; select @@read_only; @@ -87,7 +96,6 @@ select * from t2; a 2001 2002 -2003 2004 2005 select * from t1; @@ -100,7 +108,6 @@ select * from t2; a 2001 2002 -2003 2004 2005 insert into t1 values(1006); diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index deb32e7d504..5d10653b7d9 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -10,7 +10,6 @@ # ############################################################################## -rpl_read_only : WL#4284: Setting Read only won't succeed until all metadata locks are released. rpl_row_create_table : Bug#51574 2010-02-27 andrei failed different way than earlier with bug#45576 rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux rpl_get_master_version_and_clock : Bug#59178 Jan 05 2011 joro Valgrind warnings rpl_get_master_version_and_clock diff --git a/mysql-test/suite/rpl/t/rpl_read_only.test b/mysql-test/suite/rpl/t/rpl_read_only.test index 19514dc7ed7..fb92803847a 100644 --- a/mysql-test/suite/rpl/t/rpl_read_only.test +++ b/mysql-test/suite/rpl/t/rpl_read_only.test @@ -41,30 +41,39 @@ set global read_only=0; connection master1; BEGIN; + +connection master2; +BEGIN; + +connection master; +select @@read_only; +set global read_only=1; + +connection master1; +-- echo *** On SUPER USER connection *** insert into t1 values(1002); --disable_warnings insert into t2 values(2002); --enable_warnings connection master2; -BEGIN; +-- echo *** On regular USER connection *** +--error ER_OPTION_PREVENTS_STATEMENT insert into t1 values(1003); ---disable_warnings +--error ER_OPTION_PREVENTS_STATEMENT insert into t2 values(2003); ---enable_warnings - -connection master; -set global read_only=1; connection master1; ## works even with read_only=1, because master1 is root +-- echo *** SUPER USER COMMIT (must succeed) *** COMMIT; connection master2; ---error ER_OPTION_PREVENTS_STATEMENT +-- echo *** regular USER COMMIT (must succeed - nothing to commit) *** COMMIT; connection master; +select @@read_only; set global read_only=0; connection master1; From 3e3fd17cd7f5658061b51eb074c024498f0d0e6c Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 16 Feb 2011 12:24:17 +0100 Subject: [PATCH 30/64] Removed unused code accidentally pushed with Bug @58122 --- mysql-test/mysql-test-run.pl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5e61b4f1867..3eb521a8fc2 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1560,12 +1560,6 @@ sub command_line_setup { $debug_d= "d,query,info,error,enter,exit"; } - if ($opt_debug && $opt_debug ne "1") - { - $debug_d= "d,$opt_debug"; - $debug_d= "d,query,info,error,enter,exit" if $opt_debug eq "std"; - } - mtr_report("Checking supported features..."); check_ndbcluster_support(\%mysqld_variables); From 749945c1fd96ef515fcc2054fab9b1e8917f3b37 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 16 Feb 2011 13:44:28 +0100 Subject: [PATCH 31/64] Bug #11751935 42969: PLEASE ADD A MANIFEST TO EACH BUILD post-push fix: make it work in out-of-source builds. --- mysql-test/mysql-test-run.pl | 1 + mysql-test/t/file_contents.test | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 3eb521a8fc2..b0295f8380a 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2160,6 +2160,7 @@ sub environment_setup { $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir; $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir; $ENV{'MYSQL_LIBDIR'}= "$basedir/lib"; + $ENV{'MYSQL_BINDIR'}= "$bindir"; $ENV{'MYSQL_SHAREDIR'}= $path_language; $ENV{'MYSQL_CHARSETSDIR'}= $path_charsetsdir; diff --git a/mysql-test/t/file_contents.test b/mysql-test/t/file_contents.test index 61d684436f2..2a112e14495 100644 --- a/mysql-test/t/file_contents.test +++ b/mysql-test/t/file_contents.test @@ -10,7 +10,7 @@ --perl print "\nChecking 'INFO_SRC' and 'INFO_BIN'\n"; -$dir_docs = $ENV{'MYSQL_LIBDIR'}; +$dir_docs = $ENV{'MYSQL_BINDIR'}; if($dir_docs =~ m|/usr/|) { # RPM package $dir_docs =~ s|/lib|/share/doc|; From afda842f02f7a7dc7937200ea5622f8904d5f605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 16 Feb 2011 15:34:16 +0200 Subject: [PATCH 32/64] Make the implicit unpack parameter explicit in the Bug #60049 test. --- mysql-test/suite/innodb/t/innodb_bug60049.test | 4 ++-- mysql-test/suite/innodb_plugin/t/innodb_bug60049.test | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/innodb/t/innodb_bug60049.test b/mysql-test/suite/innodb/t/innodb_bug60049.test index 1e0feaf0c89..cff1c3dc09d 100644 --- a/mysql-test/suite/innodb/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb/t/innodb_bug60049.test @@ -23,13 +23,13 @@ open(FILE, "<$file") || die "Unable to open $file"; # Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; die unless read(FILE, $_, 4) == 4; -my $sys_tables_id_root = unpack "N"; +my $sys_tables_id_root = unpack("N", $_); print "Last record of ID_IND root page ($sys_tables_id_root):\n"; # This should be the last record in ID_IND. Dump it in hexadecimal. seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; read(FILE, $_, 32) || die "Unable to read $file"; close(FILE); -print unpack("H*"),"\n"; +print unpack("H*", $_), "\n"; EOF # Restart the server. diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test index 0c093ac94b1..b3557243235 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test @@ -23,13 +23,13 @@ open(FILE, "<$file") || die "Unable to open $file"; # Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; die unless read(FILE, $_, 4) == 4; -my $sys_tables_id_root = unpack "N"; +my $sys_tables_id_root = unpack("N", $_); print "Last record of ID_IND root page ($sys_tables_id_root):\n"; # This should be the last record in ID_IND. Dump it in hexadecimal. seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; read(FILE, $_, 32) || die "Unable to read $file"; close(FILE); -print unpack("H*"),"\n"; +print unpack("H*", $_), "\n"; EOF # Restart the server. From f9b50b9026ef0abfe231f48de352d869c4f06f83 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 16 Feb 2011 14:48:30 +0100 Subject: [PATCH 33/64] Bug #11766729 59906: 5.5 DEBUG BUILDS BROKEN BY EXTRA/COMP_ERR.C --- extra/comp_err.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extra/comp_err.c b/extra/comp_err.c index 4c40e70f5b7..9f7b47a1a1e 100644 --- a/extra/comp_err.c +++ b/extra/comp_err.c @@ -219,14 +219,13 @@ static void print_escaped_string(FILE *f, const char *str) static int create_header_files(struct errors *error_head) { - uint er_last; + uint er_last= 0; FILE *er_definef, *sql_statef, *er_namef; struct errors *tmp_error; struct message *er_msg; const char *er_text; DBUG_ENTER("create_header_files"); - LINT_INIT(er_last); if (!(er_definef= my_fopen(HEADERFILE, O_WRONLY, MYF(MY_WME)))) { From ba5b97343c8a2b6a38a24e9e762d5a3f2a30b576 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 16 Feb 2011 16:26:19 +0100 Subject: [PATCH 34/64] Bug #11752069 (former bug 43152) Assertion `bitmap_is_set_all(&table->s->all_set)' failed in handler::ha_reset This assertion could be triggered if two connections simultaneously executed two bitmap test functions on the same bitmap. For example, the assertion could be triggered if one connection executed UPDATE while a second connection executed SELECT on the same table. Even if bitmap test functions have read-only semantics and have const bitmaps as parameter, several of them modified the internal state of the bitmap. With interleaved execution of two such functions it was possible for one function to modify the state of the same bitmap that the other function had just modified. This lead to an inconsistent state and could trigger the assert. Internally the bitmap uses 32 bit words for storage. Since bitmaps can contain any number of bits, the last word in the bitmap may not be fully used. A 32 bit mask is maintained where a bit is set if the corresponding bit in the last bitmap word is unused. The problem was that several test functions applied this mask to the last word. Sometimes the mask was negated and used to zero out the remainder of the last word and sometimes the mask was used as-is to fill the remainder of the last word with 1's. This meant that if a function first used the negated mask and another function then used the mask as-is (or vice-versa), the first function would then get the wrong result. This patch fixes the problem by changing the implementation of 9 bitmap functions that modified the bitmap state even if the bitmap was declared const. These functions now preserve the internal state of the bitmap. This makes it possible for two connections to concurrently execute two of these functions on the same bitmap without issues. The patch also removes dead testing code from my_bitmap.c. These tests have already been moved to unittest/mysys/bitmap-t.c. Existing test coverage of my_bitmap has been extended. No MTR test case added as this would require adding several sync points to the bitmap functions. The patch has been tested with a non-deterministic test case posted on the bug report. --- include/my_bit.h | 24 +- include/my_bitmap.h | 9 +- mysys/my_bitmap.c | 629 +++++++++----------------------------- unittest/mysys/bitmap-t.c | 173 ++++++++++- 4 files changed, 328 insertions(+), 507 deletions(-) diff --git a/include/my_bit.h b/include/my_bit.h index 2e464e89049..2ab47b04184 100644 --- a/include/my_bit.h +++ b/include/my_bit.h @@ -1,3 +1,18 @@ +/* Copyright (c) 2007, 2011, 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 Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + /* Some useful bit functions */ @@ -42,9 +57,12 @@ STATIC_INLINE uint my_count_bits(ulonglong v) #endif } -STATIC_INLINE uint my_count_bits_ushort(ushort v) +STATIC_INLINE uint my_count_bits_uint32(uint32 v) { - return _my_bits_nbits[v]; + return (uint) (uchar) (_my_bits_nbits[(uchar) v] + + _my_bits_nbits[(uchar) (v >> 8)] + + _my_bits_nbits[(uchar) (v >> 16)] + + _my_bits_nbits[(uchar) (v >> 24)]); } @@ -104,6 +122,6 @@ extern uint32 my_round_up_to_next_power(uint32 v); uint32 my_clear_highest_bit(uint32 v); uint32 my_reverse_bits(uint32 key); extern uint my_count_bits(ulonglong v); -extern uint my_count_bits_ushort(ushort v); +extern uint my_count_bits_uint32(uint32 v); #endif /* HAVE_INLINE */ C_MODE_END diff --git a/include/my_bitmap.h b/include/my_bitmap.h index ab69b2d671d..42f985c8918 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (c) 2000, 2011, 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 @@ -149,9 +149,10 @@ bitmap_is_set(const MY_BITMAP *map,uint bit) static inline my_bool bitmap_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2) { - *(map1)->last_word_ptr|= (map1)->last_word_mask; - *(map2)->last_word_ptr|= (map2)->last_word_mask; - return memcmp((map1)->bitmap, (map2)->bitmap, 4*no_words_in_map((map1)))==0; + if (memcmp(map1->bitmap, map2->bitmap, 4*(no_words_in_map(map1)-1)) != 0) + return FALSE; + return ((*map1->last_word_ptr | map1->last_word_mask) == + (*map2->last_word_ptr | map2->last_word_mask)); } #define bitmap_clear_all(MAP) \ diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index b7258080337..3d3ab16b599 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (c) 2000, 2011, 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 @@ -91,6 +91,7 @@ static inline void bitmap_lock(MY_BITMAP *map __attribute__((unused))) #endif } + static inline void bitmap_unlock(MY_BITMAP *map __attribute__((unused))) { #ifdef THREAD @@ -100,6 +101,46 @@ static inline void bitmap_unlock(MY_BITMAP *map __attribute__((unused))) } +static inline uint get_first_set(uint32 value, uint word_pos) +{ + uchar *byte_ptr= (uchar*)&value; + uchar byte_value; + uint byte_pos, bit_pos; + + for (byte_pos=0; byte_pos < 4; byte_pos++, byte_ptr++) + { + byte_value= *byte_ptr; + if (byte_value) + { + for (bit_pos=0; ; bit_pos++) + if (byte_value & (1 << bit_pos)) + return (word_pos*32) + (byte_pos*8) + bit_pos; + } + } + return MY_BIT_NONE; +} + + +static inline uint get_first_not_set(uint32 value, uint word_pos) +{ + uchar *byte_ptr= (uchar*)&value; + uchar byte_value; + uint byte_pos, bit_pos; + + for (byte_pos=0; byte_pos < 4; byte_pos++, byte_ptr++) + { + byte_value= *byte_ptr; + if (byte_value != 0xFF) + { + for (bit_pos=0; ; bit_pos++) + if (!(byte_value & (1 << bit_pos))) + return (word_pos*32) + (byte_pos*8) + bit_pos; + } + } + return MY_BIT_NONE; +} + + my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits, my_bool thread_safe __attribute__((unused))) { @@ -259,7 +300,7 @@ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) memset(m, 0xff, prefix_bytes); m+= prefix_bytes; if ((prefix_bits= prefix_size & 7)) - *m++= (1 << prefix_bits)-1; + *(m++)= (1 << prefix_bits)-1; if ((d= no_bytes_in_map(map)-prefix_bytes)) bzero(m, d); } @@ -267,28 +308,43 @@ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size) { - uint prefix_bits= prefix_size & 0x7, res; - uchar *m= (uchar*)map->bitmap; - uchar *end_prefix= m+prefix_size/8; - uchar *end; - DBUG_ASSERT(m && prefix_size <= map->n_bits); - end= m+no_bytes_in_map(map); + uint prefix_bits= prefix_size % 32; + my_bitmap_map *word_ptr= map->bitmap, last_word; + my_bitmap_map *end_prefix= word_ptr + prefix_size / 32; + DBUG_ASSERT(word_ptr && prefix_size <= map->n_bits); - while (m < end_prefix) - if (*m++ != 0xff) - return 0; + /* 1: Words that should be filled with 1 */ + for (; word_ptr < end_prefix; word_ptr++) + if (*word_ptr != 0xFFFFFFFF) + return FALSE; - *map->last_word_ptr&= ~map->last_word_mask; /*Clear bits*/ - res= 0; - if (prefix_bits && *m++ != (1 << prefix_bits)-1) - goto ret; + last_word= *map->last_word_ptr & ~map->last_word_mask; - while (m < end) - if (*m++ != 0) - goto ret; - res= 1; -ret: - return res; + /* 2: Word which contains the end of the prefix (if any) */ + if (prefix_bits) + { + if (word_ptr == map->last_word_ptr) + return uint4korr((uchar*)&last_word) == (uint32)((1 << prefix_bits) - 1); + else if (uint4korr((uchar*)word_ptr) != (uint32)((1 << prefix_bits) - 1)) + return FALSE; + word_ptr++; + } + + /* 3: Words that should be filled with 0 */ + for (; word_ptr < map->last_word_ptr; word_ptr++) + if (*word_ptr != 0) + return FALSE; + + /* + We can end up here in two situations: + 1) We went through the whole bitmap in step 1. This will happen if the + whole bitmap is filled with 1 and prefix_size is a multiple of 32 + (i.e. the prefix does not end in the middle of a word). + In this case word_ptr will be larger than map->last_word_ptr. + 2) We have gone through steps 1-3 and just need to check that also + the last word is 0. + */ + return word_ptr > map->last_word_ptr || last_word == 0; } @@ -296,10 +352,12 @@ my_bool bitmap_is_set_all(const MY_BITMAP *map) { my_bitmap_map *data_ptr= map->bitmap; my_bitmap_map *end= map->last_word_ptr; - *map->last_word_ptr |= map->last_word_mask; - for (; data_ptr <= end; data_ptr++) + + for (; data_ptr < end; data_ptr++) if (*data_ptr != 0xFFFFFFFF) return FALSE; + if ((*map->last_word_ptr | map->last_word_mask) != 0xFFFFFFFF) + return FALSE; return TRUE; } @@ -307,13 +365,13 @@ my_bool bitmap_is_set_all(const MY_BITMAP *map) my_bool bitmap_is_clear_all(const MY_BITMAP *map) { my_bitmap_map *data_ptr= map->bitmap; - my_bitmap_map *end; - if (*map->last_word_ptr & ~map->last_word_mask) - return FALSE; - end= map->last_word_ptr; + my_bitmap_map *end= map->last_word_ptr; + for (; data_ptr < end; data_ptr++) if (*data_ptr) return FALSE; + if (*map->last_word_ptr & ~map->last_word_mask) + return FALSE; return TRUE; } @@ -327,14 +385,14 @@ my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2) map1->n_bits==map2->n_bits); end= map1->last_word_ptr; - *map1->last_word_ptr &= ~map1->last_word_mask; - *map2->last_word_ptr &= ~map2->last_word_mask; - while (m1 <= end) - { - if ((*m1++) & ~(*m2++)) - return 0; - } - return 1; + for (; m1 < end; m1++, m2++) + if (*m1 & ~(*m2)) + return FALSE; + + if ((*map1->last_word_ptr & ~map1->last_word_mask) & + ~(*map2->last_word_ptr & ~map2->last_word_mask)) + return FALSE; + return TRUE; } /* True if bitmaps has any common bits */ @@ -347,14 +405,14 @@ my_bool bitmap_is_overlapping(const MY_BITMAP *map1, const MY_BITMAP *map2) map1->n_bits==map2->n_bits); end= map1->last_word_ptr; - *map1->last_word_ptr &= ~map1->last_word_mask; - *map2->last_word_ptr &= ~map2->last_word_mask; - while (m1 <= end) - { - if ((*m1++) & (*m2++)) - return 1; - } - return 0; + for (; m1 < end; m1++, m2++) + if (*m1 & *m2) + return TRUE; + + if ((*map1->last_word_ptr & ~map1->last_word_mask) & + (*map2->last_word_ptr & ~map2->last_word_mask)) + return TRUE; + return FALSE; } @@ -366,15 +424,17 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) DBUG_ASSERT(map->bitmap && map2->bitmap); end= to+min(len,len2); - *map2->last_word_ptr&= ~map2->last_word_mask; /*Clear last bits in map2*/ - while (to < end) - *to++ &= *from++; + for (; to < end; to++, from++) + *to &= *from; + + if (len >= len2) + map->bitmap[len2 - 1] &= ~map2->last_word_mask; if (len2 < len) { end+=len-len2; - while (to < end) - *to++=0; + for (; to < end; to++) + *to= 0; } } @@ -405,8 +465,8 @@ void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit) uchar *to= (uchar *)map->bitmap + from_byte; uchar *end= (uchar *)map->bitmap + (map->n_bits+7)/8; - while (to < end) - *to++= use_byte; + for (; to < end; to++) + *to= use_byte; } @@ -415,59 +475,60 @@ void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); - end= map->last_word_ptr; - while (to <= end) - *to++ &= ~(*from++); + for (; to <= end; to++, from++) + *to &= ~(*from); } void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2) { my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; - DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); end= map->last_word_ptr; - while (to <= end) - *to++ |= *from++; + for (; to <= end; to++, from++) + *to |= *from; } void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2) { - my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr; + my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); - while (to <= end) - *to++ ^= *from++; + end= map->last_word_ptr; + + for (; to <= end; to++, from++) + *to ^= *from; } void bitmap_invert(MY_BITMAP *map) { my_bitmap_map *to= map->bitmap, *end; - DBUG_ASSERT(map->bitmap); end= map->last_word_ptr; - while (to <= end) - *to++ ^= 0xFFFFFFFF; + for (; to <= end; to++) + *to ^= 0xFFFFFFFF; } uint bitmap_bits_set(const MY_BITMAP *map) -{ - uchar *m= (uchar*)map->bitmap; - uchar *end= m + no_bytes_in_map(map); +{ + my_bitmap_map *data_ptr= map->bitmap; + my_bitmap_map *end= map->last_word_ptr; uint res= 0; - DBUG_ASSERT(map->bitmap); - *map->last_word_ptr&= ~map->last_word_mask; /*Reset last bits to zero*/ - while (m < end) - res+= my_count_bits_ushort(*m++); + + for (; data_ptr < end; data_ptr++) + res+= my_count_bits_uint32(*data_ptr); + + /*Reset last bits to zero*/ + res+= my_count_bits_uint32(*map->last_word_ptr & ~map->last_word_mask); return res; } @@ -475,76 +536,44 @@ uint bitmap_bits_set(const MY_BITMAP *map) void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2) { my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; - DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); end= map->last_word_ptr; - while (to <= end) - *to++ = *from++; + + for (; to <= end; to++, from++) + *to = *from; } uint bitmap_get_first_set(const MY_BITMAP *map) { - uchar *byte_ptr; - uint i,j,k; + uint word_pos; my_bitmap_map *data_ptr, *end= map->last_word_ptr; DBUG_ASSERT(map->bitmap); data_ptr= map->bitmap; - *map->last_word_ptr &= ~map->last_word_mask; - for (i=0; data_ptr <= end; data_ptr++, i++) - { + for (word_pos=0; data_ptr < end; data_ptr++, word_pos++) if (*data_ptr) - { - byte_ptr= (uchar*)data_ptr; - for (j=0; ; j++, byte_ptr++) - { - if (*byte_ptr) - { - for (k=0; ; k++) - { - if (*byte_ptr & (1 << k)) - return (i*32) + (j*8) + k; - } - } - } - } - } - return MY_BIT_NONE; + return get_first_set(*data_ptr, word_pos); + + return get_first_set(*map->last_word_ptr & ~map->last_word_mask, word_pos); } uint bitmap_get_first(const MY_BITMAP *map) { - uchar *byte_ptr; - uint i,j,k; + uint word_pos; my_bitmap_map *data_ptr, *end= map->last_word_ptr; DBUG_ASSERT(map->bitmap); data_ptr= map->bitmap; - *map->last_word_ptr|= map->last_word_mask; - for (i=0; data_ptr <= end; data_ptr++, i++) - { + for (word_pos=0; data_ptr < end; data_ptr++, word_pos++) if (*data_ptr != 0xFFFFFFFF) - { - byte_ptr= (uchar*)data_ptr; - for (j=0; ; j++, byte_ptr++) - { - if (*byte_ptr != 0xFF) - { - for (k=0; ; k++) - { - if (!(*byte_ptr & (1 << k))) - return (i*32) + (j*8) + k; - } - } - } - } - } - return MY_BIT_NONE; + return get_first_not_set(*data_ptr, word_pos); + + return get_first_not_set(*map->last_word_ptr | map->last_word_mask, word_pos); } @@ -752,375 +781,3 @@ void bitmap_lock_flip_bit(MY_BITMAP *map, uint bitmap_bit) bitmap_unlock(map); } #endif -#ifdef MAIN - -uint get_rand_bit(uint bitsize) -{ - return (rand() % bitsize); -} - -bool test_set_get_clear_bit(MY_BITMAP *map, uint bitsize) -{ - uint i, test_bit; - uint no_loops= bitsize > 128 ? 128 : bitsize; - for (i=0; i < no_loops; i++) - { - test_bit= get_rand_bit(bitsize); - bitmap_set_bit(map, test_bit); - if (!bitmap_is_set(map, test_bit)) - goto error1; - bitmap_clear_bit(map, test_bit); - if (bitmap_is_set(map, test_bit)) - goto error2; - } - return FALSE; -error1: - printf("Error in set bit, bit %u, bitsize = %u", test_bit, bitsize); - return TRUE; -error2: - printf("Error in clear bit, bit %u, bitsize = %u", test_bit, bitsize); - return TRUE; -} - -bool test_flip_bit(MY_BITMAP *map, uint bitsize) -{ - uint i, test_bit; - uint no_loops= bitsize > 128 ? 128 : bitsize; - for (i=0; i < no_loops; i++) - { - test_bit= get_rand_bit(bitsize); - bitmap_flip_bit(map, test_bit); - if (!bitmap_is_set(map, test_bit)) - goto error1; - bitmap_flip_bit(map, test_bit); - if (bitmap_is_set(map, test_bit)) - goto error2; - } - return FALSE; -error1: - printf("Error in flip bit 1, bit %u, bitsize = %u", test_bit, bitsize); - return TRUE; -error2: - printf("Error in flip bit 2, bit %u, bitsize = %u", test_bit, bitsize); - return TRUE; -} - -bool test_operators(MY_BITMAP *map __attribute__((unused)), - uint bitsize __attribute__((unused))) -{ - return FALSE; -} - -bool test_get_all_bits(MY_BITMAP *map, uint bitsize) -{ - uint i; - bitmap_set_all(map); - if (!bitmap_is_set_all(map)) - goto error1; - if (!bitmap_is_prefix(map, bitsize)) - goto error5; - bitmap_clear_all(map); - if (!bitmap_is_clear_all(map)) - goto error2; - if (!bitmap_is_prefix(map, 0)) - goto error6; - for (i=0; i 128 ? 128 : bitsize; - MY_BITMAP map2_obj, map3_obj; - MY_BITMAP *map2= &map2_obj, *map3= &map3_obj; - my_bitmap_map map2buf[1024]; - my_bitmap_map map3buf[1024]; - bitmap_init(&map2_obj, map2buf, bitsize, FALSE); - bitmap_init(&map3_obj, map3buf, bitsize, FALSE); - bitmap_clear_all(map2); - bitmap_clear_all(map3); - for (i=0; i < no_loops; i++) - { - test_bit1=get_rand_bit(bitsize); - bitmap_set_prefix(map, test_bit1); - test_bit2=get_rand_bit(bitsize); - bitmap_set_prefix(map2, test_bit2); - bitmap_intersect(map, map2); - test_bit3= test_bit2 < test_bit1 ? test_bit2 : test_bit1; - bitmap_set_prefix(map3, test_bit3); - if (!bitmap_cmp(map, map3)) - goto error1; - bitmap_clear_all(map); - bitmap_clear_all(map2); - bitmap_clear_all(map3); - test_bit1=get_rand_bit(bitsize); - test_bit2=get_rand_bit(bitsize); - test_bit3=get_rand_bit(bitsize); - bitmap_set_prefix(map, test_bit1); - bitmap_set_prefix(map2, test_bit2); - test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1; - bitmap_set_prefix(map3, test_bit3); - bitmap_union(map, map2); - if (!bitmap_cmp(map, map3)) - goto error2; - bitmap_clear_all(map); - bitmap_clear_all(map2); - bitmap_clear_all(map3); - test_bit1=get_rand_bit(bitsize); - test_bit2=get_rand_bit(bitsize); - test_bit3=get_rand_bit(bitsize); - bitmap_set_prefix(map, test_bit1); - bitmap_set_prefix(map2, test_bit2); - bitmap_xor(map, map2); - test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1; - test_bit4= test_bit2 < test_bit1 ? test_bit2 : test_bit1; - bitmap_set_prefix(map3, test_bit3); - for (j=0; j < test_bit4; j++) - bitmap_clear_bit(map3, j); - if (!bitmap_cmp(map, map3)) - goto error3; - bitmap_clear_all(map); - bitmap_clear_all(map2); - bitmap_clear_all(map3); - test_bit1=get_rand_bit(bitsize); - test_bit2=get_rand_bit(bitsize); - test_bit3=get_rand_bit(bitsize); - bitmap_set_prefix(map, test_bit1); - bitmap_set_prefix(map2, test_bit2); - bitmap_subtract(map, map2); - if (test_bit2 < test_bit1) - { - bitmap_set_prefix(map3, test_bit1); - for (j=0; j < test_bit2; j++) - bitmap_clear_bit(map3, j); - } - if (!bitmap_cmp(map, map3)) - goto error4; - bitmap_clear_all(map); - bitmap_clear_all(map2); - bitmap_clear_all(map3); - test_bit1=get_rand_bit(bitsize); - bitmap_set_prefix(map, test_bit1); - bitmap_invert(map); - bitmap_set_all(map3); - for (j=0; j < test_bit1; j++) - bitmap_clear_bit(map3, j); - if (!bitmap_cmp(map, map3)) - goto error5; - bitmap_clear_all(map); - bitmap_clear_all(map3); - } - return FALSE; -error1: - printf("intersect error bitsize=%u,size1=%u,size2=%u", bitsize, - test_bit1,test_bit2); - return TRUE; -error2: - printf("union error bitsize=%u,size1=%u,size2=%u", bitsize, - test_bit1,test_bit2); - return TRUE; -error3: - printf("xor error bitsize=%u,size1=%u,size2=%u", bitsize, - test_bit1,test_bit2); - return TRUE; -error4: - printf("subtract error bitsize=%u,size1=%u,size2=%u", bitsize, - test_bit1,test_bit2); - return TRUE; -error5: - printf("invert error bitsize=%u,size=%u", bitsize, - test_bit1); - return TRUE; -} - -bool test_count_bits_set(MY_BITMAP *map, uint bitsize) -{ - uint i, bit_count=0, test_bit; - uint no_loops= bitsize > 128 ? 128 : bitsize; - for (i=0; i < no_loops; i++) - { - test_bit=get_rand_bit(bitsize); - if (!bitmap_is_set(map, test_bit)) - { - bitmap_set_bit(map, test_bit); - bit_count++; - } - } - if (bit_count==0 && bitsize > 0) - goto error1; - if (bitmap_bits_set(map) != bit_count) - goto error2; - return FALSE; -error1: - printf("No bits set bitsize = %u", bitsize); - return TRUE; -error2: - printf("Wrong count of bits set, bitsize = %u", bitsize); - return TRUE; -} - -bool test_get_first_bit(MY_BITMAP *map, uint bitsize) -{ - uint i, test_bit; - uint no_loops= bitsize > 128 ? 128 : bitsize; - for (i=0; i < no_loops; i++) - { - test_bit=get_rand_bit(bitsize); - bitmap_set_bit(map, test_bit); - if (bitmap_get_first_set(map) != test_bit) - goto error1; - bitmap_set_all(map); - bitmap_clear_bit(map, test_bit); - if (bitmap_get_first(map) != test_bit) - goto error2; - bitmap_clear_all(map); - } - return FALSE; -error1: - printf("get_first_set error bitsize=%u,prefix_size=%u",bitsize,test_bit); - return TRUE; -error2: - printf("get_first error bitsize= %u, prefix_size= %u",bitsize,test_bit); - return TRUE; -} - -bool test_get_next_bit(MY_BITMAP *map, uint bitsize) -{ - uint i, j, test_bit; - uint no_loops= bitsize > 128 ? 128 : bitsize; - for (i=0; i < no_loops; i++) - { - test_bit=get_rand_bit(bitsize); - for (j=0; j < test_bit; j++) - bitmap_set_next(map); - if (!bitmap_is_prefix(map, test_bit)) - goto error1; - bitmap_clear_all(map); - } - return FALSE; -error1: - printf("get_next error bitsize= %u, prefix_size= %u", bitsize,test_bit); - return TRUE; -} - -bool test_prefix(MY_BITMAP *map, uint bitsize) -{ - uint i, j, test_bit; - uint no_loops= bitsize > 128 ? 128 : bitsize; - for (i=0; i < no_loops; i++) - { - test_bit=get_rand_bit(bitsize); - bitmap_set_prefix(map, test_bit); - if (!bitmap_is_prefix(map, test_bit)) - goto error1; - bitmap_clear_all(map); - for (j=0; j < test_bit; j++) - bitmap_set_bit(map, j); - if (!bitmap_is_prefix(map, test_bit)) - goto error2; - bitmap_set_all(map); - for (j=bitsize - 1; ~(j-test_bit); j--) - bitmap_clear_bit(map, j); - if (!bitmap_is_prefix(map, test_bit)) - goto error3; - bitmap_clear_all(map); - } - return FALSE; -error1: - printf("prefix1 error bitsize = %u, prefix_size = %u", bitsize,test_bit); - return TRUE; -error2: - printf("prefix2 error bitsize = %u, prefix_size = %u", bitsize,test_bit); - return TRUE; -error3: - printf("prefix3 error bitsize = %u, prefix_size = %u", bitsize,test_bit); - return TRUE; -} - - -bool do_test(uint bitsize) -{ - MY_BITMAP map; - my_bitmap_map buf[1024]; - if (bitmap_init(&map, buf, bitsize, FALSE)) - { - printf("init error for bitsize %d", bitsize); - goto error; - } - if (test_set_get_clear_bit(&map,bitsize)) - goto error; - bitmap_clear_all(&map); - if (test_flip_bit(&map,bitsize)) - goto error; - bitmap_clear_all(&map); - if (test_operators(&map,bitsize)) - goto error; - bitmap_clear_all(&map); - if (test_get_all_bits(&map, bitsize)) - goto error; - bitmap_clear_all(&map); - if (test_compare_operators(&map,bitsize)) - goto error; - bitmap_clear_all(&map); - if (test_count_bits_set(&map,bitsize)) - goto error; - bitmap_clear_all(&map); - if (test_get_first_bit(&map,bitsize)) - goto error; - bitmap_clear_all(&map); - if (test_get_next_bit(&map,bitsize)) - goto error; - if (test_prefix(&map,bitsize)) - goto error; - return FALSE; -error: - printf("\n"); - return TRUE; -} - -int main() -{ - int i; - for (i= 1; i < 4096; i++) - { - printf("Start test for bitsize=%u\n",i); - if (do_test(i)) - return -1; - } - printf("OK\n"); - return 0; -} - -/* - In directory mysys: - make test_bitmap - will build the bitmap tests and ./test_bitmap will execute it -*/ - -#endif diff --git a/unittest/mysys/bitmap-t.c b/unittest/mysys/bitmap-t.c index 0bd21b63430..d5c1791ca14 100644 --- a/unittest/mysys/bitmap-t.c +++ b/unittest/mysys/bitmap-t.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2006 MySQL AB +/* Copyright (c) 2000, 2011, 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 @@ -24,6 +24,8 @@ #include #include +#define MAX_TESTED_BITMAP_SIZE 1024 + uint get_rand_bit(uint bitsize) { return (rand() % bitsize); @@ -75,12 +77,6 @@ error2: return TRUE; } -my_bool test_operators(MY_BITMAP *map __attribute__((unused)), - uint bitsize __attribute__((unused))) -{ - return FALSE; -} - my_bool test_get_all_bits(MY_BITMAP *map, uint bitsize) { uint i; @@ -129,8 +125,8 @@ my_bool test_compare_operators(MY_BITMAP *map, uint bitsize) uint no_loops= bitsize > 128 ? 128 : bitsize; MY_BITMAP map2_obj, map3_obj; MY_BITMAP *map2= &map2_obj, *map3= &map3_obj; - uint32 map2buf[1024]; - uint32 map3buf[1024]; + uint32 map2buf[MAX_TESTED_BITMAP_SIZE]; + uint32 map3buf[MAX_TESTED_BITMAP_SIZE]; bitmap_init(&map2_obj, map2buf, bitsize, FALSE); bitmap_init(&map3_obj, map3buf, bitsize, FALSE); bitmap_clear_all(map2); @@ -259,6 +255,19 @@ my_bool test_get_first_bit(MY_BITMAP *map, uint bitsize) { uint i, test_bit; uint no_loops= bitsize > 128 ? 128 : bitsize; + + bitmap_set_all(map); + for (i=0; i < bitsize; i++) + bitmap_clear_bit(map, i); + if (bitmap_get_first_set(map) != MY_BIT_NONE) + goto error1; + bitmap_clear_all(map); + for (i=0; i < bitsize; i++) + bitmap_set_bit(map, i); + if (bitmap_get_first(map) != MY_BIT_NONE) + goto error2; + bitmap_clear_all(map); + for (i=0; i < no_loops; i++) { test_bit=get_rand_bit(bitsize); @@ -321,6 +330,24 @@ my_bool test_prefix(MY_BITMAP *map, uint bitsize) goto error3; bitmap_clear_all(map); } + for (i=0; i < bitsize; i++) + { + if (bitmap_is_prefix(map, i + 1)) + goto error4; + bitmap_set_bit(map, i); + if (!bitmap_is_prefix(map, i + 1)) + goto error5; + test_bit=get_rand_bit(bitsize); + bitmap_set_bit(map, test_bit); + if (test_bit <= i && !bitmap_is_prefix(map, i + 1)) + goto error5; + else if (test_bit > i) + { + if (bitmap_is_prefix(map, i + 1)) + goto error4; + bitmap_clear_bit(map, test_bit); + } + } return FALSE; error1: diag("prefix1 error bitsize = %u, prefix_size = %u", bitsize,test_bit); @@ -331,13 +358,127 @@ error2: error3: diag("prefix3 error bitsize = %u, prefix_size = %u", bitsize,test_bit); return TRUE; +error4: + diag("prefix4 error bitsize = %u, i = %u", bitsize,i); + return TRUE; +error5: + diag("prefix5 error bitsize = %u, i = %u", bitsize,i); + return TRUE; } +my_bool test_compare(MY_BITMAP *map, uint bitsize) +{ + MY_BITMAP map2; + uint32 map2buf[MAX_TESTED_BITMAP_SIZE]; + uint i, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + if (bitmap_init(&map2, map2buf, bitsize, FALSE)) + { + diag("init error for bitsize %d", bitsize); + return TRUE; + } + /* Test all 4 possible combinations of set/unset bits. */ + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + bitmap_clear_bit(map, test_bit); + bitmap_clear_bit(&map2, test_bit); + if (!bitmap_is_subset(map, &map2)) + goto error_is_subset; + bitmap_set_bit(map, test_bit); + if (bitmap_is_subset(map, &map2)) + goto error_is_subset; + bitmap_set_bit(&map2, test_bit); + if (!bitmap_is_subset(map, &map2)) + goto error_is_subset; + bitmap_clear_bit(map, test_bit); + if (!bitmap_is_subset(map, &map2)) + goto error_is_subset; + /* Note that test_bit is not cleared i map2. */ + } + bitmap_clear_all(map); + bitmap_clear_all(&map2); + /* Test all 4 possible combinations of set/unset bits. */ + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + if (bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_set_bit(map, test_bit); + if (bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_set_bit(&map2, test_bit); + if (!bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_clear_bit(map, test_bit); + if (bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_clear_bit(&map2, test_bit); + /* Note that test_bit is not cleared i map2. */ + } + return FALSE; +error_is_subset: + diag("is_subset error bitsize = %u", bitsize); + return TRUE; +error_is_overlapping: + diag("is_overlapping error bitsize = %u", bitsize); + return TRUE; +} + +my_bool test_intersect(MY_BITMAP *map, uint bitsize) +{ + uint bitsize2 = 1 + get_rand_bit(MAX_TESTED_BITMAP_SIZE - 1); + MY_BITMAP map2; + uint32 map2buf[bitsize2]; + uint i, test_bit1, test_bit2, test_bit3; + if (bitmap_init(&map2, map2buf, bitsize2, FALSE)) + { + diag("init error for bitsize %d", bitsize2); + return TRUE; + } + test_bit1= get_rand_bit(bitsize); + test_bit2= get_rand_bit(bitsize); + bitmap_set_bit(map, test_bit1); + bitmap_set_bit(map, test_bit2); + test_bit3= get_rand_bit(bitsize2); + bitmap_set_bit(&map2, test_bit3); + if (test_bit2 < bitsize2) + bitmap_set_bit(&map2, test_bit2); + + bitmap_intersect(map, &map2); + if (test_bit2 < bitsize2) + { + if (!bitmap_is_set(map, test_bit2)) + goto error; + bitmap_clear_bit(map, test_bit2); + } + if (test_bit1 == test_bit3) + { + if (!bitmap_is_set(map, test_bit1)) + goto error; + bitmap_clear_bit(map, test_bit1); + } + if (!bitmap_is_clear_all(map)) + goto error; + + bitmap_set_all(map); + bitmap_set_all(&map2); + for (i=0; i < bitsize2; i++) + bitmap_clear_bit(&map2, i); + bitmap_intersect(map, &map2); + if (!bitmap_is_clear_all(map)) + goto error; + return FALSE; +error: + diag("intersect error bitsize = %u, bit1 = %u, bit2 = %u, bit3 = %u", + bitsize, test_bit1, test_bit2, test_bit3); + return TRUE; +} my_bool do_test(uint bitsize) { MY_BITMAP map; - uint32 buf[1024]; + uint32 buf[MAX_TESTED_BITMAP_SIZE]; if (bitmap_init(&map, buf, bitsize, FALSE)) { diag("init error for bitsize %d", bitsize); @@ -349,9 +490,6 @@ my_bool do_test(uint bitsize) if (test_flip_bit(&map,bitsize)) goto error; bitmap_clear_all(&map); - if (test_operators(&map,bitsize)) - goto error; - bitmap_clear_all(&map); if (test_get_all_bits(&map, bitsize)) goto error; bitmap_clear_all(&map); @@ -366,8 +504,15 @@ my_bool do_test(uint bitsize) bitmap_clear_all(&map); if (test_get_next_bit(&map,bitsize)) goto error; + bitmap_clear_all(&map); if (test_prefix(&map,bitsize)) goto error; + bitmap_clear_all(&map); + if (test_compare(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_intersect(&map,bitsize)) + goto error; return FALSE; error: return TRUE; @@ -377,7 +522,7 @@ int main() { int i; int const min_size = 1; - int const max_size = 1024; + int const max_size = MAX_TESTED_BITMAP_SIZE; MY_INIT("bitmap-t"); plan(max_size - min_size); From d4674ca897a3173ef58354b0697ff47689d03ca3 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Wed, 16 Feb 2011 17:27:35 +0100 Subject: [PATCH 35/64] WL#5670: Proposal: Remove SHOW NEW MASTER statement Removes SHOW NEW MASTER statement and all related code. Also removes the unused function update_slave_list from repl_failsafe.cc. --- mysql-test/r/signal_code.result | 16 +- mysql-test/r/sp-code.result | 4 +- sql/mysqld.cc | 1 - sql/repl_failsafe.cc | 366 -------------------------------- sql/repl_failsafe.h | 7 - sql/sp_head.cc | 1 - sql/sql_lex.h | 5 +- sql/sql_parse.cc | 14 -- sql/sql_repl.cc | 17 -- sql/sql_repl.h | 2 - sql/sql_yacc.yy | 13 -- 11 files changed, 12 insertions(+), 434 deletions(-) diff --git a/mysql-test/r/signal_code.result b/mysql-test/r/signal_code.result index 2ecba4701fa..ca46f1d2079 100644 --- a/mysql-test/r/signal_code.result +++ b/mysql-test/r/signal_code.result @@ -20,16 +20,16 @@ return 0; end $$ show procedure code signal_proc; Pos Instruction -0 stmt 131 "SIGNAL foo" -1 stmt 131 "SIGNAL foo SET MESSAGE_TEXT = "This i..." -2 stmt 132 "RESIGNAL foo" -3 stmt 132 "RESIGNAL foo SET MESSAGE_TEXT = "This..." +0 stmt 130 "SIGNAL foo" +1 stmt 130 "SIGNAL foo SET MESSAGE_TEXT = "This i..." +2 stmt 131 "RESIGNAL foo" +3 stmt 131 "RESIGNAL foo SET MESSAGE_TEXT = "This..." drop procedure signal_proc; show function code signal_func; Pos Instruction -0 stmt 131 "SIGNAL foo" -1 stmt 131 "SIGNAL foo SET MESSAGE_TEXT = "This i..." -2 stmt 132 "RESIGNAL foo" -3 stmt 132 "RESIGNAL foo SET MESSAGE_TEXT = "This..." +0 stmt 130 "SIGNAL foo" +1 stmt 130 "SIGNAL foo SET MESSAGE_TEXT = "This i..." +2 stmt 131 "RESIGNAL foo" +3 stmt 131 "RESIGNAL foo SET MESSAGE_TEXT = "This..." 4 freturn 3 0 drop function signal_func; diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result index c7ea4cbb311..16a43a00f04 100644 --- a/mysql-test/r/sp-code.result +++ b/mysql-test/r/sp-code.result @@ -155,11 +155,11 @@ Pos Instruction 0 stmt 9 "drop temporary table if exists sudoku..." 1 stmt 1 "create temporary table sudoku_work ( ..." 2 stmt 1 "create temporary table sudoku_schedul..." -3 stmt 89 "call sudoku_init()" +3 stmt 88 "call sudoku_init()" 4 jump_if_not 7(8) p_naive@0 5 stmt 4 "update sudoku_work set cnt = 0 where ..." 6 jump 8 -7 stmt 89 "call sudoku_count()" +7 stmt 88 "call sudoku_count()" 8 stmt 6 "insert into sudoku_schedule (row,col)..." 9 set v_scounter@2 0 10 set v_i@3 1 diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c81d8bd75e2..c1bad398f80 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3028,7 +3028,6 @@ SHOW_VAR com_status_vars[]= { {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS}, {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS}, {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS}, - {"show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS}, {"show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS}, {"show_plugins", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PLUGINS]), SHOW_LONG_STATUS}, {"show_privileges", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PRIVILEGES]), SHOW_LONG_STATUS}, diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 540b62b9d3b..bc710616a4c 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -56,10 +56,6 @@ const char* rpl_status_type[]= "RECOVERY_CAPTAIN","NULL",NullS }; -static Slave_log_event* find_slave_event(IO_CACHE* log, - const char* log_file_name, - char* errmsg); - /* All of the functions defined in this file which are not used (the ones to handle failsafe) are not used; their code has not been updated for more than @@ -91,13 +87,6 @@ void change_rpl_status(ulong from_status, ulong to_status) }\ -static inline int cmp_master_pos(Slave_log_event* sev, LEX_MASTER_INFO* mi) -{ - return cmp_master_pos(sev->master_log, sev->master_pos, mi->log_file_name, - mi->pos); -} - - void unregister_slave(THD* thd, bool only_mine, bool need_mutex) { if (thd->server_id) @@ -228,361 +217,6 @@ void end_slave_list() } } -static int find_target_pos(LEX_MASTER_INFO *mi, IO_CACHE *log, char *errmsg) -{ - my_off_t log_pos = (my_off_t) mi->pos; - uint32 target_server_id = mi->server_id; - - for (;;) - { - Log_event* ev; - if (!(ev= Log_event::read_log_event(log, (mysql_mutex_t*) 0, 0))) - { - if (log->error > 0) - strmov(errmsg, "Binary log truncated in the middle of event"); - else if (log->error < 0) - strmov(errmsg, "I/O error reading binary log"); - else - strmov(errmsg, "Could not find target event in the binary log"); - return 1; - } - - if (ev->log_pos >= log_pos && ev->server_id == target_server_id) - { - delete ev; - mi->pos = my_b_tell(log); - return 0; - } - delete ev; - } - /* Impossible */ -} - -/** - @details - Before 4.0.15 we had a member of THD called log_pos, it was meant for - failsafe replication code in repl_failsafe.cc which is disabled until - it is reworked. Event's log_pos used to be preserved through - log-slave-updates to make code in repl_failsafe.cc work (this - function, SHOW NEW MASTER); but on the other side it caused unexpected - values in Exec_Master_Log_Pos in A->B->C replication setup, - synchronization problems in master_pos_wait(), ... So we - (Dmitri & Guilhem) removed it. - - So for now this function is broken. -*/ - -int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg) -{ - LOG_INFO linfo; - char last_log_name[FN_REFLEN]; - IO_CACHE log; - File file = -1, last_file = -1; - mysql_mutex_t *log_lock; - const char* errmsg_p; - Slave_log_event* sev = 0; - my_off_t last_pos = 0; - int error = 1; - int cmp_res; - LINT_INIT(cmp_res); - DBUG_ENTER("translate_master"); - - if (!mysql_bin_log.is_open()) - { - strmov(errmsg,"Binary log is not open"); - DBUG_RETURN(1); - } - - if (!server_id_supplied) - { - strmov(errmsg, "Misconfigured master - server id was not set"); - DBUG_RETURN(1); - } - - if (mysql_bin_log.find_log_pos(&linfo, NullS, 1)) - { - strmov(errmsg,"Could not find first log"); - DBUG_RETURN(1); - } - thd->current_linfo = &linfo; - - bzero((char*) &log,sizeof(log)); - log_lock = mysql_bin_log.get_log_lock(); - mysql_mutex_lock(log_lock); - - for (;;) - { - if ((file=open_binlog(&log, linfo.log_file_name, &errmsg_p)) < 0) - { - strmov(errmsg, errmsg_p); - goto err; - } - - if (!(sev = find_slave_event(&log, linfo.log_file_name, errmsg))) - goto err; - - cmp_res = cmp_master_pos(sev, mi); - delete sev; - - if (!cmp_res) - { - /* Copy basename */ - fn_format(mi->log_file_name, linfo.log_file_name, "","",1); - mi->pos = my_b_tell(&log); - goto mi_inited; - } - else if (cmp_res > 0) - { - if (!last_pos) - { - strmov(errmsg, - "Slave event in first log points past the target position"); - goto err; - } - end_io_cache(&log); - mysql_file_close(file, MYF(MY_WME)); - if (init_io_cache(&log, (file = last_file), IO_SIZE, READ_CACHE, 0, 0, - MYF(MY_WME))) - { - errmsg[0] = 0; - goto err; - } - break; - } - - strmov(last_log_name, linfo.log_file_name); - last_pos = my_b_tell(&log); - - switch (mysql_bin_log.find_next_log(&linfo, 1)) { - case LOG_INFO_EOF: - if (last_file >= 0) - mysql_file_close(last_file, MYF(MY_WME)); - last_file = -1; - goto found_log; - case 0: - break; - default: - strmov(errmsg, "Error reading log index"); - goto err; - } - - end_io_cache(&log); - if (last_file >= 0) - mysql_file_close(last_file, MYF(MY_WME)); - last_file = file; - } - -found_log: - my_b_seek(&log, last_pos); - if (find_target_pos(mi,&log,errmsg)) - goto err; - fn_format(mi->log_file_name, last_log_name, "","",1); /* Copy basename */ - -mi_inited: - error = 0; -err: - mysql_mutex_unlock(log_lock); - end_io_cache(&log); - mysql_mutex_lock(&LOCK_thread_count); - thd->current_linfo = 0; - mysql_mutex_unlock(&LOCK_thread_count); - if (file >= 0) - mysql_file_close(file, MYF(MY_WME)); - if (last_file >= 0 && last_file != file) - mysql_file_close(last_file, MYF(MY_WME)); - - DBUG_RETURN(error); -} - - -/** - Caller must delete result when done. -*/ - -static Slave_log_event* find_slave_event(IO_CACHE* log, - const char* log_file_name, - char* errmsg) -{ - Log_event* ev; - int i; - bool slave_event_found = 0; - LINT_INIT(ev); - - for (i = 0; i < 2; i++) - { - if (!(ev= Log_event::read_log_event(log, (mysql_mutex_t*)0, 0))) - { - my_snprintf(errmsg, SLAVE_ERRMSG_SIZE, - "Error reading event in log '%s'", - (char*)log_file_name); - return 0; - } - if (ev->get_type_code() == SLAVE_EVENT) - { - slave_event_found = 1; - break; - } - delete ev; - } - if (!slave_event_found) - { - my_snprintf(errmsg, SLAVE_ERRMSG_SIZE, - "Could not find slave event in log '%s'", - (char*)log_file_name); - return 0; - } - - return (Slave_log_event*)ev; -} - -/** - This function is broken now. - - @seealso translate_master() -*/ - -bool show_new_master(THD* thd) -{ - Protocol *protocol= thd->protocol; - DBUG_ENTER("show_new_master"); - List field_list; - char errmsg[SLAVE_ERRMSG_SIZE]; - LEX_MASTER_INFO* lex_mi= &thd->lex->mi; - - errmsg[0]=0; // Safety - if (translate_master(thd, lex_mi, errmsg)) - { - if (errmsg[0]) - my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), - "SHOW NEW MASTER", errmsg); - DBUG_RETURN(TRUE); - } - else - { - field_list.push_back(new Item_empty_string("Log_name", 20)); - field_list.push_back(new Item_return_int("Log_pos", 10, - MYSQL_TYPE_LONGLONG)); - if (protocol->send_result_set_metadata(&field_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) - DBUG_RETURN(TRUE); - protocol->prepare_for_resend(); - protocol->store(lex_mi->log_file_name, &my_charset_bin); - protocol->store((ulonglong) lex_mi->pos); - if (protocol->write()) - DBUG_RETURN(TRUE); - my_eof(thd); - DBUG_RETURN(FALSE); - } -} - -/** - Asks the master for the list of its other connected slaves. - - This is for failsafe replication: - in order for failsafe replication to work, the servers involved in - replication must know of each other. We accomplish this by having each - slave report to the master how to reach it, and on connection, each - slave receives information about where the other slaves are. - - @param mysql pre-existing connection to the master - @param mi master info - - @note - mi is used only to give detailed error messages which include the - hostname/port of the master, the username used by the slave to connect to - the master. - If the user used by the slave to connect to the master does not have the - REPLICATION SLAVE privilege, it will pop in this function because - SHOW SLAVE HOSTS will fail on the master. - - @retval - 1 error - @retval - 0 success -*/ - -int update_slave_list(MYSQL* mysql, Master_info* mi) -{ - MYSQL_RES* res=0; - MYSQL_ROW row; - const char* error=0; - bool have_auth_info; - int port_ind; - DBUG_ENTER("update_slave_list"); - - if (mysql_real_query(mysql, STRING_WITH_LEN("SHOW SLAVE HOSTS")) || - !(res = mysql_store_result(mysql))) - { - error= mysql_error(mysql); - goto err; - } - - switch (mysql_num_fields(res)) { - case 5: - have_auth_info = 0; - port_ind=2; - break; - case 7: - have_auth_info = 1; - port_ind=4; - break; - default: - error= "the master returned an invalid number of fields for SHOW SLAVE \ -HOSTS"; - goto err; - } - - mysql_mutex_lock(&LOCK_slave_list); - - while ((row= mysql_fetch_row(res))) - { - uint32 log_server_id; - SLAVE_INFO* si, *old_si; - log_server_id = atoi(row[0]); - if ((old_si= (SLAVE_INFO*)my_hash_search(&slave_list, - (uchar*)&log_server_id,4))) - si = old_si; - else - { - if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME)))) - { - error= "the slave is out of memory"; - mysql_mutex_unlock(&LOCK_slave_list); - goto err; - } - si->server_id = log_server_id; - if (my_hash_insert(&slave_list, (uchar*)si)) - { - error= "the slave is out of memory"; - mysql_mutex_unlock(&LOCK_slave_list); - goto err; - } - } - strmake(si->host, row[1], sizeof(si->host)-1); - si->port = atoi(row[port_ind]); - si->rpl_recovery_rank = atoi(row[port_ind+1]); - si->master_id = atoi(row[port_ind+2]); - if (have_auth_info) - { - strmake(si->user, row[2], sizeof(si->user)-1); - strmake(si->password, row[3], sizeof(si->password)-1); - } - } - mysql_mutex_unlock(&LOCK_slave_list); - -err: - if (res) - mysql_free_result(res); - if (error) - { - sql_print_error("While trying to obtain the list of slaves from the master " - "'%s:%d', user '%s' got the following error: '%s'", - mi->host, mi->port, mi->user, error); - DBUG_RETURN(1); - } - DBUG_RETURN(0); -} - /** Execute a SHOW SLAVE HOSTS statement. diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h index a0c41686696..2ba1f25c591 100644 --- a/sql/repl_failsafe.h +++ b/sql/repl_failsafe.h @@ -33,19 +33,12 @@ extern mysql_cond_t COND_rpl_status; extern TYPELIB rpl_role_typelib; extern const char* rpl_role_type[], *rpl_status_type[]; -pthread_handler_t handle_failsafe_rpl(void *arg); void change_rpl_status(ulong from_status, ulong to_status); int find_recovery_captain(THD* thd, MYSQL* mysql); -int update_slave_list(MYSQL* mysql, Master_info* mi); extern HASH slave_list; -bool load_master_data(THD* thd); -int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi); - -bool show_new_master(THD* thd); bool show_slave_hosts(THD* thd); -int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg); void init_slave_list(); void end_slave_list(); int register_slave(THD* thd, uchar* packet, uint packet_length); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 6959d21abd6..e87e03c1db7 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -237,7 +237,6 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_SHOW_EVENTS: case SQLCOM_SHOW_KEYS: case SQLCOM_SHOW_MASTER_STAT: - case SQLCOM_SHOW_NEW_MASTER: case SQLCOM_SHOW_OPEN_TABLES: case SQLCOM_SHOW_PRIVILEGES: case SQLCOM_SHOW_PROCESSLIST: diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2d11bc7aad7..a9abe174d6e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -162,7 +162,7 @@ enum enum_sql_command { SQLCOM_SHOW_OPEN_TABLES, SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI, - SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, + SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_DO, SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER, @@ -269,8 +269,7 @@ typedef struct st_lex_server_options /** - Structure to hold parameters for CHANGE MASTER or START/STOP SLAVE - or SHOW NEW MASTER. + Structure to hold parameters for CHANGE MASTER, START SLAVE, and STOP SLAVE. Remark: this should not be confused with Master_info (and perhaps would better be renamed to st_lex_replication_info). Some fields, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2086100db98..d9e1245bf66 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -316,7 +316,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; - sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_BINLOG_EVENTS]= CF_STATUS_COMMAND; @@ -2145,19 +2144,6 @@ case SQLCOM_PREPARE: #endif break; } - case SQLCOM_SHOW_NEW_MASTER: - { - if (check_global_access(thd, REPL_SLAVE_ACL)) - goto error; - /* This query don't work now. See comment in repl_failsafe.cc */ -#ifndef WORKING_NEW_MASTER - my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER"); - goto error; -#else - res = show_new_master(thd); - break; -#endif - } #ifdef HAVE_REPLICATION case SQLCOM_SHOW_SLAVE_HOSTS: diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index bc0cca4d887..9783917aaf0 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1694,23 +1694,6 @@ int reset_master(THD* thd) return 0; } -int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, - const char* log_file_name2, ulonglong log_pos2) -{ - int res; - size_t log_file_name1_len= strlen(log_file_name1); - size_t log_file_name2_len= strlen(log_file_name2); - - // We assume that both log names match up to '.' - if (log_file_name1_len == log_file_name2_len) - { - if ((res= strcmp(log_file_name1, log_file_name2))) - return res; - return (log_pos1 < log_pos2) ? -1 : (log_pos1 == log_pos2) ? 0 : 1; - } - return ((log_file_name1_len < log_file_name2_len) ? -1 : 1); -} - /** Execute a SHOW BINLOG EVENTS statement. diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 4d3b0b8d62c..dfe41d8e688 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -43,8 +43,6 @@ int start_slave(THD* thd, Master_info* mi, bool net_report); int stop_slave(THD* thd, Master_info* mi, bool net_report); bool change_master(THD* thd, Master_info* mi); bool mysql_show_binlog_events(THD* thd); -int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, - const char* log_file_name2, ulonglong log_pos2); int reset_slave(THD *thd, Master_info* mi); int reset_master(THD* thd); bool purge_master_logs(THD* thd, const char* to_log); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 397afd26d8d..02e0d347ef7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -10921,19 +10921,6 @@ show_param: if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) MYSQL_YYABORT; } - | NEW_SYM MASTER_SYM FOR_SYM SLAVE - WITH MASTER_LOG_FILE_SYM EQ - TEXT_STRING_sys /* $8 */ - AND_SYM MASTER_LOG_POS_SYM EQ - ulonglong_num /* $12 */ - AND_SYM MASTER_SERVER_ID_SYM EQ - ulong_num /* $16 */ - { - Lex->sql_command = SQLCOM_SHOW_NEW_MASTER; - Lex->mi.log_file_name = $8.str; - Lex->mi.pos = $12; - Lex->mi.server_id = $16; - } | master_or_binary LOGS_SYM { Lex->sql_command = SQLCOM_SHOW_BINLOGS; From d1192eb8ee11536b9b042f0ced1daa2bab25e68c Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 16 Feb 2011 18:19:10 +0100 Subject: [PATCH 36/64] Followup to Bug #11752069 (former bug 43152) Assertion `bitmap_is_set_all(&table->s->all_set)' failed in handler::ha_reset This followup fixes the compilation warning 'test_bit' may be used uninitialized in this function introduced by the previous patch. --- unittest/mysys/bitmap-t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/mysys/bitmap-t.c b/unittest/mysys/bitmap-t.c index d5c1791ca14..d0df9fbb040 100644 --- a/unittest/mysys/bitmap-t.c +++ b/unittest/mysys/bitmap-t.c @@ -253,7 +253,7 @@ error2: my_bool test_get_first_bit(MY_BITMAP *map, uint bitsize) { - uint i, test_bit; + uint i, test_bit= 0; uint no_loops= bitsize > 128 ? 128 : bitsize; bitmap_set_all(map); From 378ddd475fcaf66c1fbe87209059159f1eb4401e Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 16 Feb 2011 21:43:12 +0100 Subject: [PATCH 37/64] Simple fix of suppress pattern in test insert_select --- mysql-test/r/insert_select.result | 2 +- mysql-test/t/insert_select.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/insert_select.result b/mysql-test/r/insert_select.result index f12c9a0a61a..708c44f07a9 100644 --- a/mysql-test/r/insert_select.result +++ b/mysql-test/r/insert_select.result @@ -841,7 +841,7 @@ SET max_heap_table_size = 16384; SET @old_myisam_data_pointer_size = @@myisam_data_pointer_size; SET GLOBAL myisam_data_pointer_size = 2; INSERT INTO t1 VALUES (1), (2), (3), (4), (5); -call mtr.add_suppression("mysqld: The table '.*#sql.*' is full"); +call mtr.add_suppression("mysqld.*: The table '.*#sql.*' is full"); INSERT IGNORE INTO t1 SELECT t1.a FROM t1,t1 t2,t1 t3,t1 t4,t1 t5,t1 t6,t1 t7; Got one of the listed errors SET GLOBAL myisam_data_pointer_size = @old_myisam_data_pointer_size; diff --git a/mysql-test/t/insert_select.test b/mysql-test/t/insert_select.test index 7318e45889a..d7fe816bec7 100644 --- a/mysql-test/t/insert_select.test +++ b/mysql-test/t/insert_select.test @@ -407,7 +407,7 @@ SET GLOBAL myisam_data_pointer_size = 2; INSERT INTO t1 VALUES (1), (2), (3), (4), (5); -call mtr.add_suppression("mysqld: The table '.*#sql.*' is full"); +call mtr.add_suppression("mysqld.*: The table '.*#sql.*' is full"); --error ER_RECORD_FILE_FULL,ER_RECORD_FILE_FULL INSERT IGNORE INTO t1 SELECT t1.a FROM t1,t1 t2,t1 t3,t1 t4,t1 t5,t1 t6,t1 t7; From edcdc79e5a2d7d81efd3f7856c8f2eb28bcbc803 Mon Sep 17 00:00:00 2001 From: Jonathan Perkin Date: Wed, 16 Feb 2011 14:42:44 -0800 Subject: [PATCH 38/64] Updated README file. --- README | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README b/README index 5463de4a7e8..2e18fb55a22 100644 --- a/README +++ b/README @@ -3,18 +3,29 @@ MySQL Server This is a release of MySQL, a dual-license SQL database server. For the avoidance of doubt, this particular copy of the software is released under the version 2 of the GNU General Public License. -MySQL is brought to you by the MySQL team at Oracle. +MySQL is brought to you by Oracle. -Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. License information can be found in the COPYING file. +MySQL FOSS License Exception +We want free and open source software applications under certain +licenses to be able to use specified GPL-licensed MySQL client +libraries despite the fact that not all such FOSS licenses are +compatible with version 2 of the GNU General Public License. +Therefore there are special exceptions to the terms and conditions +of the GPLv2 as applied to these client libraries, which are +identified and described in more detail in the FOSS License +Exception at +. + This distribution may include materials developed by third parties. For license and attribution notices for these materials, please refer to the documentation that accompanies -this distribution (see the Licenses for Third-Party Components -appendix). A copy of the license/notices is also reproduced -below. +this distribution (see the "Licenses for Third-Party Components" +appendix) or view the online documentation at +. GPLv2 Disclaimer For the avoidance of doubt, except that if any license choice @@ -38,8 +49,6 @@ Some Reference Manual sections of special interest: chapter. - For the new features/bugfix history, see the MySQL Change History appendix. -- For currently known bugs, see the Errors and Common Problems - appendix. You can browse the MySQL Reference Manual online or download it in any of several formats at the URL given earlier in this file. From 26077d8dcb44af9e0512a684200c0350d47764fc Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Thu, 17 Feb 2011 08:27:24 +0100 Subject: [PATCH 39/64] Disable this test when running 'mtr --mem' With --mem if fails with +UNEXPECTED ERROR NUMBER: 1290 In var/log/mysqld.2.err we have: [ERROR] LOAD DATA INFILE in the slave SQL Thread can only read from --slave-load-tmpdir. Please, report a bug. [ERROR] Slave SQL: Error 'The MySQL server is running with the --slave-load-tmpdir option so it cannot execute this statement' on query. Default database: 'test'. Query: 'LOAD DATA INFILE '../../tmp/SQL_LOAD-2-1-1.data' INTO TABLE `t1` FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\' LINES TERMINATED BY '\n' (`a`, `b`)', Error_code: 1290 getcwd() in the server yields something like: /dev/shm/var_auto_iv5Q/mysqld.2/data --- mysql-test/suite/rpl/t/rpl_slave_load_remove_tmpfile.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/rpl/t/rpl_slave_load_remove_tmpfile.test b/mysql-test/suite/rpl/t/rpl_slave_load_remove_tmpfile.test index 1e2850fada2..41709c87f40 100644 --- a/mysql-test/suite/rpl/t/rpl_slave_load_remove_tmpfile.test +++ b/mysql-test/suite/rpl/t/rpl_slave_load_remove_tmpfile.test @@ -13,6 +13,7 @@ --source include/have_debug.inc --source include/master-slave.inc --source include/not_embedded.inc +--source include/not_var_link.inc ########################################################################## # Loading data From e428f0ed9dfbe185f13e5db6f4b56de5ba5ff5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 17 Feb 2011 09:45:07 +0200 Subject: [PATCH 40/64] Disable the Bug #60049 test on embedded, as it requires server restart. --- mysql-test/suite/innodb/t/innodb_bug60049.test | 1 + mysql-test/suite/innodb_plugin/t/innodb_bug60049.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_bug60049.test b/mysql-test/suite/innodb/t/innodb_bug60049.test index cff1c3dc09d..b35fb12cc5e 100644 --- a/mysql-test/suite/innodb/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb/t/innodb_bug60049.test @@ -2,6 +2,7 @@ # This test requires a fresh server start-up and a slow shutdown. # This was a suspected bug (not a bug). +-- source include/not_embedded.inc -- source include/have_innodb.inc CREATE TABLE t(a INT)ENGINE=InnoDB; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test index b3557243235..8d6c38ff9ef 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test @@ -2,6 +2,7 @@ # This test requires a fresh server start-up and a slow shutdown. # This was a suspected bug (not a bug). +-- source include/not_embedded.inc -- source include/have_innodb_plugin.inc CREATE TABLE t(a INT)ENGINE=InnoDB; From f2a42aee2e42edb95fddbdcba7b11f6967221de1 Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Thu, 17 Feb 2011 12:43:53 +0100 Subject: [PATCH 41/64] Bug#48053 String::c_ptr has a race and/or does an invalid memory reference There are two issues present here. 1) There is a possibility that we test a byte beyond the allocated buffer 2) We compare a byte that might never have been initalized to see if it's 0. The first issue is not triggered by existing code, but an ASSERT has been added to safe-guard against introducing new code that triggers it. The second issue is what triggers the Valgrind warnings reported in the bug report. A buffer is allocated in class String to hold the value. This buffer is populated by the character data constituting the string, but is not zero-terminated in most cases. Testing if it is indeed zero-terminated means that we check a byte that has never been explicitly set, thus causing Valgrind to trigger. Note that issue 2 is not a serious problem. The variable is read, and if it's not zero, we will set it to zero. There are no further consequences. Note that this patch does not fix the underlying problems with issue 1, as it is deemed too risky to fix at this point (as noted in the bug report). As discussed in the report, the c_ptr() method should probably be replaced, but this requires a thorough analysis of the ~200 calls to the method. --- mysql-test/r/ctype_cp1250_ch.result | 3 +++ mysql-test/r/ctype_cp1251.result | 2 ++ mysql-test/r/ctype_eucjpms.result | 2 ++ mysql-test/t/ctype_cp1250_ch.test | 10 ++++++++++ mysql-test/t/ctype_cp1251.test | 10 ++++++++++ mysql-test/t/ctype_eucjpms.test | 8 ++++++++ sql/set_var.cc | 4 ++-- sql/sql_string.h | 3 +++ 8 files changed, 40 insertions(+), 2 deletions(-) mode change 100755 => 100644 mysql-test/r/ctype_eucjpms.result diff --git a/mysql-test/r/ctype_cp1250_ch.result b/mysql-test/r/ctype_cp1250_ch.result index 7f0cdf3f17b..46ca1f25ef4 100644 --- a/mysql-test/r/ctype_cp1250_ch.result +++ b/mysql-test/r/ctype_cp1250_ch.result @@ -238,3 +238,6 @@ select a from t1 where a like "abcdefgh a abcdefghá drop table t1; +set global LC_MESSAGES=convert((@@global.log_bin_trust_function_creators) +using cp1250); +ERROR HY000: Unknown system variable 'LC_MESSAGES' diff --git a/mysql-test/r/ctype_cp1251.result b/mysql-test/r/ctype_cp1251.result index dc12f9ceb03..2e91ecb7bc0 100644 --- a/mysql-test/r/ctype_cp1251.result +++ b/mysql-test/r/ctype_cp1251.result @@ -375,6 +375,8 @@ FD FD FD D18D FD FE FE FE D18E FE FF FF FF D18F FF DROP TABLE t1; +set global LC_TIME_NAMES=convert((-8388608) using cp1251); +ERROR HY000: Unknown locale: '-8388608' # # End of 5.1 tests # diff --git a/mysql-test/r/ctype_eucjpms.result b/mysql-test/r/ctype_eucjpms.result old mode 100755 new mode 100644 index 21aa38b7fe6..21109f596c1 --- a/mysql-test/r/ctype_eucjpms.result +++ b/mysql-test/r/ctype_eucjpms.result @@ -9859,3 +9859,5 @@ hex(convert(_eucjpms 0xA5FE41 using ucs2)) select hex(convert(_eucjpms 0x8FABF841 using ucs2)); hex(convert(_eucjpms 0x8FABF841 using ucs2)) 003F0041 +set global LC_TIME_NAMES=convert((convert((0x63) using eucjpms)) using utf8); +ERROR HY000: Unknown locale: 'c' diff --git a/mysql-test/t/ctype_cp1250_ch.test b/mysql-test/t/ctype_cp1250_ch.test index 1fb656f2a01..3e17ee52164 100644 --- a/mysql-test/t/ctype_cp1250_ch.test +++ b/mysql-test/t/ctype_cp1250_ch.test @@ -72,3 +72,13 @@ select a from t1 where a like "abcdefgh drop table t1; # End of 4.1 tests + +# +# Bug #48053 String::c_ptr has a race and/or does an invalid +# memory reference +# (triggered by Valgrind tests) +# (see also ctype_eucjpms.test, ctype_cp1250.test, ctype_cp1251.test) +# +--error 1193 +set global LC_MESSAGES=convert((@@global.log_bin_trust_function_creators) + using cp1250); diff --git a/mysql-test/t/ctype_cp1251.test b/mysql-test/t/ctype_cp1251.test index 2331c731061..bde72d04ba7 100644 --- a/mysql-test/t/ctype_cp1251.test +++ b/mysql-test/t/ctype_cp1251.test @@ -55,6 +55,16 @@ drop table t1; --source include/ctype_8bit.inc +# +# Bug #48053 String::c_ptr has a race and/or does an invalid +# memory reference +# (triggered by Valgrind tests) +# (see also ctype_eucjpms.test, ctype_cp1250.test, ctype_cp1251.test) +# +--error 1105 +set global LC_TIME_NAMES=convert((-8388608) using cp1251); + + --echo # --echo # End of 5.1 tests --echo # diff --git a/mysql-test/t/ctype_eucjpms.test b/mysql-test/t/ctype_eucjpms.test index ec358d94900..165cfba897a 100644 --- a/mysql-test/t/ctype_eucjpms.test +++ b/mysql-test/t/ctype_eucjpms.test @@ -381,3 +381,11 @@ select hex(convert(_eucjpms 0xA5FE41 using ucs2)); # the next character, which is a single byte character 0x41. select hex(convert(_eucjpms 0x8FABF841 using ucs2)); +# +# Bug #48053 String::c_ptr has a race and/or does an invalid +# memory reference +# (triggered by Valgrind tests) +# (see also ctype_eucjpms.test, ctype_cp1250.test, ctype_cp1251.test) +# +--error 1105 +set global LC_TIME_NAMES=convert((convert((0x63) using eucjpms)) using utf8); diff --git a/sql/set_var.cc b/sql/set_var.cc index d297be3fc10..26c9b06a912 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1828,7 +1828,7 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) } var->save_result.ulong_value= ((ulong) - find_set(enum_names, res->c_ptr(), + find_set(enum_names, res->c_ptr_safe(), res->length(), NULL, &error, &error_len, @@ -2941,7 +2941,7 @@ bool sys_var_thd_lc_time_names::check(THD *thd, set_var *var) my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL"); return 1; } - const char *locale_str= res->c_ptr(); + const char *locale_str= res->c_ptr_safe(); if (!(locale_match= my_locale_by_name(locale_str))) { my_printf_error(ER_UNKNOWN_ERROR, diff --git a/sql/sql_string.h b/sql/sql_string.h index 092e194646f..c56c69493d4 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -106,6 +106,9 @@ public: inline const char *ptr() const { return Ptr; } inline char *c_ptr() { + DBUG_ASSERT(!alloced || !Ptr || !Alloced_length || + (Alloced_length >= (str_length + 1))); + if (!Ptr || Ptr[str_length]) /* Should be safe */ (void) realloc(str_length); return Ptr; From 5e7d3543261ad31247adaa920f0f8e4111529d59 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 17 Feb 2011 19:09:53 +0200 Subject: [PATCH 42/64] Fix the non-determinism in innodb_information_schema.test Thanks to Kristian Nielsen for finding out the root cause for the failure, see: https://bugs.launchpad.net/maria/+bug/677407 --- .../t/innodb_information_schema.test | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/innodb_plugin/t/innodb_information_schema.test b/mysql-test/suite/innodb_plugin/t/innodb_information_schema.test index 25255e0b2a9..20c25015c56 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_information_schema.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_information_schema.test @@ -116,11 +116,29 @@ SELECT * FROM ```t'\"_str` WHERE c1 = '4' FOR UPDATE; # executes before some of them, resulting in less than expected number # of rows being selected from innodb_locks. If there is a bug and there # are no 14 rows in innodb_locks then this test will fail with timeout. -let $count = 14; -let $table = INFORMATION_SCHEMA.INNODB_LOCKS; --- source include/wait_until_rows_count.inc -# the above enables the query log, re-disable it --- disable_query_log +# Notice that if we query INNODB_LOCKS more often than once per 0.1 sec +# then its contents will never change because the cache from which it is +# filled is updated only if it has not been read for 0.1 seconds. See +# CACHE_MIN_IDLE_TIME_US in trx/trx0i_s.c. +let $cnt=10; +while ($cnt) +{ + let $success=`SELECT COUNT(*) = 14 FROM INFORMATION_SCHEMA.INNODB_LOCKS`; + if ($success) + { + let $cnt=0; + } + if (!$success) + { + real_sleep 0.2; + dec $cnt; + } +} +if (!$success) +{ + -- echo Timeout waiting for rows in INNODB_LOCKS to appear +} + SELECT lock_mode, lock_type, lock_table, lock_index, lock_rec, lock_data FROM INFORMATION_SCHEMA.INNODB_LOCKS ORDER BY lock_data; From db55cf85267001e495b91a30c0004679deaf07a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 17 Feb 2011 22:25:33 +0200 Subject: [PATCH 43/64] Allow 30 seconds for slow shutdown in the Bug #60049 test. --- mysql-test/suite/innodb/t/innodb_bug60049.test | 2 +- mysql-test/suite/innodb_plugin/t/innodb_bug60049.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/innodb/t/innodb_bug60049.test b/mysql-test/suite/innodb/t/innodb_bug60049.test index b35fb12cc5e..ec4e3b8de7e 100644 --- a/mysql-test/suite/innodb/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb/t/innodb_bug60049.test @@ -13,7 +13,7 @@ let $MYSQLD_DATADIR=`select @@datadir`; # Shut down the server -- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- shutdown_server 10 +-- shutdown_server 30 -- source include/wait_until_disconnected.inc # Check the tail of ID_IND (SYS_TABLES.ID) diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test index 8d6c38ff9ef..0423f5d3635 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test @@ -13,7 +13,7 @@ let $MYSQLD_DATADIR=`select @@datadir`; # Shut down the server -- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- shutdown_server 10 +-- shutdown_server 30 -- source include/wait_until_disconnected.inc # Check the tail of ID_IND (SYS_TABLES.ID) From 498ff4468d67499eb35e5c7d0014f935523e0158 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 18 Feb 2011 10:32:40 +0300 Subject: [PATCH 44/64] Bug#60101 COALESCE with cp1251 tables causes [Err] 1267 - Illegal mix of collations Problem: IF() did not copy collation derivation and repertoire from an argument if the opposite argument was NULL: IF(cond, res1, NULL) IF(cond, NULL, res2) only CHARSET_INFO pointer was copied. This resulted in illegal mix of collations error. Fix: copy all collation parameters from the non-NULL argument: CHARSET_INFO pointer, derivation, repertoire. --- mysql-test/r/ctype_cp1251.result | 15 +++++++++++++++ mysql-test/t/ctype_cp1251.test | 10 ++++++++++ sql/item_cmpfunc.cc | 4 ++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/ctype_cp1251.result b/mysql-test/r/ctype_cp1251.result index 202dfe2b7df..24604d9d5fc 100644 --- a/mysql-test/r/ctype_cp1251.result +++ b/mysql-test/r/ctype_cp1251.result @@ -3219,5 +3219,20 @@ maketime(`a`,`a`,`a`) DROP TABLE t1; SET sql_mode=default; # +# Bug#60101 COALESCE with cp1251 tables causes [Err] 1267 - Illegal mix of collations +# +CREATE TABLE t1 (test1 INT, test2 VARCHAR(255)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `test1` int(11) DEFAULT NULL, + `test2` varchar(255) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT COALESCE(IF(test1=1, 1, NULL), test2) FROM t1; +COALESCE(IF(test1=1, 1, NULL), test2) +SELECT COALESCE(IF(test1=1, NULL, 1), test2) FROM t1; +COALESCE(IF(test1=1, NULL, 1), test2) +DROP TABLE t1; +# # End of 5.5 tests # diff --git a/mysql-test/t/ctype_cp1251.test b/mysql-test/t/ctype_cp1251.test index 7ffb60374e2..aae50944456 100644 --- a/mysql-test/t/ctype_cp1251.test +++ b/mysql-test/t/ctype_cp1251.test @@ -85,6 +85,16 @@ set global LC_TIME_NAMES=convert((-8388608) using cp1251); --source include/ctype_numconv.inc +--echo # +--echo # Bug#60101 COALESCE with cp1251 tables causes [Err] 1267 - Illegal mix of collations +--echo # +CREATE TABLE t1 (test1 INT, test2 VARCHAR(255)); +SHOW CREATE TABLE t1; +SELECT COALESCE(IF(test1=1, 1, NULL), test2) FROM t1; +SELECT COALESCE(IF(test1=1, NULL, 1), test2) FROM t1; +DROP TABLE t1; + + --echo # --echo # End of 5.5 tests --echo # diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 67635c73b43..df541f603ee 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2640,7 +2640,7 @@ Item_func_if::fix_length_and_dec() if (null1) { cached_result_type= arg2_type; - collation.set(args[2]->collation.collation); + collation.set(args[2]->collation); cached_field_type= args[2]->field_type(); max_length= args[2]->max_length; return; @@ -2649,7 +2649,7 @@ Item_func_if::fix_length_and_dec() if (null2) { cached_result_type= arg1_type; - collation.set(args[1]->collation.collation); + collation.set(args[1]->collation); cached_field_type= args[1]->field_type(); max_length= args[1]->max_length; return; From 4daaa0288002cce90c1a3655e3740b100a3f761b Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Fri, 18 Feb 2011 11:10:30 +0300 Subject: [PATCH 45/64] BUG#11766720 - setting storage engine to null segfaults mysqld MONTHNAME(0) claims that it is about to return NOT NULL value, whereas it actually returns NULL. As a result storage_engine variable (which cannot be NULL) protection was bypassed and NULL value was accepted, causing server crash. Fixed MONTHNAME(0) to report valid NULL flag. --- mysql-test/r/func_time.result | 10 +++++++++- mysql-test/t/func_time.test | 7 +++++++ sql/item_timefunc.cc | 13 +++++-------- sql/item_timefunc.h | 9 ++++++--- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 2a95b234548..bbc86ddeda0 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -136,7 +136,7 @@ dayname("1962-03-03") dayname("1962-03-03")+0 Saturday 5 select monthname("1972-03-04"),monthname("1972-03-04")+0; monthname("1972-03-04") monthname("1972-03-04")+0 -March 3 +March 0 select time_format(19980131000000,'%H|%I|%k|%l|%i|%p|%r|%S|%T'); time_format(19980131000000,'%H|%I|%k|%l|%i|%p|%r|%S|%T') 00|12|0|12|00|AM|12:00:00 AM|00|00:00:00 @@ -1368,3 +1368,11 @@ SELECT SUBDATE(STR_TO_DATE(NULL,0), INTERVAL 1 HOUR); SUBDATE(STR_TO_DATE(NULL,0), INTERVAL 1 HOUR) NULL # +# BUG#59895 - setting storage engine to null segfaults mysqld +# +SELECT MONTHNAME(0), MONTHNAME(0) IS NULL, MONTHNAME(0) + 1; +MONTHNAME(0) MONTHNAME(0) IS NULL MONTHNAME(0) + 1 +NULL 1 NULL +SET storage_engine=NULL; +ERROR 42000: Variable 'storage_engine' can't be set to the value of 'NULL' +# diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index eaa592c2ad5..f53d90cef96 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -881,4 +881,11 @@ SELECT WEEK(STR_TO_DATE(NULL,0)); SELECT SUBDATE(STR_TO_DATE(NULL,0), INTERVAL 1 HOUR); --echo # +--echo # BUG#59895 - setting storage engine to null segfaults mysqld +--echo # +SELECT MONTHNAME(0), MONTHNAME(0) IS NULL, MONTHNAME(0) + 1; +--error ER_WRONG_VALUE_FOR_VAR +SET storage_engine=NULL; + +--echo # diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 3d587ace335..99467b7c3fc 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1133,16 +1133,13 @@ String* Item_func_monthname::val_str(String* str) { DBUG_ASSERT(fixed == 1); const char *month_name; - uint month= (uint) val_int(); uint err; + MYSQL_TIME ltime; - if (null_value || !month) - { - null_value=1; - return (String*) 0; - } - null_value=0; - month_name= locale->month_names->type_names[month-1]; + if ((null_value= (get_arg0_date(<ime, TIME_FUZZY_DATE) || !ltime.month))) + return (String *) 0; + + month_name= locale->month_names->type_names[ltime.month - 1]; str->copy(month_name, (uint) strlen(month_name), &my_charset_utf8_bin, collation.collation, &err); return str; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 9732e8dc360..92e24d75111 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -163,16 +163,19 @@ public: }; -class Item_func_monthname :public Item_func_month +class Item_func_monthname :public Item_str_func { MY_LOCALE *locale; public: - Item_func_monthname(Item *a) :Item_func_month(a) {} + Item_func_monthname(Item *a) :Item_str_func(a) {} const char *func_name() const { return "monthname"; } String *val_str(String *str); - enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return TRUE;} + bool check_valid_arguments_processor(uchar *int_arg) + { + return !has_date_args(); + } }; From b97cdadbe82b35473ff7decda6b1ce711e3501c4 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 18 Feb 2011 12:46:55 +0300 Subject: [PATCH 46/64] Bug#11765108 (Bug#58036) client utf32, utf16, ucs2 should be disallowed, they crash server Problem: ucs2 was correctly disallowed in "SET NAMES" only, while mysql_real_connect() and mysql_change_user() still allowed to use ucs2, which made server crash. Fix: disallow ucs2 in mysql_real_connect() and mysql_change_user(). @ sql/set_var.cc Using new function. @ sql/sql_acl.cc - Return error if character set initialization failed - Getting rid of pointer aliasing: Initialize user_name to NULL, to avoid double free(). @ sql/sql_connect.cc - in case of unsupported client character set send error and return true - in case of success return false @ sql/sql_connect.h - changing return type for thd_init_client_charset() to bool, to return errors to the caller @ sql/sql_parse.h - introducing a new function, to reuse in all places where we need to check client character set. @ tests/mysql_client_test.c Adding test --- sql/set_var.cc | 2 +- sql/sql_acl.cc | 8 ++--- sql/sql_connect.cc | 31 +++++++++++++++--- sql/sql_connect.h | 2 +- sql/sql_parse.h | 6 ++++ tests/mysql_client_test.c | 67 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 10 deletions(-) diff --git a/sql/set_var.cc b/sql/set_var.cc index 27865aee3c2..f31fe6a351d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -776,7 +776,7 @@ int set_var_password::update(THD *thd) int set_var_collation_client::check(THD *thd) { /* Currently, UCS-2 cannot be used as a client character set */ - if (character_set_client->mbminlen > 1) + if (!is_supported_parser_charset(character_set_client)) { my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client", character_set_client->csname); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 427e2eb7346..9c57b3c102d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -7799,7 +7799,8 @@ public: Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {} bool init_client_charset(uint cs_number) { - thd_init_client_charset(thd, cs_number); + if (thd_init_client_charset(thd, cs_number)) + return true; thd->update_charset(); return thd->is_error(); } @@ -8929,9 +8930,8 @@ server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio, uint connect_errors, mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip; mpvio->auth_info.host_or_ip_length= (unsigned int) strlen(thd->security_ctx->host_or_ip); - mpvio->auth_info.user_name= thd->security_ctx->user; - mpvio->auth_info.user_name_length= thd->security_ctx->user ? - (unsigned int) strlen(thd->security_ctx->user) : 0; + mpvio->auth_info.user_name= NULL; + mpvio->auth_info.user_name_length= 0; mpvio->connect_errors= connect_errors; mpvio->status= MPVIO_EXT::FAILURE; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index ad6fe492056..9d7e20c1d6e 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -370,8 +370,23 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) } -void thd_init_client_charset(THD *thd, uint cs_number) +/** + Set thread character set variables from the given ID + + @param thd thread handle + @param cs_number character set and collation ID + + @retval 0 OK; character_set_client, collation_connection and + character_set_results are set to the new value, + or to the default global values. + + @retval 1 error, e.g. the given ID is not supported by parser. + Corresponding SQL error is sent. +*/ + +bool thd_init_client_charset(THD *thd, uint cs_number) { + CHARSET_INFO *cs; /* Use server character set and collation if - opt_character_set_client_handshake is not set @@ -380,10 +395,10 @@ void thd_init_client_charset(THD *thd, uint cs_number) - client character set doesn't exists in server */ if (!opt_character_set_client_handshake || - !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) || + !(cs= get_charset(cs_number, MYF(0))) || !my_strcasecmp(&my_charset_latin1, global_system_variables.character_set_client->name, - thd->variables.character_set_client->name)) + cs->name)) { thd->variables.character_set_client= global_system_variables.character_set_client; @@ -394,10 +409,18 @@ void thd_init_client_charset(THD *thd, uint cs_number) } else { + if (!is_supported_parser_charset(cs)) + { + /* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */ + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client", + cs->csname); + return true; + } thd->variables.character_set_results= thd->variables.collation_connection= - thd->variables.character_set_client; + thd->variables.character_set_client= cs; } + return false; } diff --git a/sql/sql_connect.h b/sql/sql_connect.h index 666db4c6462..ff5a20dfbc2 100644 --- a/sql/sql_connect.h +++ b/sql/sql_connect.h @@ -33,7 +33,7 @@ void reset_mqh(LEX_USER *lu, bool get_them); bool check_mqh(THD *thd, uint check_command); void time_out_user_resource_limits(THD *thd, USER_CONN *uc); void decrease_user_connections(USER_CONN *uc); -void thd_init_client_charset(THD *thd, uint cs_number); +bool thd_init_client_charset(THD *thd, uint cs_number); bool setup_connection_thread_globals(THD *thd); int check_user(THD *thd, enum enum_server_command command, diff --git a/sql/sql_parse.h b/sql/sql_parse.h index c9b0f981d19..9eea07401a2 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -197,4 +197,10 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, bool check_global_access(THD *thd, ulong want_access); +inline bool is_supported_parser_charset(CHARSET_INFO *cs) +{ + return test(cs->mbminlen == 1); +} + + #endif /* SQL_PARSE_INCLUDED */ diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 3861a3e1cf8..fa9e7c670ec 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -19288,6 +19288,72 @@ static void test_bug47485() } +/* + Bug#58036 client utf32, utf16, ucs2 should be disallowed, they crash server +*/ +static void test_bug58036() +{ + MYSQL *conn; + DBUG_ENTER("test_bug47485"); + myheader("test_bug58036"); + + /* Part1: try to connect with ucs2 client character set */ + conn= mysql_client_init(NULL); + mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2"); + if (mysql_real_connect(conn, opt_host, opt_user, + opt_password, opt_db ? opt_db : "test", + opt_port, opt_unix_socket, 0)) + { + if (!opt_silent) + printf("mysql_real_connect() succeeded (failure expected)\n"); + mysql_close(conn); + DIE(); + } + + if (!opt_silent) + printf("Got mysql_real_connect() error (expected): %s (%d)\n", + mysql_error(conn), mysql_errno(conn)); + DIE_UNLESS(mysql_errno(conn) == ER_WRONG_VALUE_FOR_VAR); + mysql_close(conn); + + + /* + Part2: + - connect with latin1 + - then change client character set to ucs2 + - then try mysql_change_user() + */ + conn= mysql_client_init(NULL); + mysql_options(conn, MYSQL_SET_CHARSET_NAME, "latin1"); + if (!mysql_real_connect(conn, opt_host, opt_user, + opt_password, opt_db ? opt_db : "test", + opt_port, opt_unix_socket, 0)) + { + if (!opt_silent) + printf("mysql_real_connect() failed: %s (%d)\n", + mysql_error(conn), mysql_errno(conn)); + mysql_close(conn); + DIE(); + } + + mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2"); + if (!mysql_change_user(conn, opt_user, opt_password, NULL)) + { + if (!opt_silent) + printf("mysql_change_user() succedded, error expected!"); + mysql_close(conn); + DIE(); + } + + if (!opt_silent) + printf("Got mysql_change_user() error (expected): %s (%d)\n", + mysql_error(conn), mysql_errno(conn)); + mysql_close(conn); + + DBUG_VOID_RETURN; +} + + /* Bug#49972: Crash in prepared statements. @@ -19770,6 +19836,7 @@ static struct my_tests_st my_tests[]= { { "test_bug42373", test_bug42373 }, { "test_bug54041", test_bug54041 }, { "test_bug47485", test_bug47485 }, + { "test_bug58036", test_bug58036 }, { "test_bug57058", test_bug57058 }, { 0, 0 } }; From cd4c263dc410d4ffd86152776bb2ebf073c940f7 Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Fri, 18 Feb 2011 11:50:06 +0100 Subject: [PATCH 47/64] Bug#11766675 - 59839: Aggregation followed by subquery yields wrong result The loop that was looping over subqueries' references to outer field used a local boolean variable to tell whether the field was grouped or not. But the implementor failed to reset the variable after each iteration. Thus a field that was not directly aggregated appeared to be. Fixed by resetting the variable upon each new iteration. --- mysql-test/r/group_by.result | 36 ++++++++++++++++++++ mysql-test/t/group_by.test | 37 ++++++++++++++++++++ sql/sql_select.cc | 66 +++++++++++++++++++----------------- 3 files changed, 108 insertions(+), 31 deletions(-) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 83f1f220023..1dfb0f5860a 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -1855,4 +1855,40 @@ ON 1 WHERE t2.f1 > 1 GROUP BY t2.f1; COUNT(*) 2 DROP TABLE t1; +# +# Bug#59839: Aggregation followed by subquery yields wrong result +# +CREATE TABLE t1 ( +a INT, +b INT, +c INT, +KEY (a, b) +); +INSERT INTO t1 VALUES +( 1, 1, 1 ), +( 1, 2, 2 ), +( 1, 3, 3 ), +( 1, 4, 6 ), +( 1, 5, 5 ), +( 1, 9, 13 ), +( 2, 1, 6 ), +( 2, 2, 7 ), +( 2, 3, 8 ); +EXPLAIN +SELECT a, AVG(t1.b), +(SELECT t11.c FROM t1 t11 WHERE t11.a = t1.a AND t11.b = AVG(t1.b)) AS t11c, +(SELECT t12.c FROM t1 t12 WHERE t12.a = t1.a AND t12.b = AVG(t1.b)) AS t12c +FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index NULL a 10 NULL 9 Using index +3 DEPENDENT SUBQUERY t12 ref a a 10 func,func 2 Using where +2 DEPENDENT SUBQUERY t11 ref a a 10 func,func 2 Using where +SELECT a, AVG(t1.b), +(SELECT t11.c FROM t1 t11 WHERE t11.a = t1.a AND t11.b = AVG(t1.b)) AS t11c, +(SELECT t12.c FROM t1 t12 WHERE t12.a = t1.a AND t12.b = AVG(t1.b)) AS t12c +FROM t1 GROUP BY a; +a AVG(t1.b) t11c t12c +1 4.0000 6 6 +2 2.0000 7 7 +DROP TABLE t1; # End of 5.1 tests diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index 580c2e5091c..1a4b9a3bab7 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -1247,4 +1247,41 @@ ON 1 WHERE t2.f1 > 1 GROUP BY t2.f1; DROP TABLE t1; +--echo # +--echo # Bug#59839: Aggregation followed by subquery yields wrong result +--echo # + +CREATE TABLE t1 ( + a INT, + b INT, + c INT, + KEY (a, b) +); + +INSERT INTO t1 VALUES + ( 1, 1, 1 ), + ( 1, 2, 2 ), + ( 1, 3, 3 ), + ( 1, 4, 6 ), + ( 1, 5, 5 ), + ( 1, 9, 13 ), + + ( 2, 1, 6 ), + ( 2, 2, 7 ), + ( 2, 3, 8 ); + +EXPLAIN +SELECT a, AVG(t1.b), +(SELECT t11.c FROM t1 t11 WHERE t11.a = t1.a AND t11.b = AVG(t1.b)) AS t11c, +(SELECT t12.c FROM t1 t12 WHERE t12.a = t1.a AND t12.b = AVG(t1.b)) AS t12c +FROM t1 GROUP BY a; + +SELECT a, AVG(t1.b), +(SELECT t11.c FROM t1 t11 WHERE t11.a = t1.a AND t11.b = AVG(t1.b)) AS t11c, +(SELECT t12.c FROM t1 t12 WHERE t12.a = t1.a AND t12.b = AVG(t1.b)) AS t12c +FROM t1 GROUP BY a; + +DROP TABLE t1; + + --echo # End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 869fd01ac60..eb2559fc600 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -278,61 +278,65 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, } -/* +/** Fix fields referenced from inner selects. - SYNOPSIS - fix_inner_refs() - thd Thread handle - all_fields List of all fields used in select - select Current select - ref_pointer_array Array of references to Items used in current select - group_list GROUP BY list (is NULL by default) + @param thd Thread handle + @param all_fields List of all fields used in select + @param select Current select + @param ref_pointer_array Array of references to Items used in current select + @param group_list GROUP BY list (is NULL by default) - DESCRIPTION - The function serves 3 purposes - adds fields referenced from inner - selects to the current select list, resolves which class to use - to access referenced item (Item_ref of Item_direct_ref) and fixes - references (Item_ref objects) to these fields. + @details + The function serves 3 purposes - If a field isn't already in the select list and the ref_pointer_array + - adds fields referenced from inner query blocks to the current select list + + - Decides which class to use to reference the items (Item_ref or + Item_direct_ref) + + - fixes references (Item_ref objects) to these fields. + + If a field isn't already on the select list and the ref_pointer_array is provided then it is added to the all_fields list and the pointer to it is saved in the ref_pointer_array. The class to access the outer field is determined by the following rules: - 1. If the outer field isn't used under an aggregate function - then the Item_ref class should be used. - 2. If the outer field is used under an aggregate function and this - function is aggregated in the select where the outer field was - resolved or in some more inner select then the Item_direct_ref - class should be used. - Also it should be used if we are grouping by a subquery containing - the outer field. + + -#. If the outer field isn't used under an aggregate function then the + Item_ref class should be used. + + -#. If the outer field is used under an aggregate function and this + function is, in turn, aggregated in the query block where the outer + field was resolved or some query nested therein, then the + Item_direct_ref class should be used. Also it should be used if we are + grouping by a subquery containing the outer field. + The resolution is done here and not at the fix_fields() stage as - it can be done only after sum functions are fixed and pulled up to - selects where they are have to be aggregated. + it can be done only after aggregate functions are fixed and pulled up to + selects where they are to be aggregated. + When the class is chosen it substitutes the original field in the Item_outer_ref object. After this we proceed with fixing references (Item_outer_ref objects) to this field from inner subqueries. - RETURN - TRUE an error occured - FALSE ok -*/ + @return Status + @retval true An error occured. + @retval false OK. + */ bool fix_inner_refs(THD *thd, List &all_fields, SELECT_LEX *select, Item **ref_pointer_array, ORDER *group_list) { Item_outer_ref *ref; - bool res= FALSE; - bool direct_ref= FALSE; List_iterator ref_it(select->inner_refs_list); while ((ref= ref_it++)) { + bool direct_ref= false; Item *item= ref->outer_ref; Item **item_ref= ref->ref; Item_ref *new_ref; @@ -404,7 +408,7 @@ fix_inner_refs(THD *thd, List &all_fields, SELECT_LEX *select, return TRUE; thd->used_tables|= item->used_tables(); } - return res; + return false; } /** From 8a96012922865ecd1d87290869f6a54e74d3ee12 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 18 Feb 2011 16:12:36 +0300 Subject: [PATCH 48/64] Bug#11765108 (Bug#58036) client utf32, utf16, ucs2 should be disallowed, they crash server A separate fix for 5.1 (as 5.1 and 5.5 have seriously differged in the related pieces of the code). A patch for 5.5 was approved earlier. Problem: ucs2 was correctly disallowed in "SET NAMES" only, while mysql_real_connect() and mysql_change_user() still allowed to use ucs2, which made server crash. Fix: disallow ucs2 in mysql_real_connect() and mysql_change_user(). @ sql/sql_priv.h - changing return type for thd_init_client_charset() to bool, to return errors to the caller @ sql/sql_var.cc - using new function @ sql/sql_connect.cc - thd_client_charset_init: in case of unsupported client character set send error and return true; in case of success return false - check_connection: Return error if character set initialization failed @ sql/sql_parse.cc - check charset in the very beginnig of the CMD_CHANGE_USER handling code @ tests/mysql_client_test.c - adding tests --- sql/mysql_priv.h | 6 +++- sql/set_var.cc | 2 +- sql/sql_connect.cc | 34 +++++++++++++++++--- sql/sql_parse.cc | 17 ++++++++-- tests/mysql_client_test.c | 67 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 9 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1838f0c924d..67631b265ab 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1019,7 +1019,11 @@ void reset_mqh(LEX_USER *lu, bool get_them); bool check_mqh(THD *thd, uint check_command); void time_out_user_resource_limits(THD *thd, USER_CONN *uc); void decrease_user_connections(USER_CONN *uc); -void thd_init_client_charset(THD *thd, uint cs_number); +bool thd_init_client_charset(THD *thd, uint cs_number); +inline bool is_supported_parser_charset(CHARSET_INFO *cs) +{ + return test(cs->mbminlen == 1); +} bool setup_connection_thread_globals(THD *thd); int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); diff --git a/sql/set_var.cc b/sql/set_var.cc index 26c9b06a912..831b68bbe14 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2187,7 +2187,7 @@ bool sys_var_character_set_client::check(THD *thd, set_var *var) if (sys_var_character_set_sv::check(thd, var)) return 1; /* Currently, UCS-2 cannot be used as a client character set */ - if (var->save_result.charset->mbminlen > 1) + if (!is_supported_parser_charset(var->save_result.charset)) { my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, var->save_result.charset->csname); diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 9fa6966baa2..8129324e300 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -582,8 +582,23 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) } -void thd_init_client_charset(THD *thd, uint cs_number) +/** + Set thread character set variables from the given ID + + @param thd thread handle + @param cs_number character set and collation ID + + @retval 0 OK; character_set_client, collation_connection and + character_set_results are set to the new value, + or to the default global values. + + @retval 1 error, e.g. the given ID is not supported by parser. + Corresponding SQL error is sent. +*/ + +bool thd_init_client_charset(THD *thd, uint cs_number) { + CHARSET_INFO *cs; /* Use server character set and collation if - opt_character_set_client_handshake is not set @@ -592,10 +607,10 @@ void thd_init_client_charset(THD *thd, uint cs_number) - client character set doesn't exists in server */ if (!opt_character_set_client_handshake || - !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) || + !(cs= get_charset(cs_number, MYF(0))) || !my_strcasecmp(&my_charset_latin1, global_system_variables.character_set_client->name, - thd->variables.character_set_client->name)) + cs->name)) { thd->variables.character_set_client= global_system_variables.character_set_client; @@ -606,10 +621,18 @@ void thd_init_client_charset(THD *thd, uint cs_number) } else { + if (!is_supported_parser_charset(cs)) + { + /* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */ + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client", + cs->csname); + return true; + } thd->variables.character_set_results= thd->variables.collation_connection= - thd->variables.character_set_client; + thd->variables.character_set_client= cs; } + return false; } @@ -782,7 +805,8 @@ static int check_connection(THD *thd) thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; thd->max_client_packet_length= uint4korr(net->read_pos+4); DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); - thd_init_client_charset(thd, (uint) net->read_pos[8]); + if (thd_init_client_charset(thd, (uint) net->read_pos[8])) + return 1; thd->update_charset(); end= (char*) net->read_pos+32; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6ce800312ff..9bf55f4dcdd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1153,13 +1153,22 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (ptr < packet_end) { + CHARSET_INFO *cs; if (ptr + 2 > packet_end) { my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } - cs_number= uint2korr(ptr); + if ((cs_number= uint2korr(ptr)) && + (cs= get_charset(cs_number, MYF(0))) && + !is_supported_parser_charset(cs)) + { + /* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */ + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client", + cs->csname); + break; + } } /* Convert database name to utf8 */ @@ -1205,7 +1214,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (cs_number) { - thd_init_client_charset(thd, cs_number); + /* + We have checked charset earlier, + so thd_init_client_charset cannot fail. + */ + DBUG_ASSERT(!thd_init_client_charset(thd, cs_number)); thd->update_charset(); } } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 80c7be64e94..5a45c132a5f 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18398,6 +18398,72 @@ static void test_bug47485() } +/* + Bug#58036 client utf32, utf16, ucs2 should be disallowed, they crash server +*/ +static void test_bug58036() +{ + MYSQL *conn; + DBUG_ENTER("test_bug47485"); + myheader("test_bug58036"); + + /* Part1: try to connect with ucs2 client character set */ + conn= mysql_client_init(NULL); + mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2"); + if (mysql_real_connect(conn, opt_host, opt_user, + opt_password, opt_db ? opt_db : "test", + opt_port, opt_unix_socket, 0)) + { + if (!opt_silent) + printf("mysql_real_connect() succeeded (failure expected)\n"); + mysql_close(conn); + DIE(); + } + + if (!opt_silent) + printf("Got mysql_real_connect() error (expected): %s (%d)\n", + mysql_error(conn), mysql_errno(conn)); + DIE_UNLESS(mysql_errno(conn) == ER_WRONG_VALUE_FOR_VAR); + mysql_close(conn); + + + /* + Part2: + - connect with latin1 + - then change client character set to ucs2 + - then try mysql_change_user() + */ + conn= mysql_client_init(NULL); + mysql_options(conn, MYSQL_SET_CHARSET_NAME, "latin1"); + if (!mysql_real_connect(conn, opt_host, opt_user, + opt_password, opt_db ? opt_db : "test", + opt_port, opt_unix_socket, 0)) + { + if (!opt_silent) + printf("mysql_real_connect() failed: %s (%d)\n", + mysql_error(conn), mysql_errno(conn)); + mysql_close(conn); + DIE(); + } + + mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2"); + if (!mysql_change_user(conn, opt_user, opt_password, NULL)) + { + if (!opt_silent) + printf("mysql_change_user() succedded, error expected!"); + mysql_close(conn); + DIE(); + } + + if (!opt_silent) + printf("Got mysql_change_user() error (expected): %s (%d)\n", + mysql_error(conn), mysql_errno(conn)); + mysql_close(conn); + + DBUG_VOID_RETURN; +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -18724,6 +18790,7 @@ static struct my_tests_st my_tests[]= { { "test_bug42373", test_bug42373 }, { "test_bug54041", test_bug54041 }, { "test_bug47485", test_bug47485 }, + { "test_bug58036", test_bug58036 }, { 0, 0 } }; From cd3a8131c63b47be027ea8cbcdec42324679eb08 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 18 Feb 2011 17:17:37 +0300 Subject: [PATCH 49/64] A post-fix for b58036. --- tests/mysql_client_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 5a45c132a5f..fc1f2e8293e 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18417,7 +18417,7 @@ static void test_bug58036() if (!opt_silent) printf("mysql_real_connect() succeeded (failure expected)\n"); mysql_close(conn); - DIE(); + DIE(""); } if (!opt_silent) @@ -18443,7 +18443,7 @@ static void test_bug58036() printf("mysql_real_connect() failed: %s (%d)\n", mysql_error(conn), mysql_errno(conn)); mysql_close(conn); - DIE(); + DIE(""); } mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2"); @@ -18452,7 +18452,7 @@ static void test_bug58036() if (!opt_silent) printf("mysql_change_user() succedded, error expected!"); mysql_close(conn); - DIE(); + DIE(""); } if (!opt_silent) From a8e6f7c67b975609f70795a4c4934e7b0c757b7e Mon Sep 17 00:00:00 2001 From: Nirbhay Choubey Date: Mon, 21 Feb 2011 12:37:24 +0530 Subject: [PATCH 50/64] Bug#11766310 : 59398: MYSQLDUMP 5.1 CAN'T HANDLE A DASH ("-") IN DATABASE NAMES IN ALTER DATABASE. mysqldump did not quote database name in 'ALTER DATABASE' statements in its output. This can further cause a failure while loading if database name contains a hyphen '-'. This happened as, while printing the 'ALTER DATABASE' statements, the database name was not quoted. Fixed by quoting the database name. --- client/mysqldump.c | 10 ++++++++-- mysql-test/r/mysqldump.result | 36 +++++++++++++++++++++++++++++++++++ mysql-test/t/mysqldump.test | 21 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index 57e3f5b0349..0f2f1562ce5 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1134,6 +1134,9 @@ static int switch_db_collation(FILE *sql_file, { if (strcmp(current_db_cl_name, required_db_cl_name) != 0) { + char quoted_db_buf[NAME_LEN * 2 + 3]; + char *quoted_db_name= quote_name(db_name, quoted_db_buf, FALSE); + CHARSET_INFO *db_cl= get_charset_by_name(required_db_cl_name, MYF(0)); if (!db_cl) @@ -1141,7 +1144,7 @@ static int switch_db_collation(FILE *sql_file, fprintf(sql_file, "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n", - (const char *) db_name, + (const char *) quoted_db_name, (const char *) db_cl->csname, (const char *) db_cl->name, (const char *) delimiter); @@ -1162,6 +1165,9 @@ static int restore_db_collation(FILE *sql_file, const char *delimiter, const char *db_cl_name) { + char quoted_db_buf[NAME_LEN * 2 + 3]; + char *quoted_db_name= quote_name(db_name, quoted_db_buf, FALSE); + CHARSET_INFO *db_cl= get_charset_by_name(db_cl_name, MYF(0)); if (!db_cl) @@ -1169,7 +1175,7 @@ static int restore_db_collation(FILE *sql_file, fprintf(sql_file, "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n", - (const char *) db_name, + (const char *) quoted_db_name, (const char *) db_cl->csname, (const char *) db_cl->name, (const char *) delimiter); diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index 289e7f66406..fb70e0f1731 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -4591,5 +4591,41 @@ CREATE TABLE `comment_table` (i INT COMMENT 'FIELD COMMENT') COMMENT = 'TABLE CO DROP TABLE `comment_table`; # +# BUG#11766310 : 59398: MYSQLDUMP 5.1 CAN'T HANDLE A DASH ("-") IN +# DATABASE NAMES IN ALTER DATABASE +# +CREATE DATABASE `test-database`; +USE `test-database`; +CREATE TABLE `test` (`c1` VARCHAR(10)) ENGINE=MYISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +CREATE TRIGGER `trig` BEFORE INSERT ON `test` FOR EACH ROW BEGIN +END | +ALTER DATABASE `test-database` CHARACTER SET latin1 COLLATE latin1_swedish_ci; +ALTER DATABASE `test-database` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `test` ( + `c1` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +ALTER DATABASE `test-database` CHARACTER SET latin1 COLLATE latin1_swedish_ci ; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = '' */ ; +DELIMITER ;; +/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `trig` BEFORE INSERT ON `test` FOR EACH ROW BEGIN +END */;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +ALTER DATABASE `test-database` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +DROP DATABASE `test-database`; +# # End of 5.1 tests # diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test index fe0b05dbb42..0b533284ffa 100644 --- a/mysql-test/t/mysqldump.test +++ b/mysql-test/t/mysqldump.test @@ -2173,6 +2173,27 @@ CREATE TABLE `comment_table` (i INT COMMENT 'FIELD COMMENT') COMMENT = 'TABLE CO --exec $MYSQL_DUMP --compact --skip-create --xml test DROP TABLE `comment_table`; +--echo # +--echo # BUG#11766310 : 59398: MYSQLDUMP 5.1 CAN'T HANDLE A DASH ("-") IN +--echo # DATABASE NAMES IN ALTER DATABASE +--echo # + +CREATE DATABASE `test-database`; +USE `test-database`; +CREATE TABLE `test` (`c1` VARCHAR(10)) ENGINE=MYISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +DELIMITER |; +CREATE TRIGGER `trig` BEFORE INSERT ON `test` FOR EACH ROW BEGIN +END | +DELIMITER ;| + +ALTER DATABASE `test-database` CHARACTER SET latin1 COLLATE latin1_swedish_ci; +ALTER DATABASE `test-database` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; + +--exec $MYSQL_DUMP --quote-names --compact test-database + +DROP DATABASE `test-database`; + --echo # --echo # End of 5.1 tests --echo # From 6ae9c15048cc163a583001669ec1c33fd41db84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Mon, 21 Feb 2011 11:15:29 +0100 Subject: [PATCH 51/64] Remove last traces of HAVE_NDBCLUSTER_DB define - it has not had an effect in a long time and is just confusing. At the same time backport the removal of OPT_NDB_CONNECTSTRING and OPT_NDBCLUSTER values from "enum options_client" --- client/client_priv.h | 3 --- include/my_global.h | 1 - include/mysql_embed.h | 1 - sql/ha_ndbcluster_cond.cc | 2 +- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/client/client_priv.h b/client/client_priv.h index cc589e55d52..0652444be64 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -50,9 +50,6 @@ enum options_client OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_SERVER_ARG, OPT_STOP_POSITION, OPT_START_DATETIME, OPT_STOP_DATETIME, OPT_SIGINT_IGNORE, OPT_HEXBLOB, OPT_ORDER_BY_PRIMARY, OPT_COUNT, -#ifdef HAVE_NDBCLUSTER_DB - OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, -#endif OPT_TRIGGERS, OPT_MYSQL_ONLY_PRINT, OPT_MYSQL_LOCK_DIRECTORY, diff --git a/include/my_global.h b/include/my_global.h index c1ae8a6d6bc..02f4eb596d0 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1476,7 +1476,6 @@ static inline double rint(double x) #undef HAVE_OPENSSL #undef HAVE_SMEM /* No shared memory */ -#undef HAVE_NDBCLUSTER_DB /* No NDB cluster */ #endif /* EMBEDDED_LIBRARY */ diff --git a/include/mysql_embed.h b/include/mysql_embed.h index e860a4486eb..3b6f851bd6f 100644 --- a/include/mysql_embed.h +++ b/include/mysql_embed.h @@ -25,7 +25,6 @@ #undef HAVE_DLOPEN /* No udf functions */ #undef HAVE_SMEM /* No shared memory */ -#undef HAVE_NDBCLUSTER_DB /* No NDB cluster */ #endif /* EMBEDDED_LIBRARY */ #endif /* MYSQL_EMBED_INCLUDED */ diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc index 8a96ae41453..9ab7fb6208d 100644 --- a/sql/ha_ndbcluster_cond.cc +++ b/sql/ha_ndbcluster_cond.cc @@ -1472,4 +1472,4 @@ int ha_ndbcluster_cond::generate_scan_filter_from_key(NdbScanOperation *op, DBUG_RETURN(0); } -#endif /* HAVE_NDBCLUSTER_DB */ +#endif From 5d42003fbd50d5480da3393fa6b2b2ea88ac8317 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Mon, 21 Feb 2011 12:30:08 +0100 Subject: [PATCH 52/64] Bug #11754461 CANNOT ALTER TABLE WHEN KEY PREFIX TOO LONG The problem was that doing ALTER TABLE on a table which had a key on a TEXT/BLOB column with a prefix longer than the maximum number of characteres in this column (as per the character set), by mistake, caused an error (Error 1170 - ER_BLOB_KEY_WITHOUT_LENGTH). This bug not repeatable in 5.5. This patch adds a regression test to alter_table.test and contains no code changes. --- mysql-test/r/alter_table.result | 8 ++++++++ mysql-test/t/alter_table.test | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 304f562d47d..69b93555ec0 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -1383,3 +1383,11 @@ ALTER TABLE t1 CHANGE a id INT; affected rows: 0 info: Records: 0 Duplicates: 0 Warnings: 0 DROP TABLE t1; +# +# Bug#11754461 CANNOT ALTER TABLE WHEN KEY PREFIX TOO LONG +# +DROP DATABASE IF EXISTS db1; +CREATE DATABASE db1 CHARACTER SET utf8; +CREATE TABLE db1.t1 (bar TINYTEXT, KEY (bar(100))); +ALTER TABLE db1.t1 ADD baz INT; +DROP DATABASE db1; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 5b5fdf50c16..5d71eead642 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -1144,3 +1144,18 @@ INSERT INTO t1 VALUES (1, 1), (2, 2); ALTER TABLE t1 CHANGE a id INT; --disable_info DROP TABLE t1; + + +--echo # +--echo # Bug#11754461 CANNOT ALTER TABLE WHEN KEY PREFIX TOO LONG +--echo # + +--disable_warnings +DROP DATABASE IF EXISTS db1; +--enable_warnings + +CREATE DATABASE db1 CHARACTER SET utf8; +CREATE TABLE db1.t1 (bar TINYTEXT, KEY (bar(100))); +ALTER TABLE db1.t1 ADD baz INT; + +DROP DATABASE db1; From 44b41979bd4767ccffa6450ccfcf331d3bad7b9b Mon Sep 17 00:00:00 2001 From: Jorgen Loland Date: Mon, 21 Feb 2011 16:49:03 +0100 Subject: [PATCH 53/64] BUG#11762751: UPDATE STATEMENT THROWS AN ERROR, BUT STILL UPDATES THE TABLE ENTRIES (formerly 55385) BUG#11764529: MULTI UPDATE+INNODB REPORTS ER_KEY_NOT_FOUND IF A TABLE IS UPDATED TWICE (formerly 57373) If multiple-table update updates a row through two aliases and the first update physically moves the row, the second update will fail to locate the row. This results in different errors depending on storage engine: * MyISAM: Got error 134 from storage engine * InnoDB: Can't find record in 'tbl' None of these errors accurately describe the problem. Furthermore, since MyISAM is non-transactional, the update executed first will be performed while the second will not. In addition, for two equal multiple-table update statements, one could succeed and the other fail based on whether or not the record actually moved or not. This was inconsistent. Two update operations may physically move a row: 1) Update of a column in a clustered primary key 2) Update of a column used to calculate which partition the row belongs to BUG#11764529 is about case 1) above, BUG#11762751 was about case 2). The fix for these bugs is to return with an error if multiple-table update is about to: a) Update a table through multiple aliases, and b) Perform an update that may physically more the row in at least one of these aliases This avoids * partial updates as described for MyISAM above, * provides the same error message that describes the actual problem for all SEs * inconsistent behavior where a statement fails or succeeds based on e.g. the partitioning algorithm of the table. --- mysql-test/r/multi_update.result | 17 +++++ mysql-test/r/multi_update_innodb.result | 29 ++++++++ mysql-test/r/partition.result | 48 ++++++++++++ mysql-test/t/multi_update.test | 20 +++++ mysql-test/t/multi_update_innodb.test | 33 +++++++++ mysql-test/t/partition.test | 50 +++++++++++++ sql/handler.cc | 1 + sql/share/errmsg-utf8.txt | 3 + sql/sql_update.cc | 98 ++++++++++++++++++++++++- 9 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/multi_update_innodb.result create mode 100644 mysql-test/t/multi_update_innodb.test diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index e36bef1f338..5a3e7938121 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -680,4 +680,21 @@ Warnings: Warning 1292 Truncated incorrect datetime value: '1' DROP FUNCTION f1; DROP TABLE t1; +# +# BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +# table is updated twice +# +CREATE TABLE t1( +pk INT, +a INT, +PRIMARY KEY (pk) +) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,0); +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; + +# Should be (1,2) +SELECT * FROM t1; +pk a +1 2 +DROP TABLE t1; end of tests diff --git a/mysql-test/r/multi_update_innodb.result b/mysql-test/r/multi_update_innodb.result new file mode 100644 index 00000000000..12a94accc1f --- /dev/null +++ b/mysql-test/r/multi_update_innodb.result @@ -0,0 +1,29 @@ +# +# BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +# table is updated twice +# +CREATE TABLE t1( +pk INT, +a INT, +b INT, +PRIMARY KEY (pk) +) ENGINE=InnoDB; +INSERT INTO t1 VALUES (0,0,0); +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +SELECT * FROM t1; +pk a b +0 0 0 +CREATE VIEW v1 AS SELECT * FROM t1; +UPDATE v1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +SELECT * FROM t1; +pk a b +0 0 0 +UPDATE t1 AS A, t1 AS B SET A.a = 1, B.b = 2; +# Should be (0,1,2) +SELECT * FROM t1; +pk a b +0 1 2 +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 9c8b93d9fe2..1d7a08e9a7c 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -2264,3 +2264,51 @@ INSERT INTO t1 VALUES(0); DROP TABLE t1; SET GLOBAL myisam_use_mmap=default; End of 5.1 tests +# +# BUG#55385: UPDATE statement throws an error, but still updates +# the table entries +CREATE TABLE t1_part ( +partkey int, +nokey int +) PARTITION BY LINEAR HASH(partkey) PARTITIONS 3; +INSERT INTO t1_part VALUES (1, 1) , (10, 10); +CREATE VIEW v1 AS SELECT * FROM t1_part; + +# Should be (1,1),(10,10) +SELECT * FROM t1_part; +partkey nokey +1 1 +10 10 + +# Case 1 +# Update is refused because partitioning key is updated +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.partkey = 2, B.nokey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2, B.partkey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. + +# Case 2 +# Like 1, but partition accessed through a view +UPDATE t1_part AS A NATURAL JOIN v1 as B SET A.nokey = 2 , B.partkey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +UPDATE v1 AS A NATURAL JOIN t1_part as B SET A.nokey = 2 , B.partkey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. + +# Should be (1,1),(10,10) +SELECT * FROM t1_part; +partkey nokey +1 1 +10 10 + +# Case 3 +# Update is accepted because partitioning key is not updated +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2 , B.nokey = 3; + +# Should be (1,3),(10,3) +SELECT * FROM t1_part; +partkey nokey +1 3 +10 3 + +DROP VIEW v1; +DROP TABLE t1_part; diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 0e2abe578ce..496d045075a 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -683,4 +683,24 @@ UPDATE (SELECT 1 FROM t1 WHERE f1 = (SELECT f1() FROM t1)) x, t1 SET f1 = 1; DROP FUNCTION f1; DROP TABLE t1; +--echo # +--echo # BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +--echo # table is updated twice +--echo # + +# Results differ between storage engines. +# See multi_update_innodb.test for the InnoDB variant of this test +CREATE TABLE t1( + pk INT, + a INT, + PRIMARY KEY (pk) +) ENGINE=MyISAM; + +INSERT INTO t1 VALUES (0,0); +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +--echo +--echo # Should be (1,2) +SELECT * FROM t1; +DROP TABLE t1; + --echo end of tests diff --git a/mysql-test/t/multi_update_innodb.test b/mysql-test/t/multi_update_innodb.test new file mode 100644 index 00000000000..3148d009cef --- /dev/null +++ b/mysql-test/t/multi_update_innodb.test @@ -0,0 +1,33 @@ +--source include/have_innodb.inc + +--echo # +--echo # BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +--echo # table is updated twice +--echo # + +# Results differ between storage engines. +# See multi_update.test for the MyISAM variant of this test +CREATE TABLE t1( + pk INT, + a INT, + b INT, + PRIMARY KEY (pk) +) ENGINE=InnoDB; + +INSERT INTO t1 VALUES (0,0,0); +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +SELECT * FROM t1; + +CREATE VIEW v1 AS SELECT * FROM t1; +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE v1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +SELECT * FROM t1; + +UPDATE t1 AS A, t1 AS B SET A.a = 1, B.b = 2; +--echo # Should be (0,1,2) +SELECT * FROM t1; + +DROP VIEW v1; +DROP TABLE t1; + diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 21c385485cc..f2e8cbb2da2 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -2267,3 +2267,53 @@ DROP TABLE t1; SET GLOBAL myisam_use_mmap=default; --echo End of 5.1 tests + +--echo # +--echo # BUG#55385: UPDATE statement throws an error, but still updates +--echo # the table entries + +CREATE TABLE t1_part ( + partkey int, + nokey int +) PARTITION BY LINEAR HASH(partkey) PARTITIONS 3; + +INSERT INTO t1_part VALUES (1, 1) , (10, 10); +CREATE VIEW v1 AS SELECT * FROM t1_part; + +--echo +--echo # Should be (1,1),(10,10) +SELECT * FROM t1_part; + +--echo +--echo # Case 1 +--echo # Update is refused because partitioning key is updated +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.partkey = 2, B.nokey = 3; +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2, B.partkey = 3; + +--echo +--echo # Case 2 +--echo # Like 1, but partition accessed through a view +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1_part AS A NATURAL JOIN v1 as B SET A.nokey = 2 , B.partkey = 3; +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE v1 AS A NATURAL JOIN t1_part as B SET A.nokey = 2 , B.partkey = 3; + +--echo +--echo # Should be (1,1),(10,10) +SELECT * FROM t1_part; + +--echo +--echo # Case 3 +--echo # Update is accepted because partitioning key is not updated +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2 , B.nokey = 3; + +--echo +--echo # Should be (1,3),(10,3) +SELECT * FROM t1_part; + +--echo +# Cleanup +DROP VIEW v1; +DROP TABLE t1_part; diff --git a/sql/handler.cc b/sql/handler.cc index f7690c28827..9ca1f3a20f8 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2700,6 +2700,7 @@ void handler::print_error(int error, myf errflag) break; case HA_ERR_KEY_NOT_FOUND: case HA_ERR_NO_ACTIVE_RECORD: + case HA_ERR_RECORD_DELETED: case HA_ERR_END_OF_FILE: textno=ER_KEY_NOT_FOUND; break; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index a8b0c27d115..998e88704d8 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6394,3 +6394,6 @@ ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX ER_STMT_CACHE_FULL eng "Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again" + +ER_MULTI_UPDATE_KEY_CONFLICT + eng "Primary key/partition key update is not allowed since the table is updated both as '%-.192s' and '%-.192s'." diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 08d804efcb6..59356540201 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -998,6 +998,98 @@ static table_map get_table_map(List *items) return map; } +/** + If one row is updated through two different aliases and the first + update physically moves the row, the second update will error + because the row is no longer located where expected. This function + checks if the multiple-table update is about to do that and if so + returns with an error. + + The following update operations physically moves rows: + 1) Update of a column in a clustered primary key + 2) Update of a column used to calculate which partition the row belongs to + + This function returns with an error if both of the following are + true: + + a) A table in the multiple-table update statement is updated + through multiple aliases (including views) + b) At least one of the updates on the table from a) may physically + moves the row. Note: Updating a column used to calculate which + partition a row belongs to does not necessarily mean that the + row is moved. The new value may or may not belong to the same + partition. + + @param leaves First leaf table + @param tables_for_update Map of tables that are updated + + @return + true if the update is unsafe, in which case an error message is also set, + false otherwise. +*/ +static +bool unsafe_key_update(TABLE_LIST *leaves, table_map tables_for_update) +{ + TABLE_LIST *tl= leaves; + + for (tl= leaves; tl ; tl= tl->next_leaf) + { + if (tl->table->map & tables_for_update) + { + TABLE *table1= tl->table; + bool primkey_clustered= (table1->file->primary_key_is_clustered() && + table1->s->primary_key != MAX_KEY); + + bool table_partitioned= false; +#ifdef WITH_PARTITION_STORAGE_ENGINE + table_partitioned= (table1->part_info != NULL); +#endif + + if (!table_partitioned && !primkey_clustered) + continue; + + for (TABLE_LIST* tl2= tl->next_leaf; tl2 ; tl2= tl2->next_leaf) + { + /* + Look at "next" tables only since all previous tables have + already been checked + */ + TABLE *table2= tl2->table; + if (table2->map & tables_for_update && table1->s == table2->s) + { + // A table is updated through two aliases + if (table_partitioned && + (partition_key_modified(table1, table1->write_set) || + partition_key_modified(table2, table2->write_set))) + { + // Partitioned key is updated + my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0), + tl->belong_to_view ? tl->belong_to_view->alias + : tl->alias, + tl2->belong_to_view ? tl2->belong_to_view->alias + : tl2->alias); + return true; + } + + if (primkey_clustered && + (bitmap_is_set(table1->write_set, table1->s->primary_key) || + bitmap_is_set(table2->write_set, table2->s->primary_key))) + { + // Clustered primary key is updated + my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0), + tl->belong_to_view ? tl->belong_to_view->alias + : tl->alias, + tl2->belong_to_view ? tl2->belong_to_view->alias + : tl2->alias); + return true; + } + } + } + } + } + return false; +} + /* make update specific preparation and checks after opening tables @@ -1077,10 +1169,14 @@ int mysql_multi_update_prepare(THD *thd) thd->table_map_for_update= tables_for_update= get_table_map(fields); + leaves= lex->select_lex.leaf_tables; + + if (unsafe_key_update(leaves, tables_for_update)) + DBUG_RETURN(true); + /* Setup timestamp handling and locking mode */ - leaves= lex->select_lex.leaf_tables; for (tl= leaves; tl; tl= tl->next_leaf) { TABLE *table= tl->table; From 9d6d1902e091c868bb288e0ccf9f975ccb474db9 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Tue, 22 Feb 2011 16:04:08 +1100 Subject: [PATCH 54/64] Bug #11766227: InnoDB purge lag much worse for 5.5.8 versus 5.1 Bug #11766501: Multiple RBS break the get rseg with mininum trx_t::no code during purge Bug# 59291 changes: Main problem is that truncating the UNDO log at the completion of every trx_purge() call is expensive as the number of rollback segments is increased. We truncate after a configurable amount of pages. The innodb_purge_batch_size parameter is used to control when InnoDB does the actual truncate. The truncate is done once after 128 (or TRX_SYS_N_RSEGS iterations). In other words we truncate after purge 128 * innodb_purge_batch_size. The smaller the batch size the quicker we truncate. Introduce a new parameter that allows how many rollback segments to use for storing REDO information. This is really step 1 in allowing complete control to the user over rollback space management. New parameters: i) innodb_rollback_segments = number of rollback_segments to use (default is now 128) dynamic parameter, can be changed anytime. Currently there is little benefit in changing it from the default. Optimisations in the patch. i. Change the O(n) behaviour of trx_rseg_get_on_id() to O(log n) Backported from 5.6. Refactor some of the binary heap code. Create a new include/ut0bh.ic file. ii. Avoid truncating the rollback segments after every purge. Related changes that were moved to a separate patch: i. Purge should not do any flushing, only wait for space to be free so that it only does purging of records unless it is held up by a long running transaction that is preventing it from progressing. ii. Give the purge thread preference over transactions when acquiring the rseg->mutex during commit. This to avoid purge blocking unnecessarily when getting the next rollback segment to purge. Bug #11766501 changes: Add the rseg to the min binary heap under the cover of the kernel mutex and the binary heap mutex. This ensures the ordering of the min binary heap. The two changes have to be committed together because they share the same that fixes both issues. rb://567 Approved by: Inaam Rana. --- storage/innobase/CMakeLists.txt | 4 +- storage/innobase/handler/ha_innodb.cc | 20 +- storage/innobase/include/buf0flu.h | 13 +- storage/innobase/include/srv0srv.h | 7 +- storage/innobase/include/sync0sync.h | 6 +- storage/innobase/include/trx0purge.h | 22 +- storage/innobase/include/trx0rseg.h | 12 +- storage/innobase/include/trx0sys.h | 10 +- storage/innobase/include/trx0sys.ic | 12 - storage/innobase/include/ut0bh.ic | 123 ++++++++ storage/innobase/srv/srv0srv.c | 22 +- storage/innobase/sync/sync0sync.c | 14 +- storage/innobase/trx/trx0purge.c | 431 +++++++++++++------------- storage/innobase/trx/trx0rseg.c | 52 +++- storage/innobase/trx/trx0sys.c | 43 ++- storage/innobase/trx/trx0trx.c | 270 +++++++++------- storage/innobase/ut/ut0ut.c | 9 +- 17 files changed, 675 insertions(+), 395 deletions(-) create mode 100644 storage/innobase/include/ut0bh.ic diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 4bbda0d2477..d4b98f2af0d 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2011, 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 @@ -246,7 +246,7 @@ SET(INNOBASE_SOURCES btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c btr/btr0sea.c trx/trx0sys.c trx/trx0trx.c trx/trx0undo.c usr/usr0sess.c ut/ut0byte.c ut/ut0dbg.c ut/ut0list.c ut/ut0mem.c ut/ut0rbt.c ut/ut0rnd.c - ut/ut0ut.c ut/ut0vec.c ut/ut0wqueue.c) + ut/ut0ut.c ut/ut0vec.c ut/ut0wqueue.c ut/ut0bh.c) IF(WITH_INNODB) # Legacy option diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 8565809c09f..e4d041bf915 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2000, 2010, MySQL AB & Innobase Oy. All Rights Reserved. +Copyright (c) 2000, 2011, MySQL AB & Innobase Oy. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. @@ -262,7 +262,7 @@ static PSI_mutex_info all_innodb_mutexes[] = { # endif /* UNIV_MEM_DEBUG */ {&mem_pool_mutex_key, "mem_pool_mutex", 0}, {&mutex_list_mutex_key, "mutex_list_mutex", 0}, - {&purge_sys_mutex_key, "purge_sys_mutex", 0}, + {&purge_sys_bh_mutex_key, "purge_sys_bh_mutex", 0}, {&recv_sys_mutex_key, "recv_sys_mutex", 0}, {&rseg_mutex_key, "rseg_mutex", 0}, # ifdef UNIV_SYNC_DEBUG @@ -10971,16 +10971,23 @@ static MYSQL_SYSVAR_ULONG(io_capacity, srv_io_capacity, static MYSQL_SYSVAR_ULONG(purge_batch_size, srv_purge_batch_size, PLUGIN_VAR_OPCMDARG, - "Number of UNDO logs to purge in one batch from the history list. " - "Default is 20", + "Number of UNDO log pages to purge in one batch from the history list.", NULL, NULL, 20, /* Default setting */ 1, /* Minimum value */ - 5000, 0); /* Maximum value */ + 10000, 0); /* Maximum value */ + +static MYSQL_SYSVAR_ULONG(rollback_segments, srv_rollback_segments, + PLUGIN_VAR_OPCMDARG, + "Number of UNDO logs to use.", + NULL, NULL, + 128, /* Default setting */ + 1, /* Minimum value */ + TRX_SYS_N_RSEGS, 0); /* Maximum value */ static MYSQL_SYSVAR_ULONG(purge_threads, srv_n_purge_threads, PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY, - "Purge threads can be either 0 or 1. Default is 0.", + "Purge threads can be either 0 or 1.", NULL, NULL, 0, /* Default setting */ 0, /* Minimum value */ @@ -11331,6 +11338,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(io_capacity), MYSQL_SYSVAR(purge_threads), MYSQL_SYSVAR(purge_batch_size), + MYSQL_SYSVAR(rollback_segments), NULL }; diff --git a/storage/innobase/include/buf0flu.h b/storage/innobase/include/buf0flu.h index 28d9b90e755..ae27f5dab0e 100644 --- a/storage/innobase/include/buf0flu.h +++ b/storage/innobase/include/buf0flu.h @@ -138,7 +138,18 @@ UNIV_INTERN void buf_flush_wait_batch_end( /*=====================*/ - buf_pool_t* buf_pool, /*!< buffer pool instance */ + buf_pool_t* buf_pool, /*!< in: buffer pool instance */ + enum buf_flush type); /*!< in: BUF_FLUSH_LRU + or BUF_FLUSH_LIST */ +/******************************************************************//** +Waits until a flush batch of the given type ends. This is called by +a thread that only wants to wait for a flush to end but doesn't do +any flushing itself. */ +UNIV_INTERN +void +buf_flush_wait_batch_end_wait_only( +/*===============================*/ + buf_pool_t* buf_pool, /*!< in: buffer pool instance */ enum buf_flush type); /*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */ /********************************************************************//** diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index d600ad4a034..252d1424c63 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2011, Innobase Oy. All Rights Reserved. Copyright (c) 2008, 2009, Google Inc. Copyright (c) 2009, Percona Inc. @@ -294,9 +294,12 @@ extern ulint srv_log_waits; /* the number of purge threads to use from the worker pool (currently 0 or 1) */ extern ulong srv_n_purge_threads; -/* the number of records to purge in one batch */ +/* the number of pages to purge in one batch */ extern ulong srv_purge_batch_size; +/* the number of rollback segments to use */ +extern ulong srv_rollback_segments; + /* variable that counts amount of data read in total (in bytes) */ extern ulint srv_data_read; diff --git a/storage/innobase/include/sync0sync.h b/storage/innobase/include/sync0sync.h index fb3a24c5ee5..b4f9e21933f 100644 --- a/storage/innobase/include/sync0sync.h +++ b/storage/innobase/include/sync0sync.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2011, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by @@ -93,7 +93,7 @@ extern mysql_pfs_key_t mem_hash_mutex_key; # endif /* UNIV_MEM_DEBUG */ extern mysql_pfs_key_t mem_pool_mutex_key; extern mysql_pfs_key_t mutex_list_mutex_key; -extern mysql_pfs_key_t purge_sys_mutex_key; +extern mysql_pfs_key_t purge_sys_bh_mutex_key; extern mysql_pfs_key_t recv_sys_mutex_key; extern mysql_pfs_key_t rseg_mutex_key; # ifdef UNIV_SYNC_DEBUG @@ -637,7 +637,6 @@ or row lock! */ #define SYNC_TREE_NODE_NEW 892 #define SYNC_TREE_NODE_FROM_HASH 891 #define SYNC_TREE_NODE 890 -#define SYNC_PURGE_SYS 810 #define SYNC_PURGE_LATCH 800 #define SYNC_TRX_UNDO 700 #define SYNC_RSEG 600 @@ -659,6 +658,7 @@ or row lock! */ #define SYNC_REC_LOCK 299 #define SYNC_TRX_LOCK_HEAP 298 #define SYNC_TRX_SYS_HEADER 290 +#define SYNC_PURGE_QUEUE 200 #define SYNC_LOG 170 #define SYNC_LOG_FLUSH_ORDER 147 #define SYNC_RECV 168 diff --git a/storage/innobase/include/trx0purge.h b/storage/innobase/include/trx0purge.h index d2730a68a78..0b83a76cab7 100644 --- a/storage/innobase/include/trx0purge.h +++ b/storage/innobase/include/trx0purge.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -68,8 +68,9 @@ Creates the global purge system control structure and inits the history mutex. */ UNIV_INTERN void -trx_purge_sys_create(void); -/*======================*/ +trx_purge_sys_create( +/*=================*/ + ib_bh_t* ib_bh); /*!< in/own: UNDO log min binary heap*/ /********************************************************************//** Frees the global purge system control structure. */ UNIV_INTERN @@ -128,20 +129,20 @@ struct trx_purge_struct{ ulint state; /*!< Purge system state */ sess_t* sess; /*!< System session running the purge query */ - trx_t* trx; /*!< System transaction running the purge + trx_t* trx; /*!< System transaction running the + purge query: this trx is not in the trx list of the trx system and it never ends */ que_t* query; /*!< The query graph which will do the parallelized purge operation */ - rw_lock_t latch; /*!< The latch protecting the purge view. - A purge operation must acquire an - x-latch here for the instant at which + rw_lock_t latch; /*!< The latch protecting the purge + view. A purge operation must acquire + an x-latch here for the instant at which it changes the purge view: an undo log operation can prevent this by obtaining an s-latch here. */ read_view_t* view; /*!< The purge will not remove undo logs which are >= this view (purge view) */ - mutex_t mutex; /*!< Mutex protecting the fields below */ ulint n_pages_handled;/*!< Approximate number of undo log pages processed in purge */ ulint handle_limit; /*!< Target of how many pages to get @@ -179,6 +180,11 @@ struct trx_purge_struct{ mem_heap_t* heap; /*!< Temporary storage used during a purge: can be emptied after purge completes */ + /*-----------------------------*/ + ib_bh_t* ib_bh; /*!< Binary min-heap, ordered on + rseg_queue_t::trx_no. It is protected + by the bh_mutex */ + mutex_t bh_mutex; /*!< Mutex protecting ib_bh */ }; #define TRX_PURGE_ON 1 /* purge operation is running */ diff --git a/storage/innobase/include/trx0rseg.h b/storage/innobase/include/trx0rseg.h index a293d9b896e..5acde05de3d 100644 --- a/storage/innobase/include/trx0rseg.h +++ b/storage/innobase/include/trx0rseg.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -113,7 +113,9 @@ void trx_rseg_list_and_array_init( /*=========================*/ trx_sysf_t* sys_header, /*!< in: trx system header */ + ib_bh_t* ib_bh, /*!< in: rseg queue */ mtr_t* mtr); /*!< in: mtr */ + /*************************************************************************** Free's an instance of the rollback segment in memory. */ UNIV_INTERN @@ -180,6 +182,14 @@ struct trx_rseg_struct{ memory objects */ }; +/** For prioritising the rollback segments for purge. */ +struct rseg_queue_struct { + trx_id_t trx_no; /*!< trx_rseg_t::last_trx_no */ + trx_rseg_t* rseg; /*!< Rollback segment */ +}; + +typedef struct rseg_queue_struct rseg_queue_t; + /* Undo log segment slot in a rollback segment header */ /*-------------------------------------------------------------*/ #define TRX_RSEG_SLOT_PAGE_NO 0 /* Page number of the header page of diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index 63e3f6be934..dc0ca2285b9 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -38,6 +38,7 @@ Created 3/26/1996 Heikki Tuuri #include "mem0mem.h" #include "sync0sync.h" #include "ut0lst.h" +#include "ut0bh.h" #include "read0types.h" #include "page0types.h" @@ -221,13 +222,6 @@ UNIV_INLINE trx_id_t trx_sys_get_new_trx_id(void); /*========================*/ -/*****************************************************************//** -Allocates a new transaction number. -@return new, allocated trx number */ -UNIV_INLINE -trx_id_t -trx_sys_get_new_trx_no(void); -/*========================*/ #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** Writes a trx id to an index page. In case that the id size changes in diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index 385c7f4f0cc..355f118a1ec 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -369,16 +369,4 @@ trx_sys_get_new_trx_id(void) return(id); } -/*****************************************************************//** -Allocates a new transaction number. -@return new, allocated trx number */ -UNIV_INLINE -trx_id_t -trx_sys_get_new_trx_no(void) -/*========================*/ -{ - ut_ad(mutex_own(&kernel_mutex)); - - return(trx_sys_get_new_trx_id()); -} #endif /* !UNIV_HOTBACKUP */ diff --git a/storage/innobase/include/ut0bh.ic b/storage/innobase/include/ut0bh.ic new file mode 100644 index 00000000000..10660b29559 --- /dev/null +++ b/storage/innobase/include/ut0bh.ic @@ -0,0 +1,123 @@ +/***************************************************************************//** +Copyright (c) 2011, Oracle Corpn. 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 +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +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 + +*****************************************************************************/ + +/******************************************************************//** +@file include/ut0bh.ic +Binary min-heap implementation. + +Created 2011-01-15 by Sunny Bains +*******************************************************/ + +#include "ut0bh.h" + +/**********************************************************************//** +Get the number of elements in the binary heap. +@return number of elements */ +UNIV_INLINE +ulint +ib_bh_size( +/*=======*/ + const ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh->n_elems); +} + +/**********************************************************************//** +Test if binary heap is empty. +@return TRUE if empty. */ +UNIV_INLINE +ibool +ib_bh_is_empty( +/*===========*/ + const ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh_size(ib_bh) == 0); +} + +/**********************************************************************//** +Test if binary heap is full. +@return TRUE if full. */ +UNIV_INLINE +ibool +ib_bh_is_full( +/*===========*/ + const ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh_size(ib_bh) >= ib_bh->max_elems); +} + +/**********************************************************************//** +Get a pointer to the element. +@return pointer to element */ +UNIV_INLINE +void* +ib_bh_get( +/*=======*/ + ib_bh_t* ib_bh, /*!< in: instance */ + ulint i) /*!< in: index */ +{ + byte* ptr = (byte*) (ib_bh + 1); + + ut_a(i < ib_bh_size(ib_bh)); + + return(ptr + (ib_bh->sizeof_elem * i)); +} + +/**********************************************************************//** +Copy an element to the binary heap. +@return pointer to copied element */ +UNIV_INLINE +void* +ib_bh_set( +/*======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + ulint i, /*!< in: index */ + const void* elem) /*!< in: element to add */ +{ + void* ptr = ib_bh_get(ib_bh, i); + + ut_memcpy(ptr, elem, ib_bh->sizeof_elem); + + return(ptr); +} + +/**********************************************************************//** +Return the first element from the binary heap. +@return pointer to first element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_first( +/*========*/ + ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh_is_empty(ib_bh) ? NULL : ib_bh_get(ib_bh, 0)); +} + +/**********************************************************************//** +Return the last element from the binary heap. +@return pointer to last element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_last( +/*========*/ + ib_bh_t* ib_bh) /*!< in/out: instance */ +{ + return(ib_bh_is_empty(ib_bh) + ? NULL + : ib_bh_get(ib_bh, ib_bh_size(ib_bh) - 1)); +} + diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 826145005a4..3c5eed448bd 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. @@ -269,9 +269,12 @@ UNIV_INTERN ulong srv_max_buf_pool_modified_pct = 75; /* the number of purge threads to use from the worker pool (currently 0 or 1).*/ UNIV_INTERN ulong srv_n_purge_threads = 0; -/* the number of records to purge in one batch */ +/* the number of pages to purge in one batch */ UNIV_INTERN ulong srv_purge_batch_size = 20; +/* the number of rollback segments to use */ +UNIV_INTERN ulong srv_rollback_segments = TRX_SYS_N_RSEGS; + /* variable counts amount of data read in total (in bytes) */ UNIV_INTERN ulint srv_data_read = 0; @@ -3096,6 +3099,7 @@ srv_purge_thread( required by os_thread_create */ { srv_slot_t* slot; + ulint retries = 0; ulint slot_no = ULINT_UNDEFINED; ulint n_total_purged = ULINT_UNDEFINED; @@ -3122,7 +3126,7 @@ srv_purge_thread( while (srv_shutdown_state != SRV_SHUTDOWN_EXIT_THREADS) { - ulint n_pages_purged; + ulint n_pages_purged = 0; /* If there are very few records to purge or the last purge didn't purge any records then wait for activity. @@ -3130,7 +3134,8 @@ srv_purge_thread( because in the worst case we will end up waiting for the next purge event. */ if (trx_sys->rseg_history_len < srv_purge_batch_size - || n_total_purged == 0) { + || (n_total_purged == 0 + && retries >= TRX_SYS_N_RSEGS)) { os_event_t event; @@ -3141,6 +3146,8 @@ srv_purge_thread( mutex_exit(&kernel_mutex); os_event_wait(event); + + retries = 0; } /* Check for shutdown and whether we should do purge at all. */ @@ -3151,7 +3158,12 @@ srv_purge_thread( break; } - n_total_purged = 0; + if (n_total_purged == 0 && retries <= TRX_SYS_N_RSEGS) { + ++retries; + } else if (n_total_purged > 0) { + retries = 0; + n_total_purged = 0; + } /* Purge until there are no more records to purge and there is no change in configuration or server state. */ diff --git a/storage/innobase/sync/sync0sync.c b/storage/innobase/sync/sync0sync.c index 453314f465d..0f6a60ca260 100644 --- a/storage/innobase/sync/sync0sync.c +++ b/storage/innobase/sync/sync0sync.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2011, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by @@ -1172,7 +1172,7 @@ sync_thread_add_level( case SYNC_RSEG: case SYNC_TRX_UNDO: case SYNC_PURGE_LATCH: - case SYNC_PURGE_SYS: + case SYNC_PURGE_QUEUE: case SYNC_DICT_AUTOINC_MUTEX: case SYNC_DICT_OPERATION: case SYNC_DICT_HEADER: @@ -1239,10 +1239,16 @@ sync_thread_add_level( || sync_thread_levels_g(array, SYNC_FSP, TRUE)); break; case SYNC_TRX_UNDO_PAGE: + /* Purge is allowed to read in as many UNDO pages as it likes, + there was a bogus rule here earlier that forced the caller to + acquire the purge_sys_t::mutex. The purge mutex did not really + protect anything because it was only ever acquired by the + single purge thread. The purge thread can read the UNDO pages + without any covering mutex. */ + ut_a(sync_thread_levels_contain(array, SYNC_TRX_UNDO) || sync_thread_levels_contain(array, SYNC_RSEG) - || sync_thread_levels_contain(array, SYNC_PURGE_SYS) - || sync_thread_levels_g(array, SYNC_TRX_UNDO_PAGE, TRUE)); + || sync_thread_levels_g(array, level - 1, TRUE)); break; case SYNC_RSEG_HEADER: ut_a(sync_thread_levels_contain(array, SYNC_RSEG)); diff --git a/storage/innobase/trx/trx0purge.c b/storage/innobase/trx/trx0purge.c index 4c787579a03..02ec9f1c072 100644 --- a/storage/innobase/trx/trx0purge.c +++ b/storage/innobase/trx/trx0purge.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -57,8 +57,8 @@ UNIV_INTERN mysql_pfs_key_t trx_purge_latch_key; #endif /* UNIV_PFS_RWLOCK */ #ifdef UNIV_PFS_MUTEX -/* Key to register purge_sys_mutex with performance schema */ -UNIV_INTERN mysql_pfs_key_t purge_sys_mutex_key; +/* Key to register purge_sys_bh_mutex with performance schema */ +UNIV_INTERN mysql_pfs_key_t purge_sys_bh_mutex_key; #endif /* UNIV_PFS_MUTEX */ /*****************************************************************//** @@ -219,13 +219,16 @@ Creates the global purge system control structure and inits the history mutex. */ UNIV_INTERN void -trx_purge_sys_create(void) -/*======================*/ +trx_purge_sys_create( +/*=================*/ + ib_bh_t* ib_bh) /*!< in, own: UNDO log min binary heap */ { ut_ad(mutex_own(&kernel_mutex)); - purge_sys = mem_alloc(sizeof(trx_purge_t)); + purge_sys = mem_zalloc(sizeof(trx_purge_t)); + /* Take ownership of ib_bh, we are responsible for freeing it. */ + purge_sys->ib_bh = ib_bh; purge_sys->state = TRX_STOP_PURGE; purge_sys->n_pages_handled = 0; @@ -237,8 +240,9 @@ trx_purge_sys_create(void) rw_lock_create(trx_purge_latch_key, &purge_sys->latch, SYNC_PURGE_LATCH); - mutex_create(purge_sys_mutex_key, - &purge_sys->mutex, SYNC_PURGE_SYS); + mutex_create( + purge_sys_bh_mutex_key, &purge_sys->bh_mutex, + SYNC_PURGE_QUEUE); purge_sys->heap = mem_heap_create(256); @@ -288,9 +292,12 @@ trx_purge_sys_close(void) trx_undo_arr_free(purge_sys->arr); rw_lock_free(&purge_sys->latch); - mutex_free(&purge_sys->mutex); + mutex_free(&purge_sys->bh_mutex); mem_heap_free(purge_sys->heap); + + ib_bh_free(purge_sys->ib_bh); + mem_free(purge_sys); purge_sys = NULL; @@ -311,34 +318,31 @@ trx_purge_add_update_undo_to_history( mtr_t* mtr) /*!< in: mtr */ { trx_undo_t* undo; - trx_rseg_t* rseg; trx_rsegf_t* rseg_header; -#ifdef UNIV_DEBUG - trx_usegf_t* seg_header; -#endif /* UNIV_DEBUG */ trx_ulogf_t* undo_header; - ulint hist_size; undo = trx->update_undo; ut_ad(undo); - rseg = undo->rseg; + ut_ad(mutex_own(&undo->rseg->mutex)); - ut_ad(mutex_own(&(rseg->mutex))); - - rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size, - rseg->page_no, mtr); + rseg_header = trx_rsegf_get( + undo->rseg->space, undo->rseg->zip_size, undo->rseg->page_no, + mtr); undo_header = undo_page + undo->hdr_offset; -#ifdef UNIV_DEBUG - seg_header = undo_page + TRX_UNDO_SEG_HDR; -#endif /* UNIV_DEBUG */ + /* Add the log as the first in the history list */ if (undo->state != TRX_UNDO_CACHED) { + ulint hist_size; +#ifdef UNIV_DEBUG + trx_usegf_t* seg_header = undo_page + TRX_UNDO_SEG_HDR; +#endif /* UNIV_DEBUG */ + /* The undo log segment will not be reused */ - if (undo->id >= TRX_RSEG_N_SLOTS) { + if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) { fprintf(stderr, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); @@ -347,18 +351,43 @@ trx_purge_add_update_undo_to_history( trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr); - hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, - MLOG_4BYTES, mtr); + hist_size = mtr_read_ulint( + rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr); + ut_ad(undo->size == flst_get_len( seg_header + TRX_UNDO_PAGE_LIST, mtr)); - mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, - hist_size + undo->size, MLOG_4BYTES, mtr); + mlog_write_ulint( + rseg_header + TRX_RSEG_HISTORY_SIZE, + hist_size + undo->size, MLOG_4BYTES, mtr); + } + + flst_add_first( + rseg_header + TRX_RSEG_HISTORY, + undo_header + TRX_UNDO_HISTORY_NODE, mtr); + + /* Write the trx number to the undo log header */ + + mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr); + + /* Write information about delete markings to the undo log header */ + + if (!undo->del_marks) { + mlog_write_ulint( + undo_header + TRX_UNDO_DEL_MARKS, FALSE, + MLOG_2BYTES, mtr); + } + + if (undo->rseg->last_page_no == FIL_NULL) { + undo->rseg->last_trx_no = trx->no; + undo->rseg->last_offset = undo->hdr_offset; + undo->rseg->last_page_no = undo->hdr_page_no; + undo->rseg->last_del_marks = undo->del_marks; + + /* FIXME: Add a bin heap validate function to check that + the rseg exists. */ } - /* Add the log as the first in the history list */ - flst_add_first(rseg_header + TRX_RSEG_HISTORY, - undo_header + TRX_UNDO_HISTORY_NODE, mtr); mutex_enter(&kernel_mutex); trx_sys->rseg_history_len++; mutex_exit(&kernel_mutex); @@ -367,23 +396,6 @@ trx_purge_add_update_undo_to_history( /* Inform the purge thread that there is work to do. */ srv_wake_purge_thread_if_not_active(); } - - /* Write the trx number to the undo log header */ - mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr); - /* Write information about delete markings to the undo log header */ - - if (!undo->del_marks) { - mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE, - MLOG_2BYTES, mtr); - } - - if (rseg->last_page_no == FIL_NULL) { - - rseg->last_page_no = undo->hdr_page_no; - rseg->last_offset = undo->hdr_offset; - rseg->last_trx_no = trx->no; - rseg->last_del_marks = undo->del_marks; - } } /**********************************************************************//** @@ -411,7 +423,6 @@ trx_purge_free_segment( /* fputs("Freeing an update undo log segment\n", stderr); */ - ut_ad(mutex_own(&(purge_sys->mutex))); loop: mtr_start(&mtr); mutex_enter(&(rseg->mutex)); @@ -515,8 +526,6 @@ trx_purge_truncate_rseg_history( mtr_t mtr; trx_id_t undo_trx_no; - ut_ad(mutex_own(&(purge_sys->mutex))); - mtr_start(&mtr); mutex_enter(&(rseg->mutex)); @@ -609,10 +618,8 @@ trx_purge_truncate_history(void) trx_id_t limit_trx_no; undo_no_t limit_undo_no; - ut_ad(mutex_own(&(purge_sys->mutex))); - - trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no, - &limit_undo_no); + trx_purge_arr_get_biggest( + purge_sys->arr, &limit_trx_no, &limit_undo_no); if (limit_trx_no == 0) { @@ -630,34 +637,29 @@ trx_purge_truncate_history(void) ut_ad(limit_trx_no <= purge_sys->view->low_limit_no); - rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); + for (rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); + rseg != NULL; + rseg = UT_LIST_GET_NEXT(rseg_list, rseg)) { - while (rseg) { - trx_purge_truncate_rseg_history(rseg, limit_trx_no, - limit_undo_no); - rseg = UT_LIST_GET_NEXT(rseg_list, rseg); + trx_purge_truncate_rseg_history( + rseg, limit_trx_no, limit_undo_no); } } /********************************************************************//** Does a truncate if the purge array is empty. NOTE that when this function is -called, the caller must not have any latches on undo log pages! -@return TRUE if array empty */ +called, the caller must not have any latches on undo log pages! */ UNIV_INLINE -ibool +void trx_purge_truncate_if_arr_empty(void) /*=================================*/ { - ut_ad(mutex_own(&(purge_sys->mutex))); + static ulint count; - if (purge_sys->arr->n_used == 0) { + if (!(++count % TRX_SYS_N_RSEGS) && purge_sys->arr->n_used == 0) { trx_purge_truncate_history(); - - return(TRUE); } - - return(FALSE); } /***********************************************************************//** @@ -675,8 +677,8 @@ trx_purge_rseg_get_next_history_log( trx_id_t trx_no; ibool del_marks; mtr_t mtr; - - ut_ad(mutex_own(&(purge_sys->mutex))); + rseg_queue_t rseg_queue; + const void* ptr; mutex_enter(&(rseg->mutex)); @@ -688,8 +690,9 @@ trx_purge_rseg_get_next_history_log( mtr_start(&mtr); - undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size, - rseg->last_page_no, &mtr); + undo_page = trx_undo_page_get_s_latched( + rseg->space, rseg->zip_size, rseg->last_page_no, &mtr); + log_hdr = undo_page + rseg->last_offset; /* Increase the purge page count by one for every handled log */ @@ -698,6 +701,7 @@ trx_purge_rseg_get_next_history_log( prev_log_addr = trx_purge_get_log_from_hist( flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr)); + if (prev_log_addr.page == FIL_NULL) { /* No logs left in the history list */ @@ -712,11 +716,11 @@ trx_purge_rseg_get_next_history_log( on the MySQL mailing list on Nov 9, 2004. The fut0lst.c file-based list was corrupt. The prev node pointer was FIL_NULL, even though the list length was over 8 million nodes! - We assume that purge truncates the history list in moderate + We assume that purge truncates the history list in large size pieces, and if we here reach the head of the list, the - list cannot be longer than 20 000 undo logs now. */ + list cannot be longer than 2000 000 undo logs now. */ - if (trx_sys->rseg_history_len > 20000) { + if (trx_sys->rseg_history_len > 2000000) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Warning: purge reached the" @@ -756,9 +760,127 @@ trx_purge_rseg_get_next_history_log( rseg->last_trx_no = trx_no; rseg->last_del_marks = del_marks; + rseg_queue.rseg = rseg; + rseg_queue.trx_no = rseg->last_trx_no; + + /* Purge can also produce events, however these are already ordered + in the rollback segment and any user generated event will be greater + than the events that Purge produces. ie. Purge can never produce + events from an empty rollback segment. */ + + mutex_enter(&purge_sys->bh_mutex); + + ptr = ib_bh_push(purge_sys->ib_bh, &rseg_queue); + ut_a(ptr != NULL); + + mutex_exit(&purge_sys->bh_mutex); + mutex_exit(&(rseg->mutex)); } +/***********************************************************************//** +Chooses the rollback segment with the smallest trx_id. +@return zip_size if log is for a compressed table, ULINT_UNDEFINED if + no rollback segments to purge, 0 for non compressed tables. */ +static +ulint +trx_purge_get_rseg_with_min_trx_id( +/*===============================*/ + trx_purge_t* purge_sys) /*!< in/out: purge instance */ + +{ + ulint zip_size = 0; + + mutex_enter(&purge_sys->bh_mutex); + + /* Only purge consumes events from the binary heap, user + threads only produce the events. */ + + if (!ib_bh_is_empty(purge_sys->ib_bh)) { + trx_rseg_t* rseg; + + rseg = ((rseg_queue_t*) ib_bh_first(purge_sys->ib_bh))->rseg; + ib_bh_pop(purge_sys->ib_bh); + + mutex_exit(&purge_sys->bh_mutex); + + purge_sys->rseg = rseg; + } else { + mutex_exit(&purge_sys->bh_mutex); + + purge_sys->rseg = NULL; + + return(ULINT_UNDEFINED); + } + + ut_a(purge_sys->rseg != NULL); + + mutex_enter(&purge_sys->rseg->mutex); + + ut_a(purge_sys->rseg->last_page_no != FIL_NULL); + + /* We assume in purge of externally stored fields + that space id == 0 */ + ut_a(purge_sys->rseg->space == 0); + + zip_size = purge_sys->rseg->zip_size; + + ut_a(purge_sys->purge_trx_no <= purge_sys->rseg->last_trx_no); + + purge_sys->purge_trx_no = purge_sys->rseg->last_trx_no; + + purge_sys->hdr_offset = purge_sys->rseg->last_offset; + + purge_sys->hdr_page_no = purge_sys->rseg->last_page_no; + + mutex_exit(&purge_sys->rseg->mutex); + + return(zip_size); +} + +/***********************************************************************//** +Position the purge sys "iterator" on the undo record to use for purging. */ +static +void +trx_purge_read_undo_rec( +/*====================*/ + trx_purge_t* purge_sys, /*!< in/out: purge instance */ + ulint zip_size) /*!< in: block size or 0 */ +{ + ulint page_no; + ulint offset = 0; + ib_uint64_t undo_no = 0; + + purge_sys->hdr_offset = purge_sys->rseg->last_offset; + page_no = purge_sys->hdr_page_no = purge_sys->rseg->last_page_no; + + if (purge_sys->rseg->last_del_marks) { + mtr_t mtr; + trx_undo_rec_t* undo_rec; + + mtr_start(&mtr); + + undo_rec = trx_undo_get_first_rec( + 0 /* System space id */, zip_size, + purge_sys->hdr_page_no, + purge_sys->hdr_offset, RW_S_LATCH, &mtr); + + if (undo_rec != NULL) { + offset = page_offset(undo_rec); + undo_no = trx_undo_rec_get_undo_no(undo_rec); + page_no = page_get_page_no(page_align(undo_rec)); + } + + mtr_commit(&mtr); + } + + purge_sys->offset = offset; + purge_sys->page_no = page_no; + purge_sys->purge_undo_no = undo_no; + + purge_sys->next_stored = TRUE; +} + /***********************************************************************//** Chooses the next undo log to purge and updates the info in purge_sys. This function is used to initialize purge_sys when the next record to purge is @@ -769,92 +891,19 @@ void trx_purge_choose_next_log(void) /*===========================*/ { - trx_undo_rec_t* rec; - trx_rseg_t* rseg; - trx_rseg_t* min_rseg; - trx_id_t min_trx_no; - ulint space = 0; /* remove warning (??? bug ???) */ - ulint zip_size = 0; - ulint page_no = 0; /* remove warning (??? bug ???) */ - ulint offset = 0; /* remove warning (??? bug ???) */ - mtr_t mtr; + ulint zip_size; - ut_ad(mutex_own(&(purge_sys->mutex))); ut_ad(purge_sys->next_stored == FALSE); - rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); + zip_size = trx_purge_get_rseg_with_min_trx_id(purge_sys); - min_trx_no = IB_ULONGLONG_MAX; + if (purge_sys->rseg != NULL) { - min_rseg = NULL; - - while (rseg) { - mutex_enter(&(rseg->mutex)); - - if (rseg->last_page_no != FIL_NULL) { - - if (min_rseg == NULL - || min_trx_no > rseg->last_trx_no) { - - min_rseg = rseg; - min_trx_no = rseg->last_trx_no; - space = rseg->space; - zip_size = rseg->zip_size; - ut_a(space == 0); /* We assume in purge of - externally stored fields - that space id == 0 */ - page_no = rseg->last_page_no; - offset = rseg->last_offset; - } - } - - mutex_exit(&(rseg->mutex)); - - rseg = UT_LIST_GET_NEXT(rseg_list, rseg); - } - - if (min_rseg == NULL) { - - return; - } - - mtr_start(&mtr); - - if (!min_rseg->last_del_marks) { - /* No need to purge this log */ - - rec = &trx_purge_dummy_rec; + trx_purge_read_undo_rec(purge_sys, zip_size); } else { - rec = trx_undo_get_first_rec(space, zip_size, page_no, offset, - RW_S_LATCH, &mtr); - if (rec == NULL) { - /* Undo log empty */ - - rec = &trx_purge_dummy_rec; - } + /* There is nothing to do yet. */ + os_thread_yield(); } - - purge_sys->next_stored = TRUE; - purge_sys->rseg = min_rseg; - - purge_sys->hdr_page_no = page_no; - purge_sys->hdr_offset = offset; - - purge_sys->purge_trx_no = min_trx_no; - - if (rec == &trx_purge_dummy_rec) { - - purge_sys->purge_undo_no = 0; - purge_sys->page_no = page_no; - purge_sys->offset = 0; - } else { - purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec); - - purge_sys->page_no = page_get_page_no(page_align(rec)); - purge_sys->offset = page_offset(rec); - } - - mtr_commit(&mtr); } /***********************************************************************//** @@ -880,7 +929,6 @@ trx_purge_get_next_rec( ulint cmpl_info; mtr_t mtr; - ut_ad(mutex_own(&(purge_sys->mutex))); ut_ad(purge_sys->next_stored); space = purge_sys->rseg->space; @@ -903,8 +951,8 @@ trx_purge_get_next_rec( mtr_start(&mtr); - undo_page = trx_undo_page_get_s_latched(space, zip_size, - page_no, &mtr); + undo_page = trx_undo_page_get_s_latched(space, zip_size, page_no, &mtr); + rec = undo_page + offset; rec2 = rec; @@ -913,9 +961,9 @@ trx_purge_get_next_rec( /* Try first to find the next record which requires a purge operation from the same page of the same undo log */ - next_rec = trx_undo_page_get_next_rec(rec2, - purge_sys->hdr_page_no, - purge_sys->hdr_offset); + next_rec = trx_undo_page_get_next_rec( + rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset); + if (next_rec == NULL) { rec2 = trx_undo_get_next_rec( rec2, purge_sys->hdr_page_no, @@ -995,17 +1043,12 @@ trx_purge_fetch_next_rec( { trx_undo_rec_t* undo_rec; - mutex_enter(&(purge_sys->mutex)); if (purge_sys->state == TRX_STOP_PURGE) { trx_purge_truncate_if_arr_empty(); - mutex_exit(&(purge_sys->mutex)); - return(NULL); - } - - if (!purge_sys->next_stored) { + } else if (!purge_sys->next_stored) { trx_purge_choose_next_log(); if (!purge_sys->next_stored) { @@ -1020,8 +1063,6 @@ trx_purge_fetch_next_rec( (ulong) purge_sys->n_pages_handled); } - mutex_exit(&(purge_sys->mutex)); - return(NULL); } } @@ -1032,18 +1073,12 @@ trx_purge_fetch_next_rec( trx_purge_truncate_if_arr_empty(); - mutex_exit(&(purge_sys->mutex)); - return(NULL); - } - - if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) { + } else if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); - mutex_exit(&(purge_sys->mutex)); - return(NULL); } @@ -1052,12 +1087,13 @@ trx_purge_fetch_next_rec( (ullint) purge_sys->purge_trx_no, (ullint) purge_sys->purge_undo_no); */ - *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id, - purge_sys->page_no, - purge_sys->offset); - *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no, - purge_sys->purge_undo_no); + *roll_ptr = trx_undo_build_roll_ptr( + FALSE, (purge_sys->rseg)->id, purge_sys->page_no, + purge_sys->offset); + + *cell = trx_purge_arr_store_info( + purge_sys->purge_trx_no, purge_sys->purge_undo_no); ut_ad(purge_sys->purge_trx_no < purge_sys->view->low_limit_no); @@ -1066,8 +1102,6 @@ trx_purge_fetch_next_rec( undo_rec = trx_purge_get_next_rec(heap); - mutex_exit(&(purge_sys->mutex)); - return(undo_rec); } @@ -1079,11 +1113,7 @@ trx_purge_rec_release( /*==================*/ trx_undo_inf_t* cell) /*!< in: storage cell */ { - mutex_enter(&(purge_sys->mutex)); - trx_purge_arr_remove_info(cell); - - mutex_exit(&(purge_sys->mutex)); } /*******************************************************************//** @@ -1097,23 +1127,11 @@ trx_purge( purge in one batch */ { que_thr_t* thr; - /* que_thr_t* thr2; */ ulint old_pages_handled; - mutex_enter(&(purge_sys->mutex)); + ut_a(purge_sys->trx->n_active_thrs == 0); - if (purge_sys->trx->n_active_thrs > 0) { - - mutex_exit(&(purge_sys->mutex)); - - /* Should not happen */ - - ut_error; - - return(0); - } - - rw_lock_x_lock(&(purge_sys->latch)); + rw_lock_x_lock(&purge_sys->latch); mutex_enter(&kernel_mutex); @@ -1147,8 +1165,9 @@ trx_purge( } } - purge_sys->view = read_view_oldest_copy_or_open_new(0, - purge_sys->heap); + purge_sys->view = read_view_oldest_copy_or_open_new( + 0, purge_sys->heap); + mutex_exit(&kernel_mutex); rw_lock_x_unlock(&(purge_sys->latch)); @@ -1159,7 +1178,6 @@ trx_purge( old_pages_handled = purge_sys->n_pages_handled; - mutex_exit(&(purge_sys->mutex)); mutex_enter(&kernel_mutex); @@ -1167,15 +1185,8 @@ trx_purge( ut_ad(thr); - /* thr2 = que_fork_start_command(purge_sys->query); - - ut_ad(thr2); */ - - mutex_exit(&kernel_mutex); - /* srv_que_task_enqueue(thr2); */ - if (srv_print_thread_releases) { fputs("Starting purge\n", stderr); diff --git a/storage/innobase/trx/trx0rseg.c b/storage/innobase/trx/trx0rseg.c index 740320f68c1..85beac8afbc 100644 --- a/storage/innobase/trx/trx0rseg.c +++ b/storage/innobase/trx/trx0rseg.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Oracle Corpn. 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 @@ -50,11 +50,11 @@ trx_rseg_get_on_id( { trx_rseg_t* rseg; - rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); + ut_a(id < TRX_SYS_N_RSEGS); - while (rseg && rseg->id != id) { - rseg = UT_LIST_GET_NEXT(rseg_list, rseg); - } + rseg = trx_sys->rseg_array[id]; + + ut_a(rseg == NULL || id == rseg->id); return(rseg); } @@ -181,12 +181,15 @@ static trx_rseg_t* trx_rseg_mem_create( /*================*/ - ulint id, /*!< in: rollback segment id */ - ulint space, /*!< in: space where the segment placed */ - ulint zip_size, /*!< in: compressed page size in bytes - or 0 for uncompressed pages */ - ulint page_no, /*!< in: page number of the segment header */ - mtr_t* mtr) /*!< in: mtr */ + ulint id, /*!< in: rollback segment id */ + ulint space, /*!< in: space where the segment + placed */ + ulint zip_size, /*!< in: compressed page size in bytes + or 0 for uncompressed pages */ + ulint page_no, /*!< in: page number of the segment + header */ + ib_bh_t* ib_bh, /*!< in/out: rseg queue */ + mtr_t* mtr) /*!< in: mtr */ { ulint len; trx_rseg_t* rseg; @@ -225,6 +228,9 @@ trx_rseg_mem_create( len = flst_get_len(rseg_header + TRX_RSEG_HISTORY, mtr); if (len > 0) { + const void* ptr; + rseg_queue_t rseg_queue; + trx_sys->rseg_history_len += len; node_addr = trx_purge_get_log_from_hist( @@ -240,6 +246,17 @@ trx_rseg_mem_create( undo_log_hdr + TRX_UNDO_TRX_NO); rseg->last_del_marks = mtr_read_ulint( undo_log_hdr + TRX_UNDO_DEL_MARKS, MLOG_2BYTES, mtr); + + rseg_queue.rseg = rseg; + rseg_queue.trx_no = rseg->last_trx_no; + + if (rseg->last_page_no != FIL_NULL) { + /* There is no need to cover this operation by the purge + mutex because we are still bootstrapping. */ + + ptr = ib_bh_push(ib_bh, &rseg_queue); + ut_a(ptr != NULL); + } } else { rseg->last_page_no = FIL_NULL; } @@ -255,6 +272,7 @@ void trx_rseg_create_instance( /*=====================*/ trx_sysf_t* sys_header, /*!< in: trx system header */ + ib_bh_t* ib_bh, /*!< in/out: rseg queue */ mtr_t* mtr) /*!< in: mtr */ { ulint i; @@ -278,7 +296,7 @@ trx_rseg_create_instance( zip_size = space ? fil_space_get_zip_size(space) : 0; rseg = trx_rseg_mem_create( - i, space, zip_size, page_no, mtr); + i, space, zip_size, page_no, ib_bh, mtr); ut_a(rseg->id == i); } @@ -327,7 +345,8 @@ trx_rseg_create(void) zip_size = space ? fil_space_get_zip_size(space) : 0; rseg = trx_rseg_mem_create( - slot_no, space, zip_size, page_no, &mtr); + slot_no, space, zip_size, page_no, + purge_sys->ib_bh, &mtr); } mutex_exit(&kernel_mutex); @@ -342,13 +361,14 @@ UNIV_INTERN void trx_rseg_list_and_array_init( /*=========================*/ - trx_sysf_t* sys_header, /* in: trx system header */ - mtr_t* mtr) /* in: mtr */ + trx_sysf_t* sys_header, /*!< in: trx system header */ + ib_bh_t* ib_bh, /*!< in: rseg queue */ + mtr_t* mtr) /*!< in: mtr */ { UT_LIST_INIT(trx_sys->rseg_list); trx_sys->rseg_history_len = 0; - trx_rseg_create_instance(sys_header, mtr); + trx_rseg_create_instance(sys_header, ib_bh, mtr); } diff --git a/storage/innobase/trx/trx0sys.c b/storage/innobase/trx/trx0sys.c index 101f225a06f..7af9fbb1af8 100644 --- a/storage/innobase/trx/trx0sys.c +++ b/storage/innobase/trx/trx0sys.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -945,6 +945,31 @@ trx_sysf_create( mutex_exit(&kernel_mutex); } +/*****************************************************************//** +Compare two trx_rseg_t instances on last_trx_no. */ +static +int +trx_rseg_compare_last_trx_no( +/*=========================*/ + const void* p1, /*!< in: elem to compare */ + const void* p2) /*!< in: elem to compare */ +{ + ib_int64_t cmp; + + const rseg_queue_t* rseg_q1 = (const rseg_queue_t*) p1; + const rseg_queue_t* rseg_q2 = (const rseg_queue_t*) p2; + + cmp = rseg_q1->trx_no - rseg_q2->trx_no; + + if (cmp < 0) { + return(-1); + } else if (cmp > 0) { + return(1); + } + + return(0); +} + /*****************************************************************//** Creates and initializes the central memory structures for the transaction system. This is called when the database is started. */ @@ -958,6 +983,7 @@ trx_sys_init_at_db_start(void) const char* unit = ""; trx_t* trx; mtr_t mtr; + ib_bh_t* ib_bh; mtr_start(&mtr); @@ -965,11 +991,19 @@ trx_sys_init_at_db_start(void) mutex_enter(&kernel_mutex); - trx_sys = mem_alloc(sizeof(trx_sys_t)); + /* We create the min binary heap here and pass ownership to + purge when we init the purge sub-system. Purge is responsible + for freeing the binary heap. */ + + ib_bh = ib_bh_create( + trx_rseg_compare_last_trx_no, + sizeof(rseg_queue_t), TRX_SYS_N_RSEGS); + + trx_sys = mem_zalloc(sizeof(*trx_sys)); sys_header = trx_sysf_get(&mtr); - trx_rseg_list_and_array_init(sys_header, &mtr); + trx_rseg_list_and_array_init(sys_header, ib_bh, &mtr); trx_sys->latest_rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); @@ -1023,7 +1057,8 @@ trx_sys_init_at_db_start(void) UT_LIST_INIT(trx_sys->view_list); - trx_purge_sys_create(); + /* Transfer ownership to purge. */ + trx_purge_sys_create(ib_bh); mutex_exit(&kernel_mutex); diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index 4bbcee210b0..820c40fe857 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -42,6 +42,7 @@ Created 3/26/1996 Heikki Tuuri #include "btr0sea.h" #include "os0proc.h" #include "trx0xa.h" +#include "trx0purge.h" #include "ha_prototypes.h" /** Dummy session used currently in MySQL interface */ @@ -604,36 +605,26 @@ trx_lists_init_at_db_start(void) /******************************************************************//** Assigns a rollback segment to a transaction in a round-robin fashion. -Skips the SYSTEM rollback segment if another is available. -@return assigned rollback segment id */ +@return assigned rollback segment instance */ UNIV_INLINE -ulint -trx_assign_rseg(void) -/*=================*/ +trx_rseg_t* +trx_assign_rseg( +/*============*/ + ulint max_undo_logs) /*!< in: maximum number of UNDO logs to use */ { - trx_rseg_t* rseg = trx_sys->latest_rseg; + trx_rseg_t* rseg = trx_sys->latest_rseg; ut_ad(mutex_own(&kernel_mutex)); -loop: - /* Get next rseg in a round-robin fashion */ rseg = UT_LIST_GET_NEXT(rseg_list, rseg); - if (rseg == NULL) { + if (rseg == NULL || rseg->id == max_undo_logs - 1) { rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); } - /* If it is the SYSTEM rollback segment, and there exist others, skip - it */ - - if ((rseg->id == TRX_SYS_SYSTEM_RSEG_ID) - && (UT_LIST_GET_LEN(trx_sys->rseg_list) > 1)) { - goto loop; - } - trx_sys->latest_rseg = rseg; - return(rseg->id); + return(rseg); } /****************************************************************//** @@ -663,12 +654,9 @@ trx_start_low( ut_ad(trx->conc_state != TRX_ACTIVE); - if (rseg_id == ULINT_UNDEFINED) { + ut_a(rseg_id == ULINT_UNDEFINED); - rseg_id = trx_assign_rseg(); - } - - rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id); + rseg = trx_assign_rseg(srv_rollback_segments); trx->id = trx_sys_get_new_trx_id(); @@ -718,6 +706,152 @@ trx_start( return(ret); } +/****************************************************************//** +Set the transaction serialisation number. */ +static +void +trx_serialisation_number_get( +/*=========================*/ + trx_t* trx) /*!< in: transaction */ +{ + trx_rseg_t* rseg; + + rseg = trx->rseg; + + ut_ad(mutex_own(&rseg->mutex)); + + mutex_enter(&kernel_mutex); + + trx->no = trx_sys_get_new_trx_id(); + + /* If the rollack segment is not empty then the + new trx_t::no can't be less than any trx_t::no + already in the rollback segment. User threads only + produce events when a rollback segment is empty. */ + + if (rseg->last_page_no == FIL_NULL) { + void* ptr; + rseg_queue_t rseg_queue; + + rseg_queue.rseg = rseg; + rseg_queue.trx_no = trx->no; + + mutex_enter(&purge_sys->bh_mutex); + + /* This is to reduce the pressure on the kernel mutex, + though in reality it should make very little (read no) + difference because this code path is only taken when the + rbs is empty. */ + + mutex_exit(&kernel_mutex); + + ptr = ib_bh_push(purge_sys->ib_bh, &rseg_queue); + ut_a(ptr); + + mutex_exit(&purge_sys->bh_mutex); + } else { + mutex_exit(&kernel_mutex); + } +} + +/****************************************************************//** +Assign the transaction its history serialisation number and write the +update UNDO log record to the assigned rollback segment. +@return the LSN of the UNDO log write. */ +static +ib_uint64_t +trx_write_serialisation_history( +/*============================*/ + trx_t* trx) /*!< in: transaction */ +{ + mtr_t mtr; + trx_rseg_t* rseg; + + ut_ad(!mutex_own(&kernel_mutex)); + + rseg = trx->rseg; + + mtr_start(&mtr); + + /* Change the undo log segment states from TRX_UNDO_ACTIVE + to some other state: these modifications to the file data + structure define the transaction as committed in the file + based domain, at the serialization point of the log sequence + number lsn obtained below. */ + + if (trx->update_undo != NULL) { + page_t* undo_hdr_page; + trx_undo_t* undo = trx->update_undo; + + /* We have to hold the rseg mutex because update + log headers have to be put to the history list in the + (serialisation) order of the UNDO trx number. This is + required for the purge in-memory data structures too. */ + + mutex_enter(&rseg->mutex); + + /* Assign the transaction serialisation number and also + update the purge min binary heap if this is the first + UNDO log being written to the assigned rollback segment. */ + + trx_serialisation_number_get(trx); + + /* It is not necessary to obtain trx->undo_mutex here + because only a single OS thread is allowed to do the + transaction commit for this transaction. */ + + undo_hdr_page = trx_undo_set_state_at_finish(undo, &mtr); + + trx_undo_update_cleanup(trx, undo_hdr_page, &mtr); + } else { + mutex_enter(&rseg->mutex); + } + + if (trx->insert_undo != NULL) { + trx_undo_set_state_at_finish(trx->insert_undo, &mtr); + } + + mutex_exit(&rseg->mutex); + + /* Update the latest MySQL binlog name and offset info + in trx sys header if MySQL binlogging is on or the database + server is a MySQL replication slave */ + + if (trx->mysql_log_file_name + && trx->mysql_log_file_name[0] != '\0') { + + trx_sys_update_mysql_binlog_offset( + trx->mysql_log_file_name, + trx->mysql_log_offset, + TRX_SYS_MYSQL_LOG_INFO, &mtr); + + trx->mysql_log_file_name = NULL; + } + + /* The following call commits the mini-transaction, making the + whole transaction committed in the file-based world, at this + log sequence number. The transaction becomes 'durable' when + we write the log to disk, but in the logical sense the commit + in the file-based data structures (undo logs etc.) happens + here. + + NOTE that transaction numbers, which are assigned only to + transactions with an update undo log, do not necessarily come + in exactly the same order as commit lsn's, if the transactions + have different rollback segments. To get exactly the same + order we should hold the kernel mutex up to this point, + adding to the contention of the kernel mutex. However, if + a transaction T2 is able to see modifications made by + a transaction T1, T2 will always get a bigger transaction + number and a bigger commit lsn than T1. */ + + /*--------------*/ + mtr_commit(&mtr); + /*--------------*/ + + return(mtr.end_lsn); +} + /****************************************************************//** Commits a transaction. */ UNIV_INTERN @@ -726,100 +860,26 @@ trx_commit_off_kernel( /*==================*/ trx_t* trx) /*!< in: transaction */ { - page_t* update_hdr_page; - ib_uint64_t lsn = 0; - trx_rseg_t* rseg; - trx_undo_t* undo; - mtr_t mtr; + ib_uint64_t lsn; ut_ad(mutex_own(&kernel_mutex)); trx->must_flush_log_later = FALSE; - rseg = trx->rseg; + /* If the transaction made any updates then we need to write the + UNDO logs for the updates to the assigned rollback segment. */ if (trx->insert_undo != NULL || trx->update_undo != NULL) { - mutex_exit(&kernel_mutex); - mtr_start(&mtr); - - /* Change the undo log segment states from TRX_UNDO_ACTIVE - to some other state: these modifications to the file data - structure define the transaction as committed in the file - based world, at the serialization point of the log sequence - number lsn obtained below. */ - - mutex_enter(&(rseg->mutex)); - - if (trx->insert_undo != NULL) { - trx_undo_set_state_at_finish(trx->insert_undo, &mtr); - } - - undo = trx->update_undo; - - if (undo) { - mutex_enter(&kernel_mutex); - trx->no = trx_sys_get_new_trx_no(); - mutex_exit(&kernel_mutex); - - /* It is not necessary to obtain trx->undo_mutex here - because only a single OS thread is allowed to do the - transaction commit for this transaction. */ - - update_hdr_page = trx_undo_set_state_at_finish( - undo, &mtr); - - /* We have to do the cleanup for the update log while - holding the rseg mutex because update log headers - have to be put to the history list in the order of - the trx number. */ - - trx_undo_update_cleanup(trx, update_hdr_page, &mtr); - } - - mutex_exit(&(rseg->mutex)); - - /* Update the latest MySQL binlog name and offset info - in trx sys header if MySQL binlogging is on or the database - server is a MySQL replication slave */ - - if (trx->mysql_log_file_name - && trx->mysql_log_file_name[0] != '\0') { - trx_sys_update_mysql_binlog_offset( - trx->mysql_log_file_name, - trx->mysql_log_offset, - TRX_SYS_MYSQL_LOG_INFO, &mtr); - trx->mysql_log_file_name = NULL; - } - - /* The following call commits the mini-transaction, making the - whole transaction committed in the file-based world, at this - log sequence number. The transaction becomes 'durable' when - we write the log to disk, but in the logical sense the commit - in the file-based data structures (undo logs etc.) happens - here. - - NOTE that transaction numbers, which are assigned only to - transactions with an update undo log, do not necessarily come - in exactly the same order as commit lsn's, if the transactions - have different rollback segments. To get exactly the same - order we should hold the kernel mutex up to this point, - adding to the contention of the kernel mutex. However, if - a transaction T2 is able to see modifications made by - a transaction T1, T2 will always get a bigger transaction - number and a bigger commit lsn than T1. */ - - /*--------------*/ - mtr_commit(&mtr); - /*--------------*/ - lsn = mtr.end_lsn; + lsn = trx_write_serialisation_history(trx); mutex_enter(&kernel_mutex); + } else { + lsn = 0; } - ut_ad(trx->conc_state == TRX_ACTIVE - || trx->conc_state == TRX_PREPARED); + ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED); ut_ad(mutex_own(&kernel_mutex)); /* The following assignment makes the transaction committed in memory diff --git a/storage/innobase/ut/ut0ut.c b/storage/innobase/ut/ut0ut.c index 4f4dfc5eed8..cd0894b132a 100644 --- a/storage/innobase/ut/ut0ut.c +++ b/storage/innobase/ut/ut0ut.c @@ -1,13 +1,6 @@ /***************************************************************************** -Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. -Copyright (c) 2009, Sun Microsystems, Inc. - -Portions of this file contain modifications contributed and copyrighted by -Sun Microsystems, Inc. Those modifications are gratefully acknowledged and -are described briefly in the InnoDB documentation. The contributions by -Sun Microsystems are incorporated with their permission, and subject to the -conditions contained in the file COPYING.Sun_Microsystems. +Copyright (c) 2011, Oracle Corpn. 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 From 83659a7fbae86563c9cad6c8e6e5ae25fa2a041c Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Tue, 22 Feb 2011 20:17:02 +1100 Subject: [PATCH 55/64] Add files that were missed in bug# 11798085 commit. --- storage/innobase/include/ut0bh.h | 152 ++++++++++++++++++++++++++++ storage/innobase/ut/ut0bh.c | 164 +++++++++++++++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 storage/innobase/include/ut0bh.h create mode 100644 storage/innobase/ut/ut0bh.c diff --git a/storage/innobase/include/ut0bh.h b/storage/innobase/include/ut0bh.h new file mode 100644 index 00000000000..1b211390283 --- /dev/null +++ b/storage/innobase/include/ut0bh.h @@ -0,0 +1,152 @@ +/***************************************************************************//** + +Copyright (c) 2011, Oracle Corpn. 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 +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +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 + +*****************************************************************************/ + +/******************************************************************//** +@file include/ut0bh.h +Binary min-heap interface. + +Created 2010-05-28 by Sunny Bains +*******************************************************/ + +#ifndef INNOBASE_UT0BH_H +#define INNOBASE_UT0BH_H + +#include "univ.i" + +/** Comparison function for objects in the binary heap. */ +typedef int (*ib_bh_cmp_t)(const void* p1, const void* p2); + +typedef struct ib_bh_struct ib_bh_t; + +/**********************************************************************//** +Get the number of elements in the binary heap. +@return number of elements */ +UNIV_INLINE +ulint +ib_bh_size( +/*=======*/ + const ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Test if binary heap is empty. +@return TRUE if empty. */ +UNIV_INLINE +ibool +ib_bh_is_empty( +/*===========*/ + const ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Test if binary heap is full. +@return TRUE if full. */ +UNIV_INLINE +ibool +ib_bh_is_full( +/*===========*/ + const ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Get a pointer to the element. +@return pointer to element */ +UNIV_INLINE +void* +ib_bh_get( +/*=======*/ + ib_bh_t* ib_bh, /*!< in: instance */ + ulint i); /*!< in: index */ + +/**********************************************************************//** +Copy an element to the binary heap. +@return pointer to copied element */ +UNIV_INLINE +void* +ib_bh_set( +/*======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + ulint i, /*!< in: index */ + const void* elem); /*!< in: element to add */ + +/**********************************************************************//** +Return the first element from the binary heap. +@return pointer to first element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_first( +/*========*/ + ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Return the last element from the binary heap. +@return pointer to last element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_last( +/*========*/ + ib_bh_t* ib_bh); /*!< in/out: instance */ + +/**********************************************************************//** +Create a binary heap. +@return a new binary heap */ +UNIV_INTERN +ib_bh_t* +ib_bh_create( +/*=========*/ + ib_bh_cmp_t compare, /*!< in: comparator */ + ulint sizeof_elem, /*!< in: size of one element */ + ulint max_elems); /*!< in: max elements allowed */ + +/**********************************************************************//** +Free a binary heap. +@return a new binary heap */ +UNIV_INTERN +void +ib_bh_free( +/*=======*/ + ib_bh_t* ib_bh); /*!< in,own: instance */ + +/**********************************************************************//** +Add an element to the binary heap. Note: The element is copied. +@return pointer to added element or NULL if full. */ +UNIV_INTERN +void* +ib_bh_push( +/*=======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + const void* elem); /*!< in: element to add */ + +/**********************************************************************//** +Remove the first element from the binary heap. */ +UNIV_INTERN +void +ib_bh_pop( +/*======*/ + ib_bh_t* ib_bh); /*!< in/out: instance */ + +/** Binary heap data structure */ +struct ib_bh_struct { + ulint max_elems; /*!< max elements allowed */ + ulint n_elems; /*!< current size */ + ulint sizeof_elem; /*!< sizeof element */ + ib_bh_cmp_t compare; /*!< comparator */ +}; + +#ifndef UNIV_NONINL +#include "ut0bh.ic" +#endif + +#endif /* INNOBASE_UT0BH_H */ diff --git a/storage/innobase/ut/ut0bh.c b/storage/innobase/ut/ut0bh.c new file mode 100644 index 00000000000..ae0b1aff207 --- /dev/null +++ b/storage/innobase/ut/ut0bh.c @@ -0,0 +1,164 @@ +/***************************************************************************//** +Copyright (c) 2010, 2011, Oracle Corpn. All Rights Reserved. + +Portions of this file contain modifications contributed and copyrighted by +Sun Microsystems, Inc. Those modifications are gratefully acknowledged and +are described briefly in the InnoDB documentation. The contributions by +Sun Microsystems are incorporated with their permission, and subject to the +conditions contained in the file COPYING.Sun_Microsystems. + +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 +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +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 + +*****************************************************************************/ + +/******************************************************************//** +@file ut/ut0bh.c +Binary min-heap implementation. + +Created 2010-05-28 by Sunny Bains +*******************************************************/ + +#include "ut0bh.h" +#include "ut0mem.h" + +#ifdef UNIV_NONINL +#include "ut0bh.ic" +#endif + +#include + +/**********************************************************************//** +Create a binary heap. +@return a new binary heap */ +UNIV_INTERN +ib_bh_t* +ib_bh_create( +/*=========*/ + ib_bh_cmp_t compare, /*!< in: comparator */ + ulint sizeof_elem, /*!< in: size of one element */ + ulint max_elems) /*!< in: max elements allowed */ +{ + ulint sz; + ib_bh_t* ib_bh; + + sz = sizeof(*ib_bh) + (sizeof_elem * max_elems); + + ib_bh = (ib_bh_t*) ut_malloc(sz); + memset(ib_bh, 0x0, sz); + + ib_bh->compare = compare; + ib_bh->max_elems = max_elems; + ib_bh->sizeof_elem = sizeof_elem; + + return(ib_bh); +} + +/**********************************************************************//** +Free a binary heap. +@return a new binary heap */ +UNIV_INTERN +void +ib_bh_free( +/*=======*/ + ib_bh_t* ib_bh) /*!< in/own: instance */ +{ + ut_free(ib_bh); +} + +/**********************************************************************//** +Add an element to the binary heap. Note: The element is copied. +@return pointer to added element or NULL if full. */ +UNIV_INTERN +void* +ib_bh_push( +/*=======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + const void* elem) /*!< in: element to add */ +{ + void* ptr; + + if (ib_bh_is_full(ib_bh)) { + return(NULL); + } else if (ib_bh_is_empty(ib_bh)) { + ++ib_bh->n_elems; + return(ib_bh_set(ib_bh, 0, elem)); + } else { + ulint i; + + i = ib_bh->n_elems; + + ++ib_bh->n_elems; + + for (ptr = ib_bh_get(ib_bh, i >> 1); + i > 0 && ib_bh->compare(ptr, elem) > 0; + i >>= 1, ptr = ib_bh_get(ib_bh, i >> 1)) { + + ib_bh_set(ib_bh, i, ptr); + } + + ptr = ib_bh_set(ib_bh, i, elem); + } + + return(ptr); +} + +/**********************************************************************//** +Remove the first element from the binary heap. */ +UNIV_INTERN +void +ib_bh_pop( +/*======*/ + ib_bh_t* ib_bh) /*!< in/out: instance */ +{ + byte* ptr; + byte* last; + ulint parent = 0; + + if (ib_bh_is_empty(ib_bh)) { + return; + } else if (ib_bh_size(ib_bh) == 1) { + --ib_bh->n_elems; + return; + } + + last = (byte*) ib_bh_last(ib_bh); + + /* Start from the child node */ + ptr = (byte*) ib_bh_get(ib_bh, 1); + + while (ptr < last) { + /* If the "right" child node is < "left" child node */ + if (ib_bh->compare(ptr + ib_bh->sizeof_elem, ptr) < 0) { + ptr += ib_bh->sizeof_elem; + } + + if (ib_bh->compare(last, ptr) <= 0) { + break; + } + + ib_bh_set(ib_bh, parent, ptr); + + parent = (ptr - (byte*) ib_bh_first(ib_bh)) + / ib_bh->sizeof_elem; + + if ((parent << 1) >= ib_bh_size(ib_bh)) { + break; + } + + ptr = (byte*) ib_bh_get(ib_bh, parent << 1); + } + + --ib_bh->n_elems; + + ib_bh_set(ib_bh, parent, last); +} From dd4723d47db4181e814f5b46391a660a43703e8d Mon Sep 17 00:00:00 2001 From: Nirbhay Choubey Date: Tue, 22 Feb 2011 19:28:50 +0530 Subject: [PATCH 56/64] Postfix for tests failing due to fix for bug#11766310. --- mysql-test/r/ddl_i18n_koi8r.result | 48 +++++++++++++++--------------- mysql-test/r/ddl_i18n_utf8.result | 48 +++++++++++++++--------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/mysql-test/r/ddl_i18n_koi8r.result b/mysql-test/r/ddl_i18n_koi8r.result index fe24c17a1c5..4a4a425362d 100644 --- a/mysql-test/r/ddl_i18n_koi8r.result +++ b/mysql-test/r/ddl_i18n_koi8r.result @@ -724,7 +724,7 @@ utf8_general_ci utf8_general_ci CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER SET cp866 */; USE `mysqltest1`; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -757,8 +757,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -791,7 +791,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest1 to ddl_i18n_koi8r.sp.mysqltest1.sql @@ -800,7 +800,7 @@ ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest2` /*!40100 DEFAULT CHARACTER SET cp866 */; USE `mysqltest2`; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -833,8 +833,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -867,7 +867,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest2 to ddl_i18n_koi8r.sp.mysqltest2.sql @@ -1742,7 +1742,7 @@ CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; INSERT INTO `t1` VALUES (1),(0),(1); -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1770,8 +1770,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1799,7 +1799,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest1 to ddl_i18n_koi8r.triggers.mysqltest1.sql @@ -1821,7 +1821,7 @@ CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; INSERT INTO `t1` VALUES (1),(0),(1); -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1849,8 +1849,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1878,7 +1878,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest2 to ddl_i18n_koi8r.triggers.mysqltest2.sql @@ -2486,7 +2486,7 @@ CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER USE `mysqltest1`; /*!50106 SET @save_time_zone= @@TIME_ZONE */ ; DELIMITER ;; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2512,9 +2512,9 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ;; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2540,7 +2540,7 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ; /*!50106 SET TIME_ZONE= @save_time_zone */ ; @@ -2553,7 +2553,7 @@ CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest2` /*!40100 DEFAULT CHARACTER USE `mysqltest2`; /*!50106 SET @save_time_zone= @@TIME_ZONE */ ; DELIMITER ;; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2579,9 +2579,9 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ;; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2607,7 +2607,7 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ; /*!50106 SET TIME_ZONE= @save_time_zone */ ; diff --git a/mysql-test/r/ddl_i18n_utf8.result b/mysql-test/r/ddl_i18n_utf8.result index cf4272bf90c..7969ccafa09 100644 --- a/mysql-test/r/ddl_i18n_utf8.result +++ b/mysql-test/r/ddl_i18n_utf8.result @@ -724,7 +724,7 @@ utf8_general_ci utf8_general_ci CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER SET cp866 */; USE `mysqltest1`; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -757,8 +757,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -791,7 +791,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest1 to ddl_i18n_utf8sp.mysqltest1.sql @@ -800,7 +800,7 @@ ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest2` /*!40100 DEFAULT CHARACTER SET cp866 */; USE `mysqltest2`; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -833,8 +833,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -867,7 +867,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest2 to ddl_i18n_utf8sp.mysqltest2.sql @@ -1742,7 +1742,7 @@ CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; INSERT INTO `t1` VALUES (1),(0),(1); -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1770,8 +1770,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1799,7 +1799,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest1 to ddl_i18n_utf8triggers.mysqltest1.sql @@ -1821,7 +1821,7 @@ CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; INSERT INTO `t1` VALUES (1),(0),(1); -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1849,8 +1849,8 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; /*!50003 SET @saved_col_connection = @@collation_connection */ ; @@ -1878,7 +1878,7 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ; ---> Dumping mysqltest2 to ddl_i18n_utf8triggers.mysqltest2.sql @@ -2486,7 +2486,7 @@ CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER USE `mysqltest1`; /*!50106 SET @save_time_zone= @@TIME_ZONE */ ; DELIMITER ;; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2512,9 +2512,9 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ;; -ALTER DATABASE mysqltest1 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2540,7 +2540,7 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest1 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest1` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ; /*!50106 SET TIME_ZONE= @save_time_zone */ ; @@ -2553,7 +2553,7 @@ CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest2` /*!40100 DEFAULT CHARACTER USE `mysqltest2`; /*!50106 SET @save_time_zone= @@TIME_ZONE */ ; DELIMITER ;; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2579,9 +2579,9 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ;; -ALTER DATABASE mysqltest2 CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET utf8 COLLATE utf8_unicode_ci ;; /*!50003 SET @saved_cs_client = @@character_set_client */ ;; /*!50003 SET @saved_cs_results = @@character_set_results */ ;; /*!50003 SET @saved_col_connection = @@collation_connection */ ;; @@ -2607,7 +2607,7 @@ END */ ;; /*!50003 SET character_set_client = @saved_cs_client */ ;; /*!50003 SET character_set_results = @saved_cs_results */ ;; /*!50003 SET collation_connection = @saved_col_connection */ ;; -ALTER DATABASE mysqltest2 CHARACTER SET cp866 COLLATE cp866_general_ci ;; +ALTER DATABASE `mysqltest2` CHARACTER SET cp866 COLLATE cp866_general_ci ;; DELIMITER ; /*!50106 SET TIME_ZONE= @save_time_zone */ ; From 2cc164341a55597ce47679f600267ae8753a427c Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Wed, 23 Feb 2011 07:14:49 +1100 Subject: [PATCH 57/64] Remove ut0bh.ic from 5.5 and then re-add so that the file ids are the same in 5.5 and trunk. First we remove the file in the commit. --- storage/innobase/include/ut0bh.ic | 123 ------------------------------ 1 file changed, 123 deletions(-) delete mode 100644 storage/innobase/include/ut0bh.ic diff --git a/storage/innobase/include/ut0bh.ic b/storage/innobase/include/ut0bh.ic deleted file mode 100644 index 10660b29559..00000000000 --- a/storage/innobase/include/ut0bh.ic +++ /dev/null @@ -1,123 +0,0 @@ -/***************************************************************************//** -Copyright (c) 2011, Oracle Corpn. 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 -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -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 - -*****************************************************************************/ - -/******************************************************************//** -@file include/ut0bh.ic -Binary min-heap implementation. - -Created 2011-01-15 by Sunny Bains -*******************************************************/ - -#include "ut0bh.h" - -/**********************************************************************//** -Get the number of elements in the binary heap. -@return number of elements */ -UNIV_INLINE -ulint -ib_bh_size( -/*=======*/ - const ib_bh_t* ib_bh) /*!< in: instance */ -{ - return(ib_bh->n_elems); -} - -/**********************************************************************//** -Test if binary heap is empty. -@return TRUE if empty. */ -UNIV_INLINE -ibool -ib_bh_is_empty( -/*===========*/ - const ib_bh_t* ib_bh) /*!< in: instance */ -{ - return(ib_bh_size(ib_bh) == 0); -} - -/**********************************************************************//** -Test if binary heap is full. -@return TRUE if full. */ -UNIV_INLINE -ibool -ib_bh_is_full( -/*===========*/ - const ib_bh_t* ib_bh) /*!< in: instance */ -{ - return(ib_bh_size(ib_bh) >= ib_bh->max_elems); -} - -/**********************************************************************//** -Get a pointer to the element. -@return pointer to element */ -UNIV_INLINE -void* -ib_bh_get( -/*=======*/ - ib_bh_t* ib_bh, /*!< in: instance */ - ulint i) /*!< in: index */ -{ - byte* ptr = (byte*) (ib_bh + 1); - - ut_a(i < ib_bh_size(ib_bh)); - - return(ptr + (ib_bh->sizeof_elem * i)); -} - -/**********************************************************************//** -Copy an element to the binary heap. -@return pointer to copied element */ -UNIV_INLINE -void* -ib_bh_set( -/*======*/ - ib_bh_t* ib_bh, /*!< in/out: instance */ - ulint i, /*!< in: index */ - const void* elem) /*!< in: element to add */ -{ - void* ptr = ib_bh_get(ib_bh, i); - - ut_memcpy(ptr, elem, ib_bh->sizeof_elem); - - return(ptr); -} - -/**********************************************************************//** -Return the first element from the binary heap. -@return pointer to first element or NULL if empty. */ -UNIV_INLINE -void* -ib_bh_first( -/*========*/ - ib_bh_t* ib_bh) /*!< in: instance */ -{ - return(ib_bh_is_empty(ib_bh) ? NULL : ib_bh_get(ib_bh, 0)); -} - -/**********************************************************************//** -Return the last element from the binary heap. -@return pointer to last element or NULL if empty. */ -UNIV_INLINE -void* -ib_bh_last( -/*========*/ - ib_bh_t* ib_bh) /*!< in/out: instance */ -{ - return(ib_bh_is_empty(ib_bh) - ? NULL - : ib_bh_get(ib_bh, ib_bh_size(ib_bh) - 1)); -} - From 165ccf3be7680dd18de5746bbdbab71a6eda7d61 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Wed, 23 Feb 2011 07:24:34 +1100 Subject: [PATCH 58/64] Add ut0bh.ic. --- storage/innobase/include/ut0bh.ic | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 storage/innobase/include/ut0bh.ic diff --git a/storage/innobase/include/ut0bh.ic b/storage/innobase/include/ut0bh.ic new file mode 100644 index 00000000000..afbe58e7e3b --- /dev/null +++ b/storage/innobase/include/ut0bh.ic @@ -0,0 +1,125 @@ +/***************************************************************************//** +Copyright (c) 2011, Oracle Corpn. 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 +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +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 + +*****************************************************************************/ + +/******************************************************************//** +@file include/ut0bh.ic +Binary min-heap implementation. + +Created 2011-01-15 by Sunny Bains +*******************************************************/ + +#include "ut0bh.h" +#include "ut0mem.h" /* For ut_memcpy() */ + +/**********************************************************************//** +Get the number of elements in the binary heap. +@return number of elements */ +UNIV_INLINE +ulint +ib_bh_size( +/*=======*/ + const ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh->n_elems); +} + +/**********************************************************************//** +Test if binary heap is empty. +@return TRUE if empty. */ +UNIV_INLINE +ibool +ib_bh_is_empty( +/*===========*/ + const ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh_size(ib_bh) == 0); +} + +/**********************************************************************//** +Test if binary heap is full. +@return TRUE if full. */ +UNIV_INLINE +ibool +ib_bh_is_full( +/*===========*/ + const ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh_size(ib_bh) >= ib_bh->max_elems); +} + +/**********************************************************************//** +Get a pointer to the element. +@return pointer to element */ +UNIV_INLINE +void* +ib_bh_get( +/*=======*/ + ib_bh_t* ib_bh, /*!< in: instance */ + ulint i) /*!< in: index */ +{ + byte* ptr = (byte*) (ib_bh + 1); + + ut_a(i < ib_bh_size(ib_bh)); + + return(ptr + (ib_bh->sizeof_elem * i)); +} + +/**********************************************************************//** +Copy an element to the binary heap. +@return pointer to copied element */ +UNIV_INLINE +void* +ib_bh_set( +/*======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + ulint i, /*!< in: index */ + const void* elem) /*!< in: element to add */ +{ + void* ptr = ib_bh_get(ib_bh, i); + + ut_memcpy(ptr, elem, ib_bh->sizeof_elem); + + return(ptr); +} + +/**********************************************************************//** +Return the first element from the binary heap. +@return pointer to first element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_first( +/*========*/ + ib_bh_t* ib_bh) /*!< in: instance */ +{ + return(ib_bh_is_empty(ib_bh) ? NULL : ib_bh_get(ib_bh, 0)); +} + +/**********************************************************************//** +Return the last element from the binary heap. +@return pointer to last element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_last( +/*========*/ + ib_bh_t* ib_bh) /*!< in/out: instance */ +{ + return(ib_bh_is_empty(ib_bh) + ? NULL + : ib_bh_get(ib_bh, ib_bh_size(ib_bh) - 1)); +} + + From 8d8ebc5b016977ce58813a4c34c248f1103731c4 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Wed, 23 Feb 2011 17:44:11 +1100 Subject: [PATCH 59/64] Remove ut0bh.h and ut0bh.c and re-add so that we can sync the file ids with the trunk. --- storage/innobase/include/ut0bh.h | 152 ---------------------------- storage/innobase/ut/ut0bh.c | 164 ------------------------------- 2 files changed, 316 deletions(-) delete mode 100644 storage/innobase/include/ut0bh.h delete mode 100644 storage/innobase/ut/ut0bh.c diff --git a/storage/innobase/include/ut0bh.h b/storage/innobase/include/ut0bh.h deleted file mode 100644 index 1b211390283..00000000000 --- a/storage/innobase/include/ut0bh.h +++ /dev/null @@ -1,152 +0,0 @@ -/***************************************************************************//** - -Copyright (c) 2011, Oracle Corpn. 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 -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -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 - -*****************************************************************************/ - -/******************************************************************//** -@file include/ut0bh.h -Binary min-heap interface. - -Created 2010-05-28 by Sunny Bains -*******************************************************/ - -#ifndef INNOBASE_UT0BH_H -#define INNOBASE_UT0BH_H - -#include "univ.i" - -/** Comparison function for objects in the binary heap. */ -typedef int (*ib_bh_cmp_t)(const void* p1, const void* p2); - -typedef struct ib_bh_struct ib_bh_t; - -/**********************************************************************//** -Get the number of elements in the binary heap. -@return number of elements */ -UNIV_INLINE -ulint -ib_bh_size( -/*=======*/ - const ib_bh_t* ib_bh); /*!< in: instance */ - -/**********************************************************************//** -Test if binary heap is empty. -@return TRUE if empty. */ -UNIV_INLINE -ibool -ib_bh_is_empty( -/*===========*/ - const ib_bh_t* ib_bh); /*!< in: instance */ - -/**********************************************************************//** -Test if binary heap is full. -@return TRUE if full. */ -UNIV_INLINE -ibool -ib_bh_is_full( -/*===========*/ - const ib_bh_t* ib_bh); /*!< in: instance */ - -/**********************************************************************//** -Get a pointer to the element. -@return pointer to element */ -UNIV_INLINE -void* -ib_bh_get( -/*=======*/ - ib_bh_t* ib_bh, /*!< in: instance */ - ulint i); /*!< in: index */ - -/**********************************************************************//** -Copy an element to the binary heap. -@return pointer to copied element */ -UNIV_INLINE -void* -ib_bh_set( -/*======*/ - ib_bh_t* ib_bh, /*!< in/out: instance */ - ulint i, /*!< in: index */ - const void* elem); /*!< in: element to add */ - -/**********************************************************************//** -Return the first element from the binary heap. -@return pointer to first element or NULL if empty. */ -UNIV_INLINE -void* -ib_bh_first( -/*========*/ - ib_bh_t* ib_bh); /*!< in: instance */ - -/**********************************************************************//** -Return the last element from the binary heap. -@return pointer to last element or NULL if empty. */ -UNIV_INLINE -void* -ib_bh_last( -/*========*/ - ib_bh_t* ib_bh); /*!< in/out: instance */ - -/**********************************************************************//** -Create a binary heap. -@return a new binary heap */ -UNIV_INTERN -ib_bh_t* -ib_bh_create( -/*=========*/ - ib_bh_cmp_t compare, /*!< in: comparator */ - ulint sizeof_elem, /*!< in: size of one element */ - ulint max_elems); /*!< in: max elements allowed */ - -/**********************************************************************//** -Free a binary heap. -@return a new binary heap */ -UNIV_INTERN -void -ib_bh_free( -/*=======*/ - ib_bh_t* ib_bh); /*!< in,own: instance */ - -/**********************************************************************//** -Add an element to the binary heap. Note: The element is copied. -@return pointer to added element or NULL if full. */ -UNIV_INTERN -void* -ib_bh_push( -/*=======*/ - ib_bh_t* ib_bh, /*!< in/out: instance */ - const void* elem); /*!< in: element to add */ - -/**********************************************************************//** -Remove the first element from the binary heap. */ -UNIV_INTERN -void -ib_bh_pop( -/*======*/ - ib_bh_t* ib_bh); /*!< in/out: instance */ - -/** Binary heap data structure */ -struct ib_bh_struct { - ulint max_elems; /*!< max elements allowed */ - ulint n_elems; /*!< current size */ - ulint sizeof_elem; /*!< sizeof element */ - ib_bh_cmp_t compare; /*!< comparator */ -}; - -#ifndef UNIV_NONINL -#include "ut0bh.ic" -#endif - -#endif /* INNOBASE_UT0BH_H */ diff --git a/storage/innobase/ut/ut0bh.c b/storage/innobase/ut/ut0bh.c deleted file mode 100644 index ae0b1aff207..00000000000 --- a/storage/innobase/ut/ut0bh.c +++ /dev/null @@ -1,164 +0,0 @@ -/***************************************************************************//** -Copyright (c) 2010, 2011, Oracle Corpn. All Rights Reserved. - -Portions of this file contain modifications contributed and copyrighted by -Sun Microsystems, Inc. Those modifications are gratefully acknowledged and -are described briefly in the InnoDB documentation. The contributions by -Sun Microsystems are incorporated with their permission, and subject to the -conditions contained in the file COPYING.Sun_Microsystems. - -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 -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -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 - -*****************************************************************************/ - -/******************************************************************//** -@file ut/ut0bh.c -Binary min-heap implementation. - -Created 2010-05-28 by Sunny Bains -*******************************************************/ - -#include "ut0bh.h" -#include "ut0mem.h" - -#ifdef UNIV_NONINL -#include "ut0bh.ic" -#endif - -#include - -/**********************************************************************//** -Create a binary heap. -@return a new binary heap */ -UNIV_INTERN -ib_bh_t* -ib_bh_create( -/*=========*/ - ib_bh_cmp_t compare, /*!< in: comparator */ - ulint sizeof_elem, /*!< in: size of one element */ - ulint max_elems) /*!< in: max elements allowed */ -{ - ulint sz; - ib_bh_t* ib_bh; - - sz = sizeof(*ib_bh) + (sizeof_elem * max_elems); - - ib_bh = (ib_bh_t*) ut_malloc(sz); - memset(ib_bh, 0x0, sz); - - ib_bh->compare = compare; - ib_bh->max_elems = max_elems; - ib_bh->sizeof_elem = sizeof_elem; - - return(ib_bh); -} - -/**********************************************************************//** -Free a binary heap. -@return a new binary heap */ -UNIV_INTERN -void -ib_bh_free( -/*=======*/ - ib_bh_t* ib_bh) /*!< in/own: instance */ -{ - ut_free(ib_bh); -} - -/**********************************************************************//** -Add an element to the binary heap. Note: The element is copied. -@return pointer to added element or NULL if full. */ -UNIV_INTERN -void* -ib_bh_push( -/*=======*/ - ib_bh_t* ib_bh, /*!< in/out: instance */ - const void* elem) /*!< in: element to add */ -{ - void* ptr; - - if (ib_bh_is_full(ib_bh)) { - return(NULL); - } else if (ib_bh_is_empty(ib_bh)) { - ++ib_bh->n_elems; - return(ib_bh_set(ib_bh, 0, elem)); - } else { - ulint i; - - i = ib_bh->n_elems; - - ++ib_bh->n_elems; - - for (ptr = ib_bh_get(ib_bh, i >> 1); - i > 0 && ib_bh->compare(ptr, elem) > 0; - i >>= 1, ptr = ib_bh_get(ib_bh, i >> 1)) { - - ib_bh_set(ib_bh, i, ptr); - } - - ptr = ib_bh_set(ib_bh, i, elem); - } - - return(ptr); -} - -/**********************************************************************//** -Remove the first element from the binary heap. */ -UNIV_INTERN -void -ib_bh_pop( -/*======*/ - ib_bh_t* ib_bh) /*!< in/out: instance */ -{ - byte* ptr; - byte* last; - ulint parent = 0; - - if (ib_bh_is_empty(ib_bh)) { - return; - } else if (ib_bh_size(ib_bh) == 1) { - --ib_bh->n_elems; - return; - } - - last = (byte*) ib_bh_last(ib_bh); - - /* Start from the child node */ - ptr = (byte*) ib_bh_get(ib_bh, 1); - - while (ptr < last) { - /* If the "right" child node is < "left" child node */ - if (ib_bh->compare(ptr + ib_bh->sizeof_elem, ptr) < 0) { - ptr += ib_bh->sizeof_elem; - } - - if (ib_bh->compare(last, ptr) <= 0) { - break; - } - - ib_bh_set(ib_bh, parent, ptr); - - parent = (ptr - (byte*) ib_bh_first(ib_bh)) - / ib_bh->sizeof_elem; - - if ((parent << 1) >= ib_bh_size(ib_bh)) { - break; - } - - ptr = (byte*) ib_bh_get(ib_bh, parent << 1); - } - - --ib_bh->n_elems; - - ib_bh_set(ib_bh, parent, last); -} From 669c3e71107e83fc8d6a1d6fe05c4ba222b713fc Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Wed, 23 Feb 2011 17:48:15 +1100 Subject: [PATCH 60/64] Add ut0bh.h and ut0bh.c. --- storage/innobase/include/ut0bh.h | 152 ++++++++++++++++++++++++++++ storage/innobase/ut/ut0bh.c | 164 +++++++++++++++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 storage/innobase/include/ut0bh.h create mode 100644 storage/innobase/ut/ut0bh.c diff --git a/storage/innobase/include/ut0bh.h b/storage/innobase/include/ut0bh.h new file mode 100644 index 00000000000..1b211390283 --- /dev/null +++ b/storage/innobase/include/ut0bh.h @@ -0,0 +1,152 @@ +/***************************************************************************//** + +Copyright (c) 2011, Oracle Corpn. 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 +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +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 + +*****************************************************************************/ + +/******************************************************************//** +@file include/ut0bh.h +Binary min-heap interface. + +Created 2010-05-28 by Sunny Bains +*******************************************************/ + +#ifndef INNOBASE_UT0BH_H +#define INNOBASE_UT0BH_H + +#include "univ.i" + +/** Comparison function for objects in the binary heap. */ +typedef int (*ib_bh_cmp_t)(const void* p1, const void* p2); + +typedef struct ib_bh_struct ib_bh_t; + +/**********************************************************************//** +Get the number of elements in the binary heap. +@return number of elements */ +UNIV_INLINE +ulint +ib_bh_size( +/*=======*/ + const ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Test if binary heap is empty. +@return TRUE if empty. */ +UNIV_INLINE +ibool +ib_bh_is_empty( +/*===========*/ + const ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Test if binary heap is full. +@return TRUE if full. */ +UNIV_INLINE +ibool +ib_bh_is_full( +/*===========*/ + const ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Get a pointer to the element. +@return pointer to element */ +UNIV_INLINE +void* +ib_bh_get( +/*=======*/ + ib_bh_t* ib_bh, /*!< in: instance */ + ulint i); /*!< in: index */ + +/**********************************************************************//** +Copy an element to the binary heap. +@return pointer to copied element */ +UNIV_INLINE +void* +ib_bh_set( +/*======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + ulint i, /*!< in: index */ + const void* elem); /*!< in: element to add */ + +/**********************************************************************//** +Return the first element from the binary heap. +@return pointer to first element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_first( +/*========*/ + ib_bh_t* ib_bh); /*!< in: instance */ + +/**********************************************************************//** +Return the last element from the binary heap. +@return pointer to last element or NULL if empty. */ +UNIV_INLINE +void* +ib_bh_last( +/*========*/ + ib_bh_t* ib_bh); /*!< in/out: instance */ + +/**********************************************************************//** +Create a binary heap. +@return a new binary heap */ +UNIV_INTERN +ib_bh_t* +ib_bh_create( +/*=========*/ + ib_bh_cmp_t compare, /*!< in: comparator */ + ulint sizeof_elem, /*!< in: size of one element */ + ulint max_elems); /*!< in: max elements allowed */ + +/**********************************************************************//** +Free a binary heap. +@return a new binary heap */ +UNIV_INTERN +void +ib_bh_free( +/*=======*/ + ib_bh_t* ib_bh); /*!< in,own: instance */ + +/**********************************************************************//** +Add an element to the binary heap. Note: The element is copied. +@return pointer to added element or NULL if full. */ +UNIV_INTERN +void* +ib_bh_push( +/*=======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + const void* elem); /*!< in: element to add */ + +/**********************************************************************//** +Remove the first element from the binary heap. */ +UNIV_INTERN +void +ib_bh_pop( +/*======*/ + ib_bh_t* ib_bh); /*!< in/out: instance */ + +/** Binary heap data structure */ +struct ib_bh_struct { + ulint max_elems; /*!< max elements allowed */ + ulint n_elems; /*!< current size */ + ulint sizeof_elem; /*!< sizeof element */ + ib_bh_cmp_t compare; /*!< comparator */ +}; + +#ifndef UNIV_NONINL +#include "ut0bh.ic" +#endif + +#endif /* INNOBASE_UT0BH_H */ diff --git a/storage/innobase/ut/ut0bh.c b/storage/innobase/ut/ut0bh.c new file mode 100644 index 00000000000..ae0b1aff207 --- /dev/null +++ b/storage/innobase/ut/ut0bh.c @@ -0,0 +1,164 @@ +/***************************************************************************//** +Copyright (c) 2010, 2011, Oracle Corpn. All Rights Reserved. + +Portions of this file contain modifications contributed and copyrighted by +Sun Microsystems, Inc. Those modifications are gratefully acknowledged and +are described briefly in the InnoDB documentation. The contributions by +Sun Microsystems are incorporated with their permission, and subject to the +conditions contained in the file COPYING.Sun_Microsystems. + +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 +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +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 + +*****************************************************************************/ + +/******************************************************************//** +@file ut/ut0bh.c +Binary min-heap implementation. + +Created 2010-05-28 by Sunny Bains +*******************************************************/ + +#include "ut0bh.h" +#include "ut0mem.h" + +#ifdef UNIV_NONINL +#include "ut0bh.ic" +#endif + +#include + +/**********************************************************************//** +Create a binary heap. +@return a new binary heap */ +UNIV_INTERN +ib_bh_t* +ib_bh_create( +/*=========*/ + ib_bh_cmp_t compare, /*!< in: comparator */ + ulint sizeof_elem, /*!< in: size of one element */ + ulint max_elems) /*!< in: max elements allowed */ +{ + ulint sz; + ib_bh_t* ib_bh; + + sz = sizeof(*ib_bh) + (sizeof_elem * max_elems); + + ib_bh = (ib_bh_t*) ut_malloc(sz); + memset(ib_bh, 0x0, sz); + + ib_bh->compare = compare; + ib_bh->max_elems = max_elems; + ib_bh->sizeof_elem = sizeof_elem; + + return(ib_bh); +} + +/**********************************************************************//** +Free a binary heap. +@return a new binary heap */ +UNIV_INTERN +void +ib_bh_free( +/*=======*/ + ib_bh_t* ib_bh) /*!< in/own: instance */ +{ + ut_free(ib_bh); +} + +/**********************************************************************//** +Add an element to the binary heap. Note: The element is copied. +@return pointer to added element or NULL if full. */ +UNIV_INTERN +void* +ib_bh_push( +/*=======*/ + ib_bh_t* ib_bh, /*!< in/out: instance */ + const void* elem) /*!< in: element to add */ +{ + void* ptr; + + if (ib_bh_is_full(ib_bh)) { + return(NULL); + } else if (ib_bh_is_empty(ib_bh)) { + ++ib_bh->n_elems; + return(ib_bh_set(ib_bh, 0, elem)); + } else { + ulint i; + + i = ib_bh->n_elems; + + ++ib_bh->n_elems; + + for (ptr = ib_bh_get(ib_bh, i >> 1); + i > 0 && ib_bh->compare(ptr, elem) > 0; + i >>= 1, ptr = ib_bh_get(ib_bh, i >> 1)) { + + ib_bh_set(ib_bh, i, ptr); + } + + ptr = ib_bh_set(ib_bh, i, elem); + } + + return(ptr); +} + +/**********************************************************************//** +Remove the first element from the binary heap. */ +UNIV_INTERN +void +ib_bh_pop( +/*======*/ + ib_bh_t* ib_bh) /*!< in/out: instance */ +{ + byte* ptr; + byte* last; + ulint parent = 0; + + if (ib_bh_is_empty(ib_bh)) { + return; + } else if (ib_bh_size(ib_bh) == 1) { + --ib_bh->n_elems; + return; + } + + last = (byte*) ib_bh_last(ib_bh); + + /* Start from the child node */ + ptr = (byte*) ib_bh_get(ib_bh, 1); + + while (ptr < last) { + /* If the "right" child node is < "left" child node */ + if (ib_bh->compare(ptr + ib_bh->sizeof_elem, ptr) < 0) { + ptr += ib_bh->sizeof_elem; + } + + if (ib_bh->compare(last, ptr) <= 0) { + break; + } + + ib_bh_set(ib_bh, parent, ptr); + + parent = (ptr - (byte*) ib_bh_first(ib_bh)) + / ib_bh->sizeof_elem; + + if ((parent << 1) >= ib_bh_size(ib_bh)) { + break; + } + + ptr = (byte*) ib_bh_get(ib_bh, parent << 1); + } + + --ib_bh->n_elems; + + ib_bh_set(ib_bh, parent, last); +} From 759d346e6728c3bebbbed69651cced96447bf0c7 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Wed, 23 Feb 2011 17:56:37 +1100 Subject: [PATCH 61/64] Revert the max value of innodb_purge_batch_size to 5000. --- storage/innobase/handler/ha_innodb.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index e4d041bf915..34c4500a723 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -10975,7 +10975,7 @@ static MYSQL_SYSVAR_ULONG(purge_batch_size, srv_purge_batch_size, NULL, NULL, 20, /* Default setting */ 1, /* Minimum value */ - 10000, 0); /* Maximum value */ + 5000, 0); /* Maximum value */ static MYSQL_SYSVAR_ULONG(rollback_segments, srv_rollback_segments, PLUGIN_VAR_OPCMDARG, From 213e414f5841e70baa06dfa6b4e2e4e60cf1d6d9 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 23 Feb 2011 14:50:59 +0100 Subject: [PATCH 62/64] Bug #11752069 (former bug 43152) Assertion `bitmap_is_set_all(&table->s->all_set)' failed in handler::ha_reset Post-push fix for unit test compilation error on Windows. --- unittest/mysys/bitmap-t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/mysys/bitmap-t.c b/unittest/mysys/bitmap-t.c index d0df9fbb040..a9ea7927db3 100644 --- a/unittest/mysys/bitmap-t.c +++ b/unittest/mysys/bitmap-t.c @@ -429,7 +429,7 @@ my_bool test_intersect(MY_BITMAP *map, uint bitsize) { uint bitsize2 = 1 + get_rand_bit(MAX_TESTED_BITMAP_SIZE - 1); MY_BITMAP map2; - uint32 map2buf[bitsize2]; + uint32 map2buf[MAX_TESTED_BITMAP_SIZE]; uint i, test_bit1, test_bit2, test_bit3; if (bitmap_init(&map2, map2buf, bitsize2, FALSE)) { From 700bec21fc3c940f19c0bea6cade0d5e14a28549 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 23 Feb 2011 16:39:54 +0200 Subject: [PATCH 63/64] Fix the failing sys_vars.all_vars on behalf of Sunny --- mysql-test/suite/sys_vars/r/all_vars.result | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql-test/suite/sys_vars/r/all_vars.result b/mysql-test/suite/sys_vars/r/all_vars.result index 418330f078d..fae91659ecd 100644 --- a/mysql-test/suite/sys_vars/r/all_vars.result +++ b/mysql-test/suite/sys_vars/r/all_vars.result @@ -11,8 +11,10 @@ There should be *no* long test name listed below: select variable_name as `There should be *no* variables listed below:` from t2 left join t1 on variable_name=test_name where test_name is null; There should be *no* variables listed below: +INNODB_ROLLBACK_SEGMENTS INNODB_STATS_METHOD INNODB_FILE_FORMAT_MAX +INNODB_ROLLBACK_SEGMENTS INNODB_STATS_METHOD INNODB_FILE_FORMAT_MAX drop table t1; From 4b86ca7f69b0a4176a545e8e55e95ca473fbd587 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Thu, 24 Feb 2011 08:00:12 +0100 Subject: [PATCH 64/64] Bug #11747102 30771: LOG MORE INFO ABOUT THREADS KILL'D AND SORT ABORTED MESSAGES --- mysql-test/r/filesort_debug.result | 17 ++++++++++++++ mysql-test/t/filesort_debug.test | 36 ++++++++++++++++++++++++++++++ sql/filesort.cc | 7 ++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/filesort_debug.result b/mysql-test/r/filesort_debug.result index cb62e5e7a21..c35a6753353 100644 --- a/mysql-test/r/filesort_debug.result +++ b/mysql-test/r/filesort_debug.result @@ -14,3 +14,20 @@ DELETE FROM t1 ORDER BY (f1(10)) LIMIT 1; ERROR 42000: Incorrect number of arguments for FUNCTION test.f1; expected 0, got 1 DROP TABLE t1; DROP FUNCTION f1; +# +# Bug #11747102 +# 30771: LOG MORE INFO ABOUT THREADS KILL'D AND SORT ABORTED MESSAGES +# +# connection 1 +CREATE TABLE t1(f0 int auto_increment primary key, f1 int); +INSERT INTO t1(f1) VALUES (0),(1),(2),(3),(4),(5); +SET DEBUG_SYNC='filesort_start SIGNAL filesort_started WAIT_FOR filesort_killed'; +# Sending: (not reaped since connection is killed later) +SELECT * FROM t1 ORDER BY f1 ASC, f0; +# connection 2 +SET DEBUG_SYNC='now WAIT_FOR filesort_started'; +KILL @id; +SET DEBUG_SYNC='now SIGNAL filesort_killed'; +# connection default +SET DEBUG_SYNC= "RESET"; +DROP TABLE t1; diff --git a/mysql-test/t/filesort_debug.test b/mysql-test/t/filesort_debug.test index 0058a6a3aa7..724ebc90368 100644 --- a/mysql-test/t/filesort_debug.test +++ b/mysql-test/t/filesort_debug.test @@ -1,4 +1,6 @@ --source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/count_sessions.inc SET @old_debug= @@session.debug; @@ -21,3 +23,37 @@ DELETE FROM t1 ORDER BY (f1(10)) LIMIT 1; DROP TABLE t1; DROP FUNCTION f1; + +--echo # +--echo # Bug #11747102 +--echo # 30771: LOG MORE INFO ABOUT THREADS KILL'D AND SORT ABORTED MESSAGES +--echo # + +connect (con1, localhost, root); +connect (con2, localhost, root); + +--echo # connection 1 +connection con1; +CREATE TABLE t1(f0 int auto_increment primary key, f1 int); +INSERT INTO t1(f1) VALUES (0),(1),(2),(3),(4),(5); + +let $ID= `SELECT @id := CONNECTION_ID()`; + +SET DEBUG_SYNC='filesort_start SIGNAL filesort_started WAIT_FOR filesort_killed'; +--echo # Sending: (not reaped since connection is killed later) +--send SELECT * FROM t1 ORDER BY f1 ASC, f0 + +--echo # connection 2 +connection con2; +let $ignore= `SELECT @id := $ID`; +SET DEBUG_SYNC='now WAIT_FOR filesort_started'; +KILL @id; +SET DEBUG_SYNC='now SIGNAL filesort_killed'; + +--echo # connection default +connection default; +disconnect con1; +disconnect con2; +--source include/wait_until_count_sessions.inc +SET DEBUG_SYNC= "RESET"; +DROP TABLE t1; diff --git a/sql/filesort.cc b/sql/filesort.cc index cf5334cd87f..c3250b6e699 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -32,6 +32,7 @@ #include "probes_mysql.h" #include "sql_test.h" // TEST_filesort #include "opt_range.h" // SQL_SELECT +#include "debug_sync.h" /// How to write record_ref. #define WRITE_REF(file,from) \ @@ -123,6 +124,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, Item_subselect *subselect= tab ? tab->containing_subselect() : 0; MYSQL_FILESORT_START(table->s->db.str, table->s->table_name.str); + DEBUG_SYNC(thd, "filesort_start"); /* Release InnoDB's adaptive hash index latch (if holding) before @@ -325,12 +327,13 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, } if (error) { - DBUG_ASSERT(thd->is_error()); + int kill_errno= thd->killed_errno(); + DBUG_ASSERT(thd->is_error() || kill_errno); my_printf_error(ER_FILSORT_ABORT, "%s: %s", MYF(ME_ERROR + ME_WAITTANG), ER_THD(thd, ER_FILSORT_ABORT), - thd->stmt_da->message()); + kill_errno ? ER(kill_errno) : thd->stmt_da->message()); if (global_system_variables.log_warnings > 1) {