From a30e9dda786e4583b567348d52405f283d201167 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Wed, 24 Nov 2010 23:03:16 +0300 Subject: [PATCH 01/27] backport of bug #54461 from 5.1-security to 5.0-security > revision-id: gshchepa@mysql.com-20100801181236-uyuq6ewaq43rw780 > parent: alexey.kopytov@sun.com-20100723115254-jjwmhq97b9wl932l > committer: Gleb Shchepa > branch nick: mysql-5.1-security > timestamp: Sun 2010-08-01 22:12:36 +0400 > Bug #54461: crash with longblob and union or update with subquery > > Queries may crash, if > 1) the GREATEST or the LEAST function has a mixed list of > numeric and LONGBLOB arguments and > 2) the result of such a function goes through an intermediate > temporary table. > > An Item that references a LONGBLOB field has max_length of > UINT_MAX32 == (2^32 - 1). > > The current implementation of GREATEST/LEAST returns REAL > result for a mixed list of numeric and string arguments (that > contradicts with the current documentation, this contradiction > was discussed and it was decided to update the documentation). > > The max_length of such a function call was calculated as a > maximum of argument max_length values (i.e. UINT_MAX32). > > That max_length value of UINT_MAX32 was used as a length for > the intermediate temporary table Field_double to hold > GREATEST/LEAST function result. > > The Field_double::val_str() method call on that field > allocates a String value. > > Since an allocation of String reserves an additional byte > for a zero-termination, the size of String buffer was > set to (UINT_MAX32 + 1), that caused an integer overflow: > actually, an empty buffer of size 0 was allocated. > > An initialization of the "first" byte of that zero-size > buffer with '\0' caused a crash. > > The Item_func_min_max::fix_length_and_dec() has been > modified to calculate max_length for the REAL result like > we do it for arithmetical operators. mysql-test/r/func_misc.result: Test case for bug #54461. mysql-test/t/func_misc.test: Test case for bug #54461. sql/item_func.cc: Bug #54461: crash with longblob and union or update with subquery The Item_func_min_max::fix_length_and_dec() has been modified to calculate max_length for the REAL result like we do it for arithmetical operators. --- mysql-test/r/func_misc.result | 15 +++++++++++++++ mysql-test/t/func_misc.test | 13 ++++++++++++- sql/item_func.cc | 2 ++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index 9f2fcb06638..f23718466d3 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -327,4 +327,19 @@ DROP TABLE t1; select NAME_CONST('_id',1234) as id; id 1234 +# +# Bug #54461: crash with longblob and union or update with subquery +# +CREATE TABLE t1 (a INT, b LONGBLOB); +INSERT INTO t1 VALUES (1, '2'), (2, '3'), (3, '2'); +SELECT DISTINCT LEAST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; +LEAST(a, (SELECT b FROM t1 LIMIT 1)) +1 +2 +SELECT DISTINCT GREATEST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; +GREATEST(a, (SELECT b FROM t1 LIMIT 1)) +2 +3 +1 +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index 01c508c9b58..89359fab0ca 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -450,5 +450,16 @@ DROP TABLE t1; # select NAME_CONST('_id',1234) as id; ---echo End of 5.0 tests +--echo # +--echo # Bug #54461: crash with longblob and union or update with subquery +--echo # +CREATE TABLE t1 (a INT, b LONGBLOB); +INSERT INTO t1 VALUES (1, '2'), (2, '3'), (3, '2'); + +SELECT DISTINCT LEAST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; +SELECT DISTINCT GREATEST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; + +DROP TABLE t1; + +--echo End of 5.0 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index cb0d6bdbe5f..8af7db7fa1a 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2250,6 +2250,8 @@ void Item_func_min_max::fix_length_and_dec() else if ((cmp_type == DECIMAL_RESULT) || (cmp_type == INT_RESULT)) max_length= my_decimal_precision_to_length(max_int_part+decimals, decimals, unsigned_flag); + else if (cmp_type == REAL_RESULT) + max_length= float_length(decimals); cached_field_type= agg_field_type(args, arg_count); } From 721983c393c07618541b5ebfb9c780ed3cf7a687 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 26 Nov 2010 07:28:22 +0100 Subject: [PATCH 02/27] Raise version number after cloning 5.1.54 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 6c9c64d9c86..7a35e8587d4 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.54], [], [mysql]) +AC_INIT([MySQL Server], [5.1.55], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM From a699e4fce85a055e3ee55db12e9891e3b26ef720 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Tue, 30 Nov 2010 20:11:26 +1100 Subject: [PATCH 03/27] Fix Bug# 56228 - Dropping tables from within an active statement crashes server InnoDB AUTOINC code expects the locks to be released in strict reverse order at the end of the statement. However, nested stored proedures and partition tables break this rule. We now allow the locks to be deleted from the trx->autoinc_locks vector in any order but optimise for the common (old) case. rb://441 Approved by Marko Makela --- .../r/innodb-autoinc-56228.result | 30 +++++++ .../t/innodb-autoinc-56228-master.opt | 1 + .../innodb_plugin/t/innodb-autoinc-56228.test | 42 ++++++++++ storage/innodb_plugin/include/ut0vec.h | 19 +++++ storage/innodb_plugin/include/ut0vec.ic | 29 +++++++ storage/innodb_plugin/lock/lock0lock.c | 78 ++++++++++++++++++- 6 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 mysql-test/suite/innodb_plugin/r/innodb-autoinc-56228.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228-master.opt create mode 100644 mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228.test diff --git a/mysql-test/suite/innodb_plugin/r/innodb-autoinc-56228.result b/mysql-test/suite/innodb_plugin/r/innodb-autoinc-56228.result new file mode 100644 index 00000000000..2ff2bd2800a --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb-autoinc-56228.result @@ -0,0 +1,30 @@ +DROP TABLE IF EXISTS t1_56228; +Warnings: +Note 1051 Unknown table 't1_56228' +DROP TABLE IF EXISTS t2_56228; +Warnings: +Note 1051 Unknown table 't2_56228' +DROP FUNCTION IF EXISTS bug56228; +Warnings: +Note 1305 FUNCTION bug56228 does not exist +CREATE TEMPORARY TABLE t1_56228( +c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +CREATE TEMPORARY TABLE t2_56228( +c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +CREATE FUNCTION bug56228() RETURNS INT DETERMINISTIC +BEGIN +INSERT INTO t1_56228 VALUES(NULL); +INSERT INTO t2_56228 VALUES(NULL); +INSERT INTO t1_56228 VALUES(NULL); +INSERT INTO t2_56228 VALUES(NULL); +DROP TEMPORARY TABLE t1_56228; +RETURN 42; +END // +SELECT bug56228(); +bug56228() +42 +DROP FUNCTION bug56228; +DROP TEMPORARY TABLE t2_56228; +DROP TEMPORARY TABLE IF EXISTS t1_56228; +Warnings: +Note 1051 Unknown table 't1_56228' diff --git a/mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228-master.opt b/mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228-master.opt new file mode 100644 index 00000000000..0eed7aaadad --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228-master.opt @@ -0,0 +1 @@ +--innodb_autoinc_lock_mode=0 diff --git a/mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228.test b/mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228.test new file mode 100644 index 00000000000..eb38b21861d --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228.test @@ -0,0 +1,42 @@ +-- source include/have_innodb_plugin.inc + +let $innodb_file_format_check_orig=`select @@innodb_file_format_check`; + +## +# Bug #56228: dropping tables from within an active statement crashes server +# +DROP TABLE IF EXISTS t1_56228; +DROP TABLE IF EXISTS t2_56228; +DROP FUNCTION IF EXISTS bug56228; + +CREATE TEMPORARY TABLE t1_56228( + c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +CREATE TEMPORARY TABLE t2_56228( + c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; + +DELIMITER //; + +CREATE FUNCTION bug56228() RETURNS INT DETERMINISTIC +BEGIN + INSERT INTO t1_56228 VALUES(NULL); + INSERT INTO t2_56228 VALUES(NULL); + INSERT INTO t1_56228 VALUES(NULL); + INSERT INTO t2_56228 VALUES(NULL); + DROP TEMPORARY TABLE t1_56228; + RETURN 42; +END // + +DELIMITER ;// + +SELECT bug56228(); + +DROP FUNCTION bug56228; +DROP TEMPORARY TABLE t2_56228; +DROP TEMPORARY TABLE IF EXISTS t1_56228; + +# +# restore environment to the state it was before this test execution +# + +-- disable_query_log +eval set global innodb_file_format_check=$innodb_file_format_check_orig; diff --git a/storage/innodb_plugin/include/ut0vec.h b/storage/innodb_plugin/include/ut0vec.h index a770f671cfc..0f8b955b098 100644 --- a/storage/innodb_plugin/include/ut0vec.h +++ b/storage/innodb_plugin/include/ut0vec.h @@ -93,6 +93,25 @@ ib_vector_get( ib_vector_t* vec, /*!< in: vector */ ulint n); /*!< in: element index to get */ +/****************************************************************//** +Get last element. The vector must not be empty. +@return last element */ +UNIV_INLINE +void* +ib_vector_get_last( +/*===============*/ + ib_vector_t* vec); /*!< in: vector */ + +/****************************************************************//** +Set the n'th element. */ +UNIV_INLINE +void +ib_vector_set( +/*==========*/ + ib_vector_t* vec, /*!< in/out: vector */ + ulint n, /*!< in: element index to set */ + void* elem); /*!< in: data element */ + /****************************************************************//** Remove the last element from the vector. */ UNIV_INLINE diff --git a/storage/innodb_plugin/include/ut0vec.ic b/storage/innodb_plugin/include/ut0vec.ic index 02e881f9bca..34c858868ce 100644 --- a/storage/innodb_plugin/include/ut0vec.ic +++ b/storage/innodb_plugin/include/ut0vec.ic @@ -50,6 +50,35 @@ ib_vector_get( return(vec->data[n]); } +/****************************************************************//** +Get last element. The vector must not be empty. +@return last element */ +UNIV_INLINE +void* +ib_vector_get_last( +/*===============*/ + ib_vector_t* vec) /*!< in: vector */ +{ + ut_a(vec->used > 0); + + return(vec->data[vec->used - 1]); +} + +/****************************************************************//** +Set the n'th element. */ +UNIV_INLINE +void +ib_vector_set( +/*==========*/ + ib_vector_t* vec, /*!< in/out: vector */ + ulint n, /*!< in: element index to set */ + void* elem) /*!< in: data element */ +{ + ut_a(n < vec->used); + + vec->data[n] = elem; +} + /****************************************************************//** Remove the last element from the vector. @return last vector element */ diff --git a/storage/innodb_plugin/lock/lock0lock.c b/storage/innodb_plugin/lock/lock0lock.c index 77d69d11a2d..c8bbc5c02bd 100644 --- a/storage/innodb_plugin/lock/lock0lock.c +++ b/storage/innodb_plugin/lock/lock0lock.c @@ -3624,6 +3624,80 @@ lock_table_create( return(lock); } +/*************************************************************//** +Pops autoinc lock requests from the transaction's autoinc_locks. We +handle the case where there are gaps in the array and they need to +be popped off the stack. */ +UNIV_INLINE +void +lock_table_pop_autoinc_locks( +/*=========================*/ + trx_t* trx) /*!< in/out: transaction that owns the AUTOINC locks */ +{ + ut_ad(mutex_own(&kernel_mutex)); + ut_ad(!ib_vector_is_empty(trx->autoinc_locks)); + + /* Skip any gaps, gaps are NULL lock entries in the + trx->autoinc_locks vector. */ + + do { + ib_vector_pop(trx->autoinc_locks); + + if (ib_vector_is_empty(trx->autoinc_locks)) { + return; + } + + } while (ib_vector_get_last(trx->autoinc_locks) == NULL); +} + +/*************************************************************//** +Removes an autoinc lock request from the transaction's autoinc_locks. */ +UNIV_INLINE +void +lock_table_remove_autoinc_lock( +/*===========================*/ + lock_t* lock, /*!< in: table lock */ + trx_t* trx) /*!< in/out: transaction that owns the lock */ +{ + lock_t* autoinc_lock; + lint i = ib_vector_size(trx->autoinc_locks) - 1; + + ut_ad(mutex_own(&kernel_mutex)); + ut_ad(lock_get_mode(lock) == LOCK_AUTO_INC); + ut_ad(lock_get_type_low(lock) & LOCK_TABLE); + ut_ad(!ib_vector_is_empty(trx->autoinc_locks)); + + /* With stored functions and procedures the user may drop + a table within the same "statement". This special case has + to be handled by deleting only those AUTOINC locks that were + held by the table being dropped. */ + + autoinc_lock = ib_vector_get(trx->autoinc_locks, i); + + /* This is the default fast case. */ + + if (autoinc_lock == lock) { + lock_table_pop_autoinc_locks(trx); + } else { + /* The last element should never be NULL */ + ut_a(autoinc_lock != NULL); + + /* Handle freeing the locks from within the stack. */ + + while (--i >= 0) { + autoinc_lock = ib_vector_get(trx->autoinc_locks, i); + + if (UNIV_LIKELY(autoinc_lock == lock)) { + ib_vector_set(trx->autoinc_locks, i, NULL); + return; + } + } + + /* Must find the autoinc lock. */ + ut_error; + } +} + /*************************************************************//** Removes a table lock request from the queue and the trx list of locks; this is a low-level function which does NOT check if waiting requests @@ -3663,10 +3737,8 @@ lock_table_remove_low( if (!lock_get_wait(lock) && !ib_vector_is_empty(trx->autoinc_locks)) { - lock_t* autoinc_lock; - autoinc_lock = ib_vector_pop(trx->autoinc_locks); - ut_a(autoinc_lock == lock); + lock_table_remove_autoinc_lock(lock, trx); } ut_a(table->n_waiting_or_granted_auto_inc_locks > 0); From a4b106c882d56516b0f9c01b54432608a30577c8 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 3 Dec 2010 16:56:36 +0200 Subject: [PATCH 04/27] Backport of bug #55564 to 5.0-security --- mysql-test/r/user_var.result | 17 +++++++++++++++++ mysql-test/t/user_var.test | 20 ++++++++++++++++++++ sql/item_func.cc | 8 ++++++++ sql/item_func.h | 1 + 4 files changed, 46 insertions(+) diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result index 8236dbe94ac..81b42676f6e 100644 --- a/mysql-test/r/user_var.result +++ b/mysql-test/r/user_var.result @@ -378,4 +378,21 @@ FROM t1,t2 WHERE t1.f1 = t2.f1 ORDER BY t2.f1; MIN(t2.f1) @bar:= (SELECT MIN(t3.f2) FROM t3 WHERE t3.f2 > foo) 10 NULL DROP TABLE t1, t2, t3; +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (0),(0); +# BUG#55615 : should not crash +SELECT (@a:=((SELECT @a:=1 FROM t1 LIMIT 1))) AND COUNT(1) FROM t1 GROUP BY @a; +(@a:=((SELECT @a:=1 FROM t1 LIMIT 1))) AND COUNT(1) +1 +1 +# BUG#55564 : should not crash +SELECT IF( +@v:=LEAST((SELECT 1 FROM t1 t2 LEFT JOIN t1 ON (@v) GROUP BY t1.a), a), +count(*), 1) +FROM t1 GROUP BY a LIMIT 1; +IF( +@v:=LEAST((SELECT 1 FROM t1 t2 LEFT JOIN t1 ON (@v) GROUP BY t1.a), a), +count(*), 1) +1 +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test index 59a5238b35b..b4eb7910788 100644 --- a/mysql-test/t/user_var.test +++ b/mysql-test/t/user_var.test @@ -268,4 +268,24 @@ FROM t1,t2 WHERE t1.f1 = t2.f1 ORDER BY t2.f1; DROP TABLE t1, t2, t3; + +# +# Bug #55615: debug assertion after using variable in assignment and +# referred to +# Bug #55564: crash with user variables, assignments, joins... +# + +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (0),(0); +--echo # BUG#55615 : should not crash +SELECT (@a:=((SELECT @a:=1 FROM t1 LIMIT 1))) AND COUNT(1) FROM t1 GROUP BY @a; +--echo # BUG#55564 : should not crash +SELECT IF( + @v:=LEAST((SELECT 1 FROM t1 t2 LEFT JOIN t1 ON (@v) GROUP BY t1.a), a), + count(*), 1) +FROM t1 GROUP BY a LIMIT 1; + +DROP TABLE t1; + + --echo End of 5.0 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 8af7db7fa1a..6cd8f34ecef 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4303,6 +4303,14 @@ longlong Item_func_set_user_var::val_int_result() return entry->val_int(&null_value); } +bool Item_func_set_user_var::val_bool_result() +{ + DBUG_ASSERT(fixed == 1); + check(TRUE); + update(); // Store expression + return entry->val_int(&null_value) != 0; +} + String *Item_func_set_user_var::str_result(String *str) { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_func.h b/sql/item_func.h index 47a13559e90..ccab6c855c6 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1300,6 +1300,7 @@ public: my_decimal *val_decimal(my_decimal *); double val_result(); longlong val_int_result(); + bool val_bool_result(); String *str_result(String *str); my_decimal *val_decimal_result(my_decimal *); bool is_null_result(); From e5a88caf08749b6ac1de4d5420f04b6125898e08 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Mon, 6 Dec 2010 23:38:31 +0300 Subject: [PATCH 05/27] Bug #57187: more user variable fun with multiple assignments and comparison in query A query that compares assignments of the same user variable caused Valgrind warnings: access to freed memory region. In case of a DECIMAL argument the assignment operator (:=) may return a pointer to a stored value instead of its copy when evaluated. The next assignment to the same variable may: a) overwrite the stored value with a new one and return the same pointer or even b) reallocate stored value. Thus, if we evaluate an assignment and keep the result pointer and then evaluate another assignment to the same variable, then the kept result pointer of the first assignment will point to unexpectedly changed data or it may be a dead pointer. That may cause wrong data or crash. The user_var_entry::val_decimal method has been modified to copy user variable data. mysql-test/r/user_var.result: Test case for bug #57187. mysql-test/t/user_var.test: Test case for bug #57187. sql/item_func.cc: Bug #57187: more user variable fun with multiple assignments and comparison in query The user_var_entry::val_decimal method has been modified to copy user variable data. --- mysql-test/r/user_var.result | 6 ++++++ mysql-test/t/user_var.test | 12 ++++++++++++ sql/item_func.cc | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result index cf82a18ea83..374520ff610 100644 --- a/mysql-test/r/user_var.result +++ b/mysql-test/r/user_var.result @@ -450,4 +450,10 @@ DROP TABLE t1; select @v:=@v:=sum(1) from dual; @v:=@v:=sum(1) 1 +CREATE TABLE t1(a DECIMAL(31,21)); +INSERT INTO t1 VALUES (0); +SELECT (@v:=a) <> (@v:=1) FROM t1; +(@v:=a) <> (@v:=1) +1 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test index 56217fe67d5..efaf8afd91e 100644 --- a/mysql-test/t/user_var.test +++ b/mysql-test/t/user_var.test @@ -353,4 +353,16 @@ DROP TABLE t1; select @v:=@v:=sum(1) from dual; +# +# Bug #57187: more user variable fun with multiple assignments and +# comparison in query +# + +CREATE TABLE t1(a DECIMAL(31,21)); +INSERT INTO t1 VALUES (0); + +SELECT (@v:=a) <> (@v:=1) FROM t1; + +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 8bb1009ac2c..5a8f65a795a 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4064,7 +4064,7 @@ my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val) int2my_decimal(E_DEC_FATAL_ERROR, *(longlong*) value, 0, val); break; case DECIMAL_RESULT: - val= (my_decimal *)value; + my_decimal2decimal((my_decimal *) value, val); break; case STRING_RESULT: str2my_decimal(E_DEC_FATAL_ERROR, value, length, collation.collation, val); From 3190d454236c73184b41dcbd7a296069ac2d115b Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 8 Dec 2010 16:05:26 +0200 Subject: [PATCH 06/27] Backport of the 5.1 fix for bug #55826 to 5.0 --- mysql-test/r/innodb_mysql.result | 12 ++++++++++++ mysql-test/t/innodb_mysql.test | 15 +++++++++++++++ sql/item_func.cc | 16 +++++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index a51dc978f3e..42eefe4d946 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1315,4 +1315,16 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t1 system NULL NULL NULL NULL 0 const row not found 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 1 DROP TABLE t1,t2; +# +# Bug#55826: create table .. select crashes with when KILL_BAD_DATA +# is returned +# +CREATE TABLE t1(a INT) ENGINE=innodb; +INSERT INTO t1 VALUES (0); +SET SQL_MODE='STRICT_ALL_TABLES'; +CREATE TABLE t2 +SELECT LEAST((SELECT '' FROM t1),NOW()) FROM `t1`; +ERROR 22007: Truncated incorrect datetime value: '' +DROP TABLE t1; +SET SQL_MODE=DEFAULT; End of 5.0 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 3c81a27e9ac..9a17cc5e8d1 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -1073,4 +1073,19 @@ explain select b from t1 where a not in (select b from t1,t2 group by a) group by a; DROP TABLE t1,t2; +--echo # +--echo # Bug#55826: create table .. select crashes with when KILL_BAD_DATA +--echo # is returned +--echo # + +CREATE TABLE t1(a INT) ENGINE=innodb; +INSERT INTO t1 VALUES (0); +SET SQL_MODE='STRICT_ALL_TABLES'; +--error ER_TRUNCATED_WRONG_VALUE +CREATE TABLE t2 + SELECT LEAST((SELECT '' FROM t1),NOW()) FROM `t1`; +DROP TABLE t1; +SET SQL_MODE=DEFAULT; + + --echo End of 5.0 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 6cd8f34ecef..3d105a159e0 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2270,7 +2270,7 @@ void Item_func_min_max::fix_length_and_dec() stored to the value pointer, if latter is provided. RETURN - 0 If one of arguments is NULL + 0 If one of arguments is NULL or there was a execution error # index of the least/greatest argument */ @@ -2284,6 +2284,14 @@ uint Item_func_min_max::cmp_datetimes(ulonglong *value) Item **arg= args + i; bool is_null; longlong res= get_datetime_value(thd, &arg, 0, datetime_item, &is_null); + + /* Check if we need to stop (because of error or KILL) and stop the loop */ + if (thd->net.report_error) + { + null_value= 1; + return 0; + } + if ((null_value= args[i]->null_value)) return 0; if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) @@ -2312,6 +2320,12 @@ String *Item_func_min_max::val_str(String *str) if (null_value) return 0; str_res= args[min_max_idx]->val_str(str); + if (args[min_max_idx]->null_value) + { + // check if the call to val_str() above returns a NULL value + null_value= 1; + return NULL; + } str_res->set_charset(collation.collation); return str_res; } From 1bd81f6b817cc9b8e0b61d126b7ad159117e1b29 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Wed, 15 Dec 2010 19:00:01 +0300 Subject: [PATCH 07/27] Patch for Bug#57952 (privilege change is not taken into account by EXECUTE). The user-visible problem was that changes to column-level privileges, happened in between of PREPARE and EXECUTE of a prepared statement, were neglected. I.e. a prepared statement could be executed with the column-level privileges as of PREPARE-time. The problem existed for column-level privileges only. A similar problem existed for stored programs: the changes between executions didn't have an effect. Technically the thing is that table references are cached in Prepared_statement::prepare() call. In subsequent Prepared_statement::execute() calls those cached values are used. There are two functions to get a field by name: find_field_in_table() and find_field_in_table_ref(). On prepare-phase find_field_in_table_ref() is called, on execute-phase -- find_field_in_table() because the table is cached. find_field_in_table() does not check column-level privileges and expects the caller to do that. The problem was that this check was forgotten. The fix is to check them there as it happens in find_field_in_table_ref(). --- mysql-test/r/grant.result | 74 ++++++++++++++++++++++++++++ mysql-test/t/grant.test | 101 ++++++++++++++++++++++++++++++++++++++ sql/sql_base.cc | 12 +++++ 3 files changed, 187 insertions(+) diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 7a5b0520f7c..e7ae6612746 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -1156,4 +1156,78 @@ CURRENT_USER() root@localhost SET PASSWORD FOR CURRENT_USER() = PASSWORD("admin"); SET PASSWORD FOR CURRENT_USER() = PASSWORD(""); + +# Bug#57952 + +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; +use mysqltest1; +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 VALUES (1, 1); +CREATE TABLE t2(a INT); +INSERT INTO t2 VALUES (2); +CREATE TABLE mysqltest2.t3(a INT); +INSERT INTO mysqltest2.t3 VALUES (4); +CREATE USER testuser@localhost; +GRANT CREATE ROUTINE, EXECUTE ON mysqltest1.* TO testuser@localhost; +GRANT SELECT(b) ON t1 TO testuser@localhost; +GRANT SELECT ON t2 TO testuser@localhost; +GRANT SELECT ON mysqltest2.* TO testuser@localhost; + +# Connection: bug57952_con1 (testuser@localhost, db: mysqltest1) +PREPARE s1 FROM 'SELECT b FROM t1'; +PREPARE s2 FROM 'SELECT a FROM t2'; +PREPARE s3 FROM 'SHOW TABLES FROM mysqltest2'; +CREATE PROCEDURE p1() SELECT b FROM t1; +CREATE PROCEDURE p2() SELECT a FROM t2; +CREATE PROCEDURE p3() SHOW TABLES FROM mysqltest2; +CALL p1; +b +1 +CALL p2; +a +2 +CALL p3; +Tables_in_mysqltest2 +t3 + +# Connection: default +REVOKE SELECT ON t1 FROM testuser@localhost; +GRANT SELECT(a) ON t1 TO testuser@localhost; +REVOKE SELECT ON t2 FROM testuser@localhost; +REVOKE SELECT ON mysqltest2.* FROM testuser@localhost; + +# Connection: bug57952_con1 (testuser@localhost, db: mysqltest1) +# - Check column-level privileges... +EXECUTE s1; +ERROR 42000: SELECT command denied to user 'testuser'@'localhost' for column 'b' in table 't1' +SELECT b FROM t1; +ERROR 42000: SELECT command denied to user 'testuser'@'localhost' for column 'b' in table 't1' +EXECUTE s1; +ERROR 42000: SELECT command denied to user 'testuser'@'localhost' for column 'b' in table 't1' +CALL p1; +ERROR 42000: SELECT command denied to user 'testuser'@'localhost' for column 'b' in table 't1' +# - Check table-level privileges... +SELECT a FROM t2; +ERROR 42000: SELECT command denied to user 'testuser'@'localhost' for table 't2' +EXECUTE s2; +ERROR 42000: SELECT command denied to user 'testuser'@'localhost' for table 't2' +CALL p2; +ERROR 42000: SELECT command denied to user 'testuser'@'localhost' for table 't2' +# - Check database-level privileges... +SHOW TABLES FROM mysqltest2; +ERROR 42000: Access denied for user 'testuser'@'localhost' to database 'mysqltest2' +EXECUTE s3; +ERROR 42000: Access denied for user 'testuser'@'localhost' to database 'mysqltest2' +CALL p3; +ERROR 42000: Access denied for user 'testuser'@'localhost' to database 'mysqltest2' + +# Connection: default +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; +DROP USER testuser@localhost; +use test; + End of 5.0 tests diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index 1b2b8465c83..21e3bbf5842 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -1166,6 +1166,107 @@ SELECT CURRENT_USER(); SET PASSWORD FOR CURRENT_USER() = PASSWORD("admin"); SET PASSWORD FOR CURRENT_USER() = PASSWORD(""); +# +# Bug#57952: privilege change is not taken into account by EXECUTE. +# + +--echo +--echo # Bug#57952 +--echo + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +--enable_warnings + +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; + +use mysqltest1; +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 VALUES (1, 1); + +CREATE TABLE t2(a INT); +INSERT INTO t2 VALUES (2); + +CREATE TABLE mysqltest2.t3(a INT); +INSERT INTO mysqltest2.t3 VALUES (4); + +CREATE USER testuser@localhost; +GRANT CREATE ROUTINE, EXECUTE ON mysqltest1.* TO testuser@localhost; +GRANT SELECT(b) ON t1 TO testuser@localhost; +GRANT SELECT ON t2 TO testuser@localhost; +GRANT SELECT ON mysqltest2.* TO testuser@localhost; + +--echo +--echo # Connection: bug57952_con1 (testuser@localhost, db: mysqltest1) +--connect (bug57952_con1,localhost,testuser,,mysqltest1) +PREPARE s1 FROM 'SELECT b FROM t1'; +PREPARE s2 FROM 'SELECT a FROM t2'; +PREPARE s3 FROM 'SHOW TABLES FROM mysqltest2'; + +CREATE PROCEDURE p1() SELECT b FROM t1; +CREATE PROCEDURE p2() SELECT a FROM t2; +CREATE PROCEDURE p3() SHOW TABLES FROM mysqltest2; + +CALL p1; +CALL p2; +CALL p3; + +--echo +--echo # Connection: default +--connection default +REVOKE SELECT ON t1 FROM testuser@localhost; +GRANT SELECT(a) ON t1 TO testuser@localhost; +REVOKE SELECT ON t2 FROM testuser@localhost; +REVOKE SELECT ON mysqltest2.* FROM testuser@localhost; + +--echo +--echo # Connection: bug57952_con1 (testuser@localhost, db: mysqltest1) +--connection bug57952_con1 +--echo # - Check column-level privileges... +--error ER_COLUMNACCESS_DENIED_ERROR +EXECUTE s1; + +--error ER_COLUMNACCESS_DENIED_ERROR +SELECT b FROM t1; + +--error ER_COLUMNACCESS_DENIED_ERROR +EXECUTE s1; + +--error ER_COLUMNACCESS_DENIED_ERROR +CALL p1; + +--echo # - Check table-level privileges... +--error ER_TABLEACCESS_DENIED_ERROR +SELECT a FROM t2; + +--error ER_TABLEACCESS_DENIED_ERROR +EXECUTE s2; + +--error ER_TABLEACCESS_DENIED_ERROR +CALL p2; + +--echo # - Check database-level privileges... +--error ER_DBACCESS_DENIED_ERROR +SHOW TABLES FROM mysqltest2; + +--error ER_DBACCESS_DENIED_ERROR +EXECUTE s3; + +--error ER_DBACCESS_DENIED_ERROR +CALL p3; + +--echo +--echo # Connection: default +--connection default +--disconnect bug57952_con1 +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; +DROP USER testuser@localhost; +use test; +--echo + --echo End of 5.0 tests disconnect master; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 88d1e8879d1..669229a8404 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3657,6 +3657,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, /* Find field by name in a base table or a view with temp table algorithm. + The caller is expected to check column-level privileges. + SYNOPSIS find_field_in_table() thd thread handler @@ -3753,6 +3755,8 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, This procedure detects the type of the table reference 'table_list' and calls the corresponding search routine. + The routine checks column-level privieleges for the found field. + RETURN 0 field is not found view_ref_found found value in VIEW (real result is in *ref) @@ -3944,8 +3948,16 @@ find_field_in_tables(THD *thd, Item_ident *item, when table_ref->field_translation != NULL. */ if (table_ref->table && !table_ref->view) + { found= find_field_in_table(thd, table_ref->table, name, length, TRUE, &(item->cached_field_index)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* Check if there are sufficient access rights to the found field. */ + if (found && check_privileges && + check_column_grant_in_table_ref(thd, table_ref, name, length)) + found= WRONG_GRANT; +#endif + } else found= find_field_in_table_ref(thd, table_ref, name, length, item->name, NULL, NULL, ref, check_privileges, From ffdeb062727361fb52c1baa38a184a214ec91ea3 Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Thu, 16 Dec 2010 10:07:48 +0100 Subject: [PATCH 08/27] Bug#54568: create view cause Assertion failed: 0, file .\item_subselect.cc, line 836 IN quantified predicates are never executed directly. They are rather wrapped inside nodes called IN Optimizers (Item_in_optimizer) which take care of the execution. However, this is not done during query preparation. Unfortunately the LIKE predicate pre-evaluates constant right-hand side arguments even during name resolution. Likely this is meant as an optimization. Fixed by not pre-evaluating LIKE arguments in view prepare mode. Back-ported to 5.0s --- mysql-test/r/subselect4.result | 21 +++++++++++++++++++++ mysql-test/t/subselect4.test | 16 ++++++++++++++++ sql/item_cmpfunc.cc | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index e863cbfb7a8..4f808a963b9 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -59,3 +59,24 @@ FROM t3 WHERE 1 = 0 GROUP BY 1; (SELECT 1 FROM t1,t2 WHERE t2.b > t3.b) DROP TABLE t1,t2,t3; End of 5.0 tests. +# +# Bug#54568: create view cause Assertion failed: 0, +# file .\item_subselect.cc, line 836 +# +EXPLAIN SELECT 1 LIKE ( 1 IN ( SELECT 1 ) ); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1249 Select 2 was reduced during optimization +DESCRIBE SELECT 1 LIKE ( 1 IN ( SELECT 1 ) ); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1249 Select 2 was reduced during optimization +# None of the below should crash +CREATE VIEW v1 AS SELECT 1 LIKE ( 1 IN ( SELECT 1 ) ); +CREATE VIEW v2 AS SELECT 1 LIKE '%' ESCAPE ( 1 IN ( SELECT 1 ) ); +DROP VIEW v1, v2; +# +# End of 5.1 tests. +# diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index 440eca22828..2c6efdbaac2 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -62,3 +62,19 @@ FROM t3 WHERE 1 = 0 GROUP BY 1; DROP TABLE t1,t2,t3; --echo End of 5.0 tests. + +--echo # +--echo # Bug#54568: create view cause Assertion failed: 0, +--echo # file .\item_subselect.cc, line 836 +--echo # +EXPLAIN SELECT 1 LIKE ( 1 IN ( SELECT 1 ) ); +DESCRIBE SELECT 1 LIKE ( 1 IN ( SELECT 1 ) ); +--echo # None of the below should crash +CREATE VIEW v1 AS SELECT 1 LIKE ( 1 IN ( SELECT 1 ) ); +CREATE VIEW v2 AS SELECT 1 LIKE '%' ESCAPE ( 1 IN ( SELECT 1 ) ); +DROP VIEW v1, v2; + + +--echo # +--echo # End of 5.1 tests. +--echo # diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 4ae381af683..b79c13f4578 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4248,7 +4248,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) return TRUE; } - if (escape_item->const_item()) + if (escape_item->const_item() && !thd->lex->view_prepare_mode) { /* If we are on execution stage */ String *escape_str= escape_item->val_str(&tmp_value1); From 21da523f1796555fb61d5053ec8509ca3d74a2f6 Mon Sep 17 00:00:00 2001 From: Kristofer Pettersson Date: Thu, 16 Dec 2010 11:49:40 +0100 Subject: [PATCH 09/27] Bug58747 57359 patch: breaks secure_file_priv+not secure yet+still accesses other folders "load data infile .." allowed for access to unautohorized tables. Due to a faulty if-statement it was possible to circumvent the secure_file_priv restriction. mysql-test/mysql-test-run.pl: * Add SECURE_LOAD_PATH environment variable to mtr test cases. mysql-test/suite/sys_vars/r/secure_file_priv2.result: * add test for bug58747 mysql-test/suite/sys_vars/t/secure_file_priv2-master.opt: * add test for bug58747 mysql-test/suite/sys_vars/t/secure_file_priv2.test: * add test for bug58747 sql/sql_load.cc: * Correct faulty if-statement * fix indentation * move my_stat() block to after is_secure_file_path() check. --- mysql-test/mysql-test-run.pl | 10 +++ .../suite/sys_vars/r/secure_file_priv2.result | 6 ++ .../sys_vars/t/secure_file_priv2-master.opt | 1 + .../suite/sys_vars/t/secure_file_priv2.test | 23 +++++ sql/sql_load.cc | 89 ++++++++++--------- 5 files changed, 85 insertions(+), 44 deletions(-) create mode 100644 mysql-test/suite/sys_vars/r/secure_file_priv2.result create mode 100644 mysql-test/suite/sys_vars/t/secure_file_priv2-master.opt create mode 100644 mysql-test/suite/sys_vars/t/secure_file_priv2.test diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 22c60cf1997..1348d0f991b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2025,6 +2025,16 @@ sub environment_setup { $ENV{'DEFAULT_MASTER_PORT'}= $mysqld_variables{'master-port'} || 3306; $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir; $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir; + + if (IS_WINDOWS) + { + $ENV{'SECURE_LOAD_PATH'}= $glob_mysql_test_dir."\\std_data"; + } + else + { + $ENV{'SECURE_LOAD_PATH'}= $glob_mysql_test_dir."/std_data"; + } + # ---------------------------------------------------- # Setup env for NDB diff --git a/mysql-test/suite/sys_vars/r/secure_file_priv2.result b/mysql-test/suite/sys_vars/r/secure_file_priv2.result new file mode 100644 index 00000000000..ec91b6037d0 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/secure_file_priv2.result @@ -0,0 +1,6 @@ +CREATE TABLE t1 (c1 INT); +LOAD DATA INFILE "t1.MYI" into table t1; +ERROR HY000: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement +LOAD DATA INFILE "/test" into table t1; +ERROR HY000: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement +DROP TABLE t1; diff --git a/mysql-test/suite/sys_vars/t/secure_file_priv2-master.opt b/mysql-test/suite/sys_vars/t/secure_file_priv2-master.opt new file mode 100644 index 00000000000..1d9a49c8f75 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/secure_file_priv2-master.opt @@ -0,0 +1 @@ +--secure_file_priv=$SECURE_LOAD_PATH diff --git a/mysql-test/suite/sys_vars/t/secure_file_priv2.test b/mysql-test/suite/sys_vars/t/secure_file_priv2.test new file mode 100644 index 00000000000..0ca0a1839e1 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/secure_file_priv2.test @@ -0,0 +1,23 @@ +# +# Bug58747 breaks secure_file_priv+not secure yet+still accesses other folders +# +CREATE TABLE t1 (c1 INT); +# +# Before the patch this statement failed with +# Linux: +# -> errno 13: 'Can't get stat of ' +# Windows: +# -> Warning 1366 Incorrect integer value: '■■☺' for +# -> column 'c1' at row 1 +# Now it should consistently fail with ER_OPTION_PREVENTS_STATEMENT +# on all platforms. +--error ER_OPTION_PREVENTS_STATEMENT +LOAD DATA INFILE "t1.MYI" into table t1; + +# +# The following test makes the assuption that /test isn't a valid path in any +# operating system running the test suite. +--error ER_OPTION_PREVENTS_STATEMENT +LOAD DATA INFILE "/test" into table t1; + +DROP TABLE t1; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 4b68f2a3821..a0f9ebbe39b 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -314,56 +314,57 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, (void) fn_format(name, ex->file_name, mysql_real_data_home, "", MY_RELATIVE_PATH | MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH); -#if !defined(__WIN__) && ! defined(__NETWARE__) - MY_STAT stat_info; - if (!my_stat(name,&stat_info,MYF(MY_WME))) - DBUG_RETURN(TRUE); + } - // 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))) - { - my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name); - DBUG_RETURN(TRUE); - } - if ((stat_info.st_mode & S_IFIFO) == S_IFIFO) - is_fifo = 1; -#endif - - if (thd->slave_thread) - { + if (thd->slave_thread) + { #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) - if (strncmp(active_mi->rli.slave_patternload_file, name, - active_mi->rli.slave_patternload_file_size)) - { - /* - LOAD DATA INFILE in the slave SQL Thread can only read from - --slave-load-tmpdir". This should never happen. Please, report a bug. - */ - - sql_print_error("LOAD DATA INFILE in the slave SQL Thread can only read from --slave-load-tmpdir. " \ - "Please, report a bug."); - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--slave-load-tmpdir"); - DBUG_RETURN(TRUE); - } -#else - /* - This is impossible and should never happen. - */ - DBUG_ASSERT(FALSE); -#endif - } - else if (!is_secure_file_path(name)) + if (strncmp(active_mi->rli.slave_patternload_file, name, + active_mi->rli.slave_patternload_file_size)) { - /* Read only allowed from within dir specified by secure_file_priv */ - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); + /* + LOAD DATA INFILE in the slave SQL Thread can only read from + --slave-load-tmpdir". This should never happen. Please, report a bug. + */ + + sql_print_error("LOAD DATA INFILE in the slave SQL Thread can only read from --slave-load-tmpdir. " \ + "Please, report a bug."); + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--slave-load-tmpdir"); DBUG_RETURN(TRUE); } - +#else + /* + This is impossible and should never happen. + */ + DBUG_ASSERT(FALSE); +#endif } + else if (!is_secure_file_path(name)) + { + /* Read only allowed from within dir specified by secure_file_priv */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); + DBUG_RETURN(TRUE); + } + +#if !defined(__WIN__) && ! defined(__NETWARE__) + MY_STAT stat_info; + if (!my_stat(name,&stat_info,MYF(MY_WME))) + DBUG_RETURN(TRUE); + + // 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))) + { + my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name); + DBUG_RETURN(TRUE); + } + if ((stat_info.st_mode & S_IFIFO) == S_IFIFO) + is_fifo = 1; +#endif + if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0) DBUG_RETURN(TRUE); } From c1fce59c3d9a9dd100dd7048f21451505068f27b Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 23 Dec 2010 13:35:42 +0200 Subject: [PATCH 10/27] fixed the binlog problem --- mysql-test/suite/binlog/r/binlog_unsafe.result | 2 +- mysql-test/suite/binlog/t/binlog_unsafe.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 77fe5eb0b5e..e0e0babb8b7 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -271,7 +271,7 @@ INSERT INTO t1 SELECT * FROM t2 LIMIT 1; DROP TABLE t1,t2; "Should NOT have any warning message issued in the following func7() and trig" CREATE TABLE t1 (a INT); -CREATE TABLE t2 (a CHAR(40)); +CREATE TABLE t2 (a TEXT); CREATE TABLE trigger_table (a CHAR(7)); CREATE FUNCTION func7() RETURNS INT diff --git a/mysql-test/suite/binlog/t/binlog_unsafe.test b/mysql-test/suite/binlog/t/binlog_unsafe.test index a86e49e475a..1e2e31eacf7 100644 --- a/mysql-test/suite/binlog/t/binlog_unsafe.test +++ b/mysql-test/suite/binlog/t/binlog_unsafe.test @@ -329,7 +329,7 @@ DROP TABLE t1,t2; --echo "Should NOT have any warning message issued in the following func7() and trig" CREATE TABLE t1 (a INT); -CREATE TABLE t2 (a CHAR(40)); +CREATE TABLE t2 (a TEXT); CREATE TABLE trigger_table (a CHAR(7)); DELIMITER |; CREATE FUNCTION func7() From 95cc85dbf515991ea1b19307cacce5c4ba24f21d Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 6 Jan 2011 23:45:59 -0800 Subject: [PATCH 11/27] Backport Bug #58643 InnoDB: too long table name. Also fix Bug #59312 examine MAX_FULL_NAME_LEN in InnoDB to address possible insufficient name buffer Bug #59312 Approved by Sunny Bains --- storage/innodb_plugin/ChangeLog | 10 ++++++++++ storage/innodb_plugin/dict/dict0dict.c | 10 +++++----- storage/innodb_plugin/handler/ha_innodb.cc | 10 ++++++++++ storage/innodb_plugin/handler/i_s.cc | 11 +---------- storage/innodb_plugin/include/univ.i | 12 ++++++++++++ storage/innodb_plugin/row/row0merge.c | 4 ++-- 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 5ca60eb73d5..34d0fe7f6ef 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,13 @@ +2011-01-06 The InnoDB Team + * row/row0merge.c: + Fix Bug#59312 Examine MAX_FULL_NAME_LEN in InnoDB to address + possible insufficient name buffer + +2011-01-06 The InnoDB Team + * dict/dict0dict.c, handler/ha_innodb.cc, handler/i_s.cc, + include/univ.i: + Fix Bug#58643 InnoDB: too long table name + 2010-11-11 The InnoDB Team * thr/thr0loc.c, trx/trx0i_s.c: Fix Bug#57802 Empty ASSERTION parameter passed to the HASH_SEARCH macro diff --git a/storage/innodb_plugin/dict/dict0dict.c b/storage/innodb_plugin/dict/dict0dict.c index eb3169bd176..2e936e27065 100644 --- a/storage/innodb_plugin/dict/dict0dict.c +++ b/storage/innodb_plugin/dict/dict0dict.c @@ -932,7 +932,7 @@ dict_table_rename_in_cache( dict_foreign_t* foreign; dict_index_t* index; ulint fold; - char old_name[MAX_TABLE_NAME_LEN + 1]; + char old_name[MAX_FULL_NAME_LEN + 1]; ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); @@ -944,7 +944,7 @@ dict_table_rename_in_cache( ut_print_timestamp(stderr); fprintf(stderr, "InnoDB: too long table name: '%s', " "max length is %d\n", table->name, - MAX_TABLE_NAME_LEN); + MAX_FULL_NAME_LEN); ut_error; } @@ -994,11 +994,11 @@ dict_table_rename_in_cache( ut_fold_string(old_name), table); if (strlen(new_name) > strlen(table->name)) { - /* We allocate MAX_TABLE_NAME_LEN+1 bytes here to avoid + /* We allocate MAX_FULL_NAME_LEN + 1 bytes here to avoid memory fragmentation, we assume a repeated calls of ut_realloc() with the same size do not cause fragmentation */ - ut_a(strlen(new_name) <= MAX_TABLE_NAME_LEN); - table->name = ut_realloc(table->name, MAX_TABLE_NAME_LEN + 1); + ut_a(strlen(new_name) <= MAX_FULL_NAME_LEN); + table->name = ut_realloc(table->name, MAX_FULL_NAME_LEN + 1); } memcpy(table->name, new_name, strlen(new_name) + 1); diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 5965bd0e59e..c0e3163d717 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -6009,6 +6009,16 @@ create_table_def( DBUG_RETURN(HA_ERR_GENERIC); } + /* MySQL does the name length check. But we do additional check + on the name length here */ + if (strlen(table_name) > MAX_FULL_NAME_LEN) { + push_warning_printf( + (THD*) trx->mysql_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TABLE_NAME, + "InnoDB: Table Name or Database Name is too long"); + DBUG_RETURN(ER_TABLE_NAME); + } + n_cols = form->s->fields; /* We pass 0 as the space id, and determine at a lower level the space diff --git a/storage/innodb_plugin/handler/i_s.cc b/storage/innodb_plugin/handler/i_s.cc index 9ad2d656365..0ef4f77ed3e 100644 --- a/storage/innodb_plugin/handler/i_s.cc +++ b/storage/innodb_plugin/handler/i_s.cc @@ -579,16 +579,7 @@ fill_innodb_locks_from_cache( for (i = 0; i < rows_num; i++) { i_s_locks_row_t* row; - - /* note that the decoded database or table name is - never expected to be longer than NAME_LEN; - NAME_LEN for database name - 2 for surrounding quotes around database name - NAME_LEN for table name - 2 for surrounding quotes around table name - 1 for the separating dot (.) - 9 for the #mysql50# prefix */ - char buf[2 * NAME_LEN + 14]; + char buf[MAX_FULL_NAME_LEN + 1]; const char* bufend; char lock_trx_id[TRX_ID_MAX_LEN + 1]; diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index cab3af5297e..4e74411628e 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -296,6 +296,18 @@ number does not include a terminating '\0'. InnoDB probably can handle longer names internally */ #define MAX_TABLE_NAME_LEN 192 +/* The maximum length of a database name. Like MAX_TABLE_NAME_LEN this is +the MySQL's NAME_LEN, see check_and_convert_db_name(). */ +#define MAX_DATABASE_NAME_LEN MAX_TABLE_NAME_LEN + +/* MAX_FULL_NAME_LEN defines the full name path including the +database name and table name. In addition, 14 bytes is added for: + 2 for surrounding quotes around table name + 1 for the separating dot (.) + 9 for the #mysql50# prefix */ +#define MAX_FULL_NAME_LEN \ + (MAX_TABLE_NAME_LEN + MAX_DATABASE_NAME_LEN + 14) + /* UNIVERSAL TYPE DEFINITIONS ========================== diff --git a/storage/innodb_plugin/row/row0merge.c b/storage/innodb_plugin/row/row0merge.c index 160edd32fbf..647d0031635 100644 --- a/storage/innodb_plugin/row/row0merge.c +++ b/storage/innodb_plugin/row/row0merge.c @@ -2341,7 +2341,7 @@ row_merge_rename_tables( { ulint err = DB_ERROR; pars_info_t* info; - char old_name[MAX_TABLE_NAME_LEN + 1]; + char old_name[MAX_FULL_NAME_LEN + 1]; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(old_table != new_table); @@ -2356,7 +2356,7 @@ row_merge_rename_tables( ut_print_timestamp(stderr); fprintf(stderr, "InnoDB: too long table name: '%s', " "max length is %d\n", old_table->name, - MAX_TABLE_NAME_LEN); + MAX_FULL_NAME_LEN); ut_error; } From 651313bf91d7771831de7228bb4130f0ac64c33a Mon Sep 17 00:00:00 2001 From: Oystein Grovlen Date: Wed, 12 Jan 2011 10:37:15 +0100 Subject: [PATCH 12/27] Bug#59211: Select Returns Different Value for min(year) Function get_year_value() contains code to convert 2-digits year to 4-digits. The fix for Bug#49910 added a check on the size of the underlying field so that this conversion is not done for YEAR(4) values. (Since otherwise one would convert invalid YEAR(4) values to valid ones.) The existing check does not work when Item_cache is used, since it is not detected when the cache is based on a Field. The reported change in behavior is due to Bug#58030 which added extra cached items in min/max computations. The elegant solution would be to implement Item_cache::real_item() to return the underlying Item. However, some side effects are observed (change in explain output) that indicates that such a change is not straight- forward, and definitely not appropriate for an MRU. Instead, a Item_cache::field() method has been added in order to get access to the underlying field. (This field() method eliminates the need for Item_cache::eq_def() used in test_if_ref(), but in order to limit the scope of this fix, that code has been left as is.) mysql-test/r/type_year.result: Added test case for Bug#59211. mysql-test/t/type_year.test: Added test case for Bug#59211. sql/item.h: Added function Item_cache::field() to get access to the underlying Field of a cached field Value. sql/item_cmpfunc.cc: Also check underlying fields of Item_cache, not just Item_Field, when checking whether the value is of type YEAR(4) or not. --- mysql-test/r/type_year.result | 14 ++++++++++++++ mysql-test/t/type_year.test | 10 ++++++++++ sql/item.h | 13 ++++++++++--- sql/item_cmpfunc.cc | 9 ++++++--- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/type_year.result b/mysql-test/r/type_year.result index 8948214f565..2dc491c6166 100644 --- a/mysql-test/r/type_year.result +++ b/mysql-test/r/type_year.result @@ -341,4 +341,18 @@ ta_y s tb_y s 2001 2001 2001 2001 DROP TABLE t1; # +# Bug #59211: Select Returns Different Value for min(year) Function +# +CREATE TABLE t1(c1 YEAR(4)); +INSERT INTO t1 VALUES (1901),(2155),(0000); +SELECT * FROM t1; +c1 +1901 +2155 +0000 +SELECT COUNT(*) AS total_rows, MIN(c1) AS min_value, MAX(c1) FROM t1; +total_rows min_value MAX(c1) +3 0 2155 +DROP TABLE t1; +# End of 5.1 tests diff --git a/mysql-test/t/type_year.test b/mysql-test/t/type_year.test index d8da4ccc82c..1a9e66478e1 100644 --- a/mysql-test/t/type_year.test +++ b/mysql-test/t/type_year.test @@ -149,6 +149,16 @@ SELECT ta.y AS ta_y, ta.s, tb.y AS tb_y, tb.s FROM t1 ta, t1 tb HAVING ta_y = tb DROP TABLE t1; +--echo # +--echo # Bug #59211: Select Returns Different Value for min(year) Function +--echo # + +CREATE TABLE t1(c1 YEAR(4)); +INSERT INTO t1 VALUES (1901),(2155),(0000); +SELECT * FROM t1; +SELECT COUNT(*) AS total_rows, MIN(c1) AS min_value, MAX(c1) FROM t1; +DROP TABLE t1; + --echo # --echo End of 5.1 tests diff --git a/sql/item.h b/sql/item.h index eb809ca410f..fdf8d4906d1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2960,11 +2960,10 @@ class Item_cache: public Item_basic_constant protected: Item *example; table_map used_table_map; - /* - Field that this object will get value from. This is set/used by + /** + Field that this object will get value from. This is used by index-based subquery engines to detect and remove the equality injected by IN->EXISTS transformation. - For all other uses of Item_cache, cached_field doesn't matter. */ Field *cached_field; enum enum_field_types cached_field_type; @@ -3021,6 +3020,14 @@ public: { return this == item; } + + /** + If this item caches a field value, return pointer to underlying field. + + @return Pointer to field, or NULL if this is not a cache for a field value. + */ + Field* field() { return cached_field; } + virtual void store(Item *item); virtual bool cache_value()= 0; }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d2e1ebbb3a1..4f2034ae902 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1196,9 +1196,12 @@ get_year_value(THD *thd, Item ***item_arg, Item **cache_arg, value of 2000. */ Item *real_item= item->real_item(); - if (!(real_item->type() == Item::FIELD_ITEM && - ((Item_field *)real_item)->field->type() == MYSQL_TYPE_YEAR && - ((Item_field *)real_item)->field->field_length == 4)) + Field *field= NULL; + if (real_item->type() == Item::FIELD_ITEM) + field= ((Item_field *)real_item)->field; + else if (real_item->type() == Item::CACHE_ITEM) + field= ((Item_cache *)real_item)->field(); + if (!(field && field->type() == MYSQL_TYPE_YEAR && field->field_length == 4)) { if (value < 70) value+= 100; From de3c4428b8c759e85631d8d70b5845c872de5400 Mon Sep 17 00:00:00 2001 From: Karen Langford Date: Tue, 25 Jan 2011 15:42:40 +0100 Subject: [PATCH 13/27] Updating header copyright/README in source for 2011 --- README | 2 +- include/my_pthread.h | 2 +- include/my_sys.h | 2 +- mysys/my_fopen.c | 2 +- mysys/my_getsystime.c | 2 +- sql/handler.cc | 2 +- sql/item.h | 2 +- sql/item_cmpfunc.cc | 2 +- sql/item_geofunc.h | 2 +- sql/log.cc | 2 +- sql/net_serv.cc | 2 +- sql/sql_base.cc | 2 +- sql/sql_select.cc | 2 +- sql/sql_show.cc | 2 +- sql/sql_view.cc | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README b/README index 5463de4a7e8..48948352e12 100644 --- a/README +++ b/README @@ -5,7 +5,7 @@ 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. -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. diff --git a/include/my_pthread.h b/include/my_pthread.h index 3880511da2d..87cdaaad0dd 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.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 diff --git a/include/my_sys.h b/include/my_sys.h index 0ac220cec31..f6cd9dada99 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -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 diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index b8373ecb3ab..72991490d75 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.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 diff --git a/mysys/my_getsystime.c b/mysys/my_getsystime.c index dfd99f6380d..ebe6cea842b 100644 --- a/mysys/my_getsystime.c +++ b/mysys/my_getsystime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 MySQL AB +/* Copyright (c) 2004, 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 diff --git a/sql/handler.cc b/sql/handler.cc index 711d2942ce0..5968a78b587 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* 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 diff --git a/sql/item.h b/sql/item.h index 866d620d9d7..8568e89542e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -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 diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 4f2034ae902..df5ed7f38de 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -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 diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 08161badfd3..0a6e8d03a46 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (c) 2003, 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 diff --git a/sql/log.cc b/sql/log.cc index 23182fa1902..17642696e7d 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* 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 diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 0c559f5619d..e45d57e57dc 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -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 diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 995520ab16f..9765148cda1 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -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 diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8cc2ec6a0f8..95378db80d1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -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 diff --git a/sql/sql_show.cc b/sql/sql_show.cc index cf6a34d4ef5..1524a8fb87f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -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 diff --git a/sql/sql_view.cc b/sql/sql_view.cc index a25ef931344..ab6da7c1925 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 MySQL AB +/* Copyright (c) 2004, 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 From e952ee1158be7611f3443bf9e1919652eb3b4602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 31 Jan 2011 09:56:51 +0200 Subject: [PATCH 14/27] Bug#59230 assert 0 row_upd_changes_ord_field_binary() in post-crash trx rollback or purge This patch does not relax the failing debug assertion during purge. That will be revisited once we have managed to repeat the assertion failure. row_upd_changes_ord_field_binary_func(): Renamed from row_upd_changes_ord_field_binary(). Add the parameter que_thr_t* in UNIV_DEBUG builds. When the off-page column cannot be retrieved, assert that the current transaction is a recovered one and that it is the one that is currently being rolled back. row_upd_changes_ord_field_binary(): A wrapper macro for row_upd_changes_ord_field_binary_func() that discards the que_thr_t* parameter unless UNIV_DEBUG is defined. row_purge_upd_exist_or_extern_func(): Renamed from row_purge_upd_exist_or_extern(). Add the parameter que_thr_t* in UNIV_DEBUG builds. row_purge_upd_exist_or_extern(): A wrapper macro for row_purge_upd_exist_or_extern_func() that discards the que_thr_t* parameter unless UNIV_DEBUG is defined. Make trx_roll_crash_recv_trx const. If there were a 'do not dereference' attribute, it would be appropriate as well. rb://588 approved by Jimmy Yang --- storage/innodb_plugin/ChangeLog | 7 +++++ storage/innodb_plugin/btr/btr0cur.c | 4 +-- storage/innodb_plugin/include/row0upd.h | 26 +++++++++++----- storage/innodb_plugin/row/row0purge.c | 21 ++++++++++--- storage/innodb_plugin/row/row0umod.c | 5 +-- storage/innodb_plugin/row/row0upd.c | 41 ++++++++++++++++--------- storage/innodb_plugin/trx/trx0roll.c | 4 +-- 7 files changed, 74 insertions(+), 34 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 18ab48f32a5..0cbdc8ed9d2 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,10 @@ +2011-01-31 The InnoDB Team + + * btr/btr0cur.c, include/row0upd.h, + row/row0purge.c, row/row0umod.c, row/row0upd.c: + Bug#59230 assert 0 row_upd_changes_ord_field_binary() + in post-crash rollback or purge + 2011-01-27 The InnoDB Team * btr/btr0cur.c: diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 874db3066b5..143135ef24c 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -1768,8 +1768,8 @@ btr_cur_update_in_place( NOT call it if index is secondary */ if (!dict_index_is_clust(index) - || row_upd_changes_ord_field_binary(NULL, NULL, - index, update)) { + || row_upd_changes_ord_field_binary(index, update, thr, + NULL, NULL)) { /* Remove possible hash index pointer to this record */ btr_search_update_hash_on_delete(cursor); diff --git a/storage/innodb_plugin/include/row0upd.h b/storage/innodb_plugin/include/row0upd.h index b61e6b6dca1..97b7ec49a17 100644 --- a/storage/innodb_plugin/include/row0upd.h +++ b/storage/innodb_plugin/include/row0upd.h @@ -280,19 +280,29 @@ NOTE: we compare the fields as binary strings! @return TRUE if update vector changes an ordering field in the index record */ UNIV_INTERN ibool -row_upd_changes_ord_field_binary( -/*=============================*/ +row_upd_changes_ord_field_binary_func( +/*==================================*/ + dict_index_t* index, /*!< in: index of the record */ + const upd_t* update, /*!< in: update vector for the row; NOTE: the + field numbers in this MUST be clustered index + positions! */ +#ifdef UNIV_DEBUG + const que_thr_t*thr, /*!< in: query thread */ +#endif /* UNIV_DEBUG */ const dtuple_t* row, /*!< in: old value of row, or NULL if the row and the data values in update are not known when this function is called, e.g., at compile time */ - const row_ext_t*ext, /*!< NULL, or prefixes of the externally + const row_ext_t*ext) /*!< NULL, or prefixes of the externally stored columns in the old row */ - dict_index_t* index, /*!< in: index of the record */ - const upd_t* update) /*!< in: update vector for the row; NOTE: the - field numbers in this MUST be clustered index - positions! */ - __attribute__((nonnull(3,4), warn_unused_result)); + __attribute__((nonnull(1,2), warn_unused_result)); +#ifdef UNIV_DEBUG +# define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \ + row_upd_changes_ord_field_binary_func(index,update,thr,row,ext) +#else /* UNIV_DEBUG */ +# define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \ + row_upd_changes_ord_field_binary_func(index,update,row,ext) +#endif /* UNIV_DEBUG */ /***********************************************************//** Checks if an update vector changes an ordering field of an index record. This function is fast if the update vector is short or the number of ordering diff --git a/storage/innodb_plugin/row/row0purge.c b/storage/innodb_plugin/row/row0purge.c index 8bf2ae0f458..c91ec2e8a3b 100644 --- a/storage/innodb_plugin/row/row0purge.c +++ b/storage/innodb_plugin/row/row0purge.c @@ -387,8 +387,11 @@ Purges an update of an existing record. Also purges an update of a delete marked record if that record contained an externally stored field. */ static void -row_purge_upd_exist_or_extern( -/*==========================*/ +row_purge_upd_exist_or_extern_func( +/*===============================*/ +#ifdef UNIV_DEBUG + const que_thr_t*thr, /*!< in: query thread */ +#endif /* UNIV_DEBUG */ purge_node_t* node) /*!< in: row purge node */ { mem_heap_t* heap; @@ -413,8 +416,8 @@ row_purge_upd_exist_or_extern( while (node->index != NULL) { index = node->index; - if (row_upd_changes_ord_field_binary(NULL, NULL, node->index, - node->update)) { + if (row_upd_changes_ord_field_binary(node->index, node->update, + thr, NULL, NULL)) { /* Build the older version of the index entry */ entry = row_build_index_entry(node->row, NULL, index, heap); @@ -496,6 +499,14 @@ skip_secondaries: } } +#ifdef UNIV_DEBUG +# define row_purge_upd_exist_or_extern(thr,node) \ + row_purge_upd_exist_or_extern_func(thr,node) +#else /* UNIV_DEBUG */ +# define row_purge_upd_exist_or_extern(thr,node) \ + row_purge_upd_exist_or_extern_func(node) +#endif /* UNIV_DEBUG */ + /***********************************************************//** Parses the row reference and other info in a modify undo log record. @return TRUE if purge operation required: NOTE that then the CALLER @@ -654,7 +665,7 @@ row_purge( } else if (updated_extern || node->rec_type == TRX_UNDO_UPD_EXIST_REC) { - row_purge_upd_exist_or_extern(node); + row_purge_upd_exist_or_extern(thr, node); } if (node->found_clust) { diff --git a/storage/innodb_plugin/row/row0umod.c b/storage/innodb_plugin/row/row0umod.c index 562f8093c38..f7736935489 100644 --- a/storage/innodb_plugin/row/row0umod.c +++ b/storage/innodb_plugin/row/row0umod.c @@ -668,8 +668,9 @@ row_undo_mod_upd_exist_sec( while (node->index != NULL) { index = node->index; - if (row_upd_changes_ord_field_binary( - node->row, node->ext, node->index, node->update)) { + if (row_upd_changes_ord_field_binary(node->index, node->update, + thr, + node->row, node->ext)) { /* Build the newest version of the index entry */ entry = row_build_index_entry(node->row, node->ext, diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 691d263e6ed..b5d4aeb434e 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1192,25 +1192,31 @@ NOTE: we compare the fields as binary strings! @return TRUE if update vector changes an ordering field in the index record */ UNIV_INTERN ibool -row_upd_changes_ord_field_binary( -/*=============================*/ +row_upd_changes_ord_field_binary_func( +/*==================================*/ + dict_index_t* index, /*!< in: index of the record */ + const upd_t* update, /*!< in: update vector for the row; NOTE: the + field numbers in this MUST be clustered index + positions! */ +#ifdef UNIV_DEBUG + const que_thr_t*thr, /*!< in: query thread */ +#endif /* UNIV_DEBUG */ const dtuple_t* row, /*!< in: old value of row, or NULL if the row and the data values in update are not known when this function is called, e.g., at compile time */ - const row_ext_t*ext, /*!< NULL, or prefixes of the externally + const row_ext_t*ext) /*!< NULL, or prefixes of the externally stored columns in the old row */ - dict_index_t* index, /*!< in: index of the record */ - const upd_t* update) /*!< in: update vector for the row; NOTE: the - field numbers in this MUST be clustered index - positions! */ { ulint n_unique; ulint i; const dict_index_t* clust_index; - ut_ad(update); ut_ad(index); + ut_ad(update); + ut_ad(thr); + ut_ad(thr->graph); + ut_ad(thr->graph->trx); n_unique = dict_index_get_n_unique(index); @@ -1263,9 +1269,14 @@ row_upd_changes_ord_field_binary( if (UNIV_LIKELY_NULL(buf)) { if (UNIV_UNLIKELY(buf == field_ref_zero)) { - /* This should never happen, but - we try to fail safe here. */ - ut_ad(0); + /* The externally stored field + was not written yet. This + record should only be seen by + recv_recovery_rollback_active(), + when the server had crashed before + storing the field. */ + ut_ad(thr->graph->trx->is_recovered); + ut_ad(trx_is_recv(thr->graph->trx)); return(TRUE); } @@ -1612,8 +1623,8 @@ row_upd_sec_step( ut_ad(!dict_index_is_clust(node->index)); if (node->state == UPD_NODE_UPDATE_ALL_SEC - || row_upd_changes_ord_field_binary(node->row, node->ext, - node->index, node->update)) { + || row_upd_changes_ord_field_binary(node->index, node->update, + thr, node->row, node->ext)) { return(row_upd_sec_index_entry(node, thr)); } @@ -2140,8 +2151,8 @@ exit_func: row_upd_store_row(node); - if (row_upd_changes_ord_field_binary(node->row, node->ext, index, - node->update)) { + if (row_upd_changes_ord_field_binary(index, node->update, thr, + node->row, node->ext)) { /* Update causes an ordering field (ordering fields within the B-tree) of the clustered index record to change: perform diff --git a/storage/innodb_plugin/trx/trx0roll.c b/storage/innodb_plugin/trx/trx0roll.c index 1a43e419214..a4bbf7fd652 100644 --- a/storage/innodb_plugin/trx/trx0roll.c +++ b/storage/innodb_plugin/trx/trx0roll.c @@ -48,8 +48,8 @@ Created 3/26/1996 Heikki Tuuri rollback */ #define TRX_ROLL_TRUNC_THRESHOLD 1 -/** In crash recovery, the current trx to be rolled back */ -static trx_t* trx_roll_crash_recv_trx = NULL; +/** In crash recovery, the current trx to be rolled back; NULL otherwise */ +static const trx_t* trx_roll_crash_recv_trx = NULL; /** In crash recovery we set this to the undo n:o of the current trx to be rolled back. Then we can print how many % the rollback has progressed. */ From 1f796c2b646824aeac2927599ba3e51594cd3534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 14:05:12 +0200 Subject: [PATCH 15/27] Bug #55284 diagnostics: Enable UNIV_DEBUG_FILE_ACCESSES by UNIV_DEBUG It was the enabling of UNIV_DEBUG_FILE_ACCESSES that caught Bug #55284 in the first place. This is a very light piece of of debug code, and there really is no reason why it is not enabled in all debug builds. rb://551 approved by Jimmy Yang --- storage/innodb_plugin/buf/buf0buf.c | 30 +++++++++++++------------ storage/innodb_plugin/fsp/fsp0fsp.c | 8 +++---- storage/innodb_plugin/ibuf/ibuf0ibuf.c | 8 +++---- storage/innodb_plugin/include/buf0buf.h | 8 +++---- storage/innodb_plugin/include/univ.i | 5 ++--- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/storage/innodb_plugin/buf/buf0buf.c b/storage/innodb_plugin/buf/buf0buf.c index 6e76e4c65be..6bbd5565c58 100644 --- a/storage/innodb_plugin/buf/buf0buf.c +++ b/storage/innodb_plugin/buf/buf0buf.c @@ -657,9 +657,9 @@ buf_block_init( block->modify_clock = 0; -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG block->page.file_page_was_freed = FALSE; -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ block->check_index_page_at_flush = FALSE; block->index = NULL; @@ -1600,7 +1600,7 @@ buf_page_peek_if_search_hashed( return(is_hashed); } -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG /********************************************************************//** Sets file_page_was_freed TRUE if the page is found in the buffer pool. This function should be called when we free a file page and want the @@ -1621,6 +1621,8 @@ buf_page_set_file_page_was_freed( bpage = buf_page_hash_get(space, offset); if (bpage) { + /* bpage->file_page_was_freed can already hold + when this code is invoked from dict_drop_index_tree() */ bpage->file_page_was_freed = TRUE; } @@ -1656,7 +1658,7 @@ buf_page_reset_file_page_was_freed( return(bpage); } -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ /********************************************************************//** Get read access to a compressed page (usually of type @@ -1753,7 +1755,7 @@ got_block: buf_page_set_accessed_make_young(bpage, access_time); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(!bpage->file_page_was_freed); #endif @@ -2321,7 +2323,7 @@ wait_until_unfixed: buf_page_set_accessed_make_young(&block->page, access_time); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(!block->page.file_page_was_freed); #endif @@ -2479,7 +2481,7 @@ buf_page_optimistic_get( ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(block->page.file_page_was_freed == FALSE); #endif if (UNIV_UNLIKELY(!access_time)) { @@ -2587,7 +2589,7 @@ buf_page_get_known_nowait( ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(block->page.file_page_was_freed == FALSE); #endif @@ -2670,9 +2672,9 @@ buf_page_try_get_func( ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(block->page.file_page_was_freed == FALSE); -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); buf_pool->stat.n_page_gets++; @@ -2701,9 +2703,9 @@ buf_page_init_low( bpage->newest_modification = 0; bpage->oldest_modification = 0; HASH_INVALIDATE(bpage, hash); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG bpage->file_page_was_freed = FALSE; -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ } /********************************************************************//** @@ -3009,9 +3011,9 @@ buf_page_create( #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(space, offset) == 0); #endif -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG block->page.file_page_was_freed = FALSE; -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ /* Page can be found in buf_pool */ buf_pool_mutex_exit(); diff --git a/storage/innodb_plugin/fsp/fsp0fsp.c b/storage/innodb_plugin/fsp/fsp0fsp.c index e9d24b8fdf6..d091a14c474 100644 --- a/storage/innodb_plugin/fsp/fsp0fsp.c +++ b/storage/innodb_plugin/fsp/fsp0fsp.c @@ -3444,9 +3444,9 @@ fseg_free_page( fseg_free_page_low(seg_inode, space, zip_size, page, mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG buf_page_set_file_page_was_freed(space, page); -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ } /**********************************************************************//** @@ -3513,13 +3513,13 @@ fseg_free_extent( fsp_free_extent(space, zip_size, page, mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG for (i = 0; i < FSP_EXTENT_SIZE; i++) { buf_page_set_file_page_was_freed(space, first_page_in_extent + i); } -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ } /**********************************************************************//** diff --git a/storage/innodb_plugin/ibuf/ibuf0ibuf.c b/storage/innodb_plugin/ibuf/ibuf0ibuf.c index 701e8f0ef04..23981ac388e 100644 --- a/storage/innodb_plugin/ibuf/ibuf0ibuf.c +++ b/storage/innodb_plugin/ibuf/ibuf0ibuf.c @@ -1878,9 +1878,9 @@ ibuf_remove_free_page(void) fseg_free_page(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, IBUF_SPACE_ID, page_no, &mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG buf_page_reset_file_page_was_freed(IBUF_SPACE_ID, page_no); -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ ibuf_enter(); @@ -1922,9 +1922,9 @@ ibuf_remove_free_page(void) ibuf_bitmap_page_set_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_IBUF, FALSE, &mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG buf_page_set_file_page_was_freed(IBUF_SPACE_ID, page_no); -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ mtr_commit(&mtr); mutex_exit(&ibuf_mutex); diff --git a/storage/innodb_plugin/include/buf0buf.h b/storage/innodb_plugin/include/buf0buf.h index d903b443920..a16de67aa3a 100644 --- a/storage/innodb_plugin/include/buf0buf.h +++ b/storage/innodb_plugin/include/buf0buf.h @@ -368,7 +368,7 @@ buf_reset_check_index_page_at_flush( /*================================*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: page number */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG /********************************************************************//** Sets file_page_was_freed TRUE if the page is found in the buffer pool. This function should be called when we free a file page and want the @@ -393,7 +393,7 @@ buf_page_reset_file_page_was_freed( /*===============================*/ ulint space, /*!< in: space id */ ulint offset); /*!< in: page number */ -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ /********************************************************************//** Reads the freed_page_clock of a buffer block. @return freed_page_clock */ @@ -1135,11 +1135,11 @@ struct buf_page_struct{ 0 if the block was never accessed in the buffer pool */ /* @} */ -# ifdef UNIV_DEBUG_FILE_ACCESSES +# if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ibool file_page_was_freed; /*!< this is set to TRUE when fsp frees a page in buffer pool */ -# endif /* UNIV_DEBUG_FILE_ACCESSES */ +# endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ #endif /* !UNIV_HOTBACKUP */ }; diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index 4425950748b..4ba48b10896 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -182,9 +182,8 @@ command. Not tested on Windows. */ #define UNIV_DEBUG_LOCK_VALIDATE /* Enable ut_ad(lock_rec_validate_page()) assertions. */ -#define UNIV_DEBUG_FILE_ACCESSES /* Debug .ibd file access - (field file_page_was_freed - in buf_page_t) */ +#define UNIV_DEBUG_FILE_ACCESSES /* Enable freed block access + debugging without UNIV_DEBUG */ #define UNIV_LRU_DEBUG /* debug the buffer pool LRU */ #define UNIV_HASH_DEBUG /* debug HASH_ macros */ #define UNIV_LIST_DEBUG /* debug UT_LIST_ macros */ From f2eacde4cd905303ff906d9e33308b8ba03c17f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 14:10:12 +0200 Subject: [PATCH 16/27] Bug #55284 diagnostics: When UNIV_DEBUG, do not tolerate garbage in Antelope files in btr_check_blob_fil_page_type(). Unfortunately, we must keep the check in production builds, because InnoDB wrote uninitialized garbage to FIL_PAGE_TYPE until fairly recently (5.1.x). rb://546 approved by Jimmy Yang --- storage/innodb_plugin/btr/btr0cur.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 143135ef24c..46cd4a81ec5 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -4169,6 +4169,7 @@ btr_check_blob_fil_page_type( if (UNIV_UNLIKELY(type != FIL_PAGE_TYPE_BLOB)) { ulint flags = fil_space_get_flags(space_id); +#ifndef UNIV_DEBUG /* Improve debug test coverage */ if (UNIV_LIKELY ((flags & DICT_TF_FORMAT_MASK) == DICT_TF_FORMAT_51)) { /* Old versions of InnoDB did not initialize @@ -4177,6 +4178,7 @@ btr_check_blob_fil_page_type( a BLOB page that is in Antelope format.*/ return; } +#endif /* !UNIV_DEBUG */ ut_print_timestamp(stderr); fprintf(stderr, From 7c45708f5d29747da043aea144c2f8ba8c142c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 14:12:49 +0200 Subject: [PATCH 17/27] Non-functional changes (cleanup) made while narrowing down Bug #55284: row_purge(): Change the return type to void. (The return value always was DB_SUCCESS.) Remove some local variables. row_undo_mod_remove_clust_low(): Remove some local variables. rb://547 approved by Jimmy Yang --- storage/innodb_plugin/row/row0purge.c | 47 +++++++-------------------- storage/innodb_plugin/row/row0umod.c | 30 +++++------------ 2 files changed, 19 insertions(+), 58 deletions(-) diff --git a/storage/innodb_plugin/row/row0purge.c b/storage/innodb_plugin/row/row0purge.c index c91ec2e8a3b..752a2ec9e83 100644 --- a/storage/innodb_plugin/row/row0purge.c +++ b/storage/innodb_plugin/row/row0purge.c @@ -613,47 +613,32 @@ err_exit: /***********************************************************//** Fetches an undo log record and does the purge for the recorded operation. If none left, or the current purge completed, returns the control to the -parent node, which is always a query thread node. -@return DB_SUCCESS if operation successfully completed, else error code */ -static -ulint +parent node, which is always a query thread node. */ +static __attribute__((nonnull)) +void row_purge( /*======*/ purge_node_t* node, /*!< in: row purge node */ que_thr_t* thr) /*!< in: query thread */ { - roll_ptr_t roll_ptr; - ibool purge_needed; ibool updated_extern; - trx_t* trx; - ut_ad(node && thr); + ut_ad(node); + ut_ad(thr); - trx = thr_get_trx(thr); - - node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr, - &(node->reservation), + node->undo_rec = trx_purge_fetch_next_rec(&node->roll_ptr, + &node->reservation, node->heap); if (!node->undo_rec) { /* Purge completed for this query thread */ thr->run_node = que_node_get_parent(node); - return(DB_SUCCESS); + return; } - node->roll_ptr = roll_ptr; - - if (node->undo_rec == &trx_purge_dummy_rec) { - purge_needed = FALSE; - } else { - purge_needed = row_purge_parse_undo_rec(node, &updated_extern, - thr); - /* If purge_needed == TRUE, we must also remember to unfreeze - data dictionary! */ - } - - if (purge_needed) { + if (node->undo_rec != &trx_purge_dummy_rec + && row_purge_parse_undo_rec(node, &updated_extern, thr)) { node->found_clust = FALSE; node->index = dict_table_get_next_index( @@ -672,7 +657,7 @@ row_purge( btr_pcur_close(&(node->pcur)); } - row_mysql_unfreeze_data_dictionary(trx); + row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); } /* Do some cleanup */ @@ -680,8 +665,6 @@ row_purge( mem_heap_empty(node->heap); thr->run_node = node; - - return(DB_SUCCESS); } /***********************************************************//** @@ -695,9 +678,6 @@ row_purge_step( que_thr_t* thr) /*!< in: query thread */ { purge_node_t* node; -#ifdef UNIV_DEBUG - ulint err; -#endif /* UNIV_DEBUG */ ut_ad(thr); @@ -705,12 +685,7 @@ row_purge_step( ut_ad(que_node_get_type(node) == QUE_NODE_PURGE); -#ifdef UNIV_DEBUG - err = -#endif /* UNIV_DEBUG */ row_purge(node, thr); - ut_ad(err == DB_SUCCESS); - return(thr); } diff --git a/storage/innodb_plugin/row/row0umod.c b/storage/innodb_plugin/row/row0umod.c index f7736935489..5202a498eed 100644 --- a/storage/innodb_plugin/row/row0umod.c +++ b/storage/innodb_plugin/row/row0umod.c @@ -173,40 +173,26 @@ row_undo_mod_remove_clust_low( mtr_t* mtr, /*!< in: mtr */ ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { - btr_pcur_t* pcur; btr_cur_t* btr_cur; ulint err; - ibool success; ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC); - pcur = &(node->pcur); - btr_cur = btr_pcur_get_btr_cur(pcur); - success = btr_pcur_restore_position(mode, pcur, mtr); + /* Find out if the record has been purged already + or if we can remove it. */ - if (!success) { + if (!btr_pcur_restore_position(mode, &node->pcur, mtr) + || row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) { return(DB_SUCCESS); } - /* Find out if we can remove the whole clustered index record */ - - if (node->rec_type == TRX_UNDO_UPD_DEL_REC - && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) { - - /* Ok, we can remove */ - } else { - return(DB_SUCCESS); - } + btr_cur = btr_pcur_get_btr_cur(&node->pcur); if (mode == BTR_MODIFY_LEAF) { - success = btr_cur_optimistic_delete(btr_cur, mtr); - - if (success) { - err = DB_SUCCESS; - } else { - err = DB_FAIL; - } + err = btr_cur_optimistic_delete(btr_cur, mtr) + ? DB_SUCCESS + : DB_FAIL; } else { ut_ad(mode == BTR_MODIFY_TREE); From 5adf2313f713821841d979472eef0d7b07658965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 15:51:08 +0200 Subject: [PATCH 18/27] Bug #55284 diagnostics: Introduce UNIV_BLOB_LIGHT_DEBUG, enabled by UNIV_DEBUG btr_rec_get_field_ref_offs(), btr_rec_get_field_ref(): New functions. Get the pointer to an externally stored field. btr_cur_set_ownership_of_extern_field(): Assert that the BLOB has not already been disowned. btr_store_big_rec_extern_fields(): Rename to btr_store_big_rec_extern_fields_func() and add the debug parameter update_in_place. All pointers to externally stored columns in the record must either be zero or they must be pointers to inherited columns, owned by this record or an earlier record version. For any BLOB that is stored, the BLOB pointer must previously have been zero. When the function completes, all BLOB pointers must be nonzero and owned by the record. rb://549 approved by Jimmy Yang --- storage/innodb_plugin/btr/btr0cur.c | 150 ++++++++++++++++-------- storage/innodb_plugin/include/btr0cur.h | 42 ++++++- storage/innodb_plugin/include/univ.i | 2 + storage/innodb_plugin/row/row0ins.c | 2 +- storage/innodb_plugin/row/row0upd.c | 2 +- 5 files changed, 144 insertions(+), 54 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 46cd4a81ec5..704cc606a5f 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -186,7 +186,7 @@ static ulint btr_rec_get_externally_stored_len( /*==============================*/ - rec_t* rec, /*!< in: record */ + const rec_t* rec, /*!< in: record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ #endif /* !UNIV_HOTBACKUP */ @@ -3483,6 +3483,35 @@ btr_estimate_number_of_different_key_vals( /*================== EXTERNAL STORAGE OF BIG FIELDS ===================*/ +/***********************************************************//** +Gets the offset of the pointer to the externally stored part of a field. +@return offset of the pointer to the externally stored part */ +static +ulint +btr_rec_get_field_ref_offs( +/*=======================*/ + const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ + ulint n) /*!< in: index of the external field */ +{ + ulint field_ref_offs; + ulint local_len; + + ut_a(rec_offs_nth_extern(offsets, n)); + field_ref_offs = rec_get_nth_field_offs(offsets, n, &local_len); + ut_a(local_len != UNIV_SQL_NULL); + ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); + + return(field_ref_offs + local_len - BTR_EXTERN_FIELD_REF_SIZE); +} + +/** Gets a pointer to the externally stored part of a field. +@param rec record +@param offsets rec_get_offsets(rec) +@param n index of the externally stored field +@return pointer to the externally stored part */ +#define btr_rec_get_field_ref(rec, offsets, n) \ + ((rec) + btr_rec_get_field_ref_offs(offsets, n)) + /***********************************************************//** Gets the externally stored size of a record, in units of a database page. @return externally stored part, in units of a database page */ @@ -3490,28 +3519,27 @@ static ulint btr_rec_get_externally_stored_len( /*==============================*/ - rec_t* rec, /*!< in: record */ + const rec_t* rec, /*!< in: record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint n_fields; - byte* data; - ulint local_len; - ulint extern_len; ulint total_extern_len = 0; ulint i; ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); + + if (!rec_offs_any_extern(offsets)) { + return(0); + } + n_fields = rec_offs_n_fields(offsets); for (i = 0; i < n_fields; i++) { if (rec_offs_nth_extern(offsets, i)) { - data = rec_get_nth_field(rec, offsets, i, &local_len); - - local_len -= BTR_EXTERN_FIELD_REF_SIZE; - - extern_len = mach_read_from_4(data + local_len - + BTR_EXTERN_LEN + 4); + ulint extern_len = mach_read_from_4( + btr_rec_get_field_ref(rec, offsets, i) + + BTR_EXTERN_LEN + 4); total_extern_len += ut_calc_align(extern_len, UNIV_PAGE_SIZE); @@ -3541,7 +3569,7 @@ btr_cur_set_ownership_of_extern_field( ulint byte_val; data = rec_get_nth_field(rec, offsets, i, &local_len); - + ut_ad(rec_offs_nth_extern(offsets, i)); ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; @@ -3551,6 +3579,9 @@ btr_cur_set_ownership_of_extern_field( if (val) { byte_val = byte_val & (~BTR_EXTERN_OWNER_FLAG); } else { +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + ut_a(!(byte_val & BTR_EXTERN_OWNER_FLAG)); +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ byte_val = byte_val | BTR_EXTERN_OWNER_FLAG; } @@ -3788,8 +3819,8 @@ file segment of the index tree. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ UNIV_INTERN ulint -btr_store_big_rec_extern_fields( -/*============================*/ +btr_store_big_rec_extern_fields_func( +/*=================================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ buf_block_t* rec_block, /*!< in/out: block containing rec */ @@ -3798,11 +3829,17 @@ btr_store_big_rec_extern_fields( the "external storage" flags in offsets will not correspond to rec when this function returns */ - big_rec_t* big_rec_vec, /*!< in: vector containing fields +#ifdef UNIV_DEBUG + mtr_t* local_mtr, /*!< in: mtr containing the + latch to rec and to the tree */ +#endif /* UNIV_DEBUG */ +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + ibool update_in_place,/*! in: TRUE if the record is updated + in place (not delete+insert) */ +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ + const big_rec_t*big_rec_vec) /*!< in: vector containing fields to be stored externally */ - mtr_t* local_mtr __attribute__((unused))) /*!< in: mtr - containing the latch to rec and to the - tree */ + { ulint rec_page_no; byte* field_ref; @@ -3820,6 +3857,7 @@ btr_store_big_rec_extern_fields( z_stream c_stream; ut_ad(rec_offs_validate(rec, index, offsets)); + ut_ad(rec_offs_any_extern(offsets)); ut_ad(mtr_memo_contains(local_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(local_mtr, rec_block, MTR_MEMO_PAGE_X_FIX)); @@ -3851,21 +3889,37 @@ btr_store_big_rec_extern_fields( ut_a(err == Z_OK); } +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + /* All pointers to externally stored columns in the record + must either be zero or they must be pointers to inherited + columns, owned by this record or an earlier record version. */ + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (!rec_offs_nth_extern(offsets, i)) { + continue; + } + field_ref = btr_rec_get_field_ref(rec, offsets, i); + + ut_a(!(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG)); + /* Either this must be an update in place, + or the BLOB must be inherited, or the BLOB pointer + must be zero (will be written in this function). */ + ut_a(update_in_place + || (field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_INHERITED_FLAG) + || !memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + } +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ /* We have to create a file segment to the tablespace for each field and put the pointer to the field in rec */ for (i = 0; i < big_rec_vec->n_fields; i++) { - ut_ad(rec_offs_nth_extern(offsets, - big_rec_vec->fields[i].field_no)); - { - ulint local_len; - field_ref = rec_get_nth_field( - rec, offsets, big_rec_vec->fields[i].field_no, - &local_len); - ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); - local_len -= BTR_EXTERN_FIELD_REF_SIZE; - field_ref += local_len; - } + field_ref = btr_rec_get_field_ref( + rec, offsets, big_rec_vec->fields[i].field_no); +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + /* A zero BLOB pointer should have been initially inserted. */ + ut_a(!memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ extern_len = big_rec_vec->fields[i].len; UNIV_MEM_ASSERT_RW(big_rec_vec->fields[i].data, extern_len); @@ -4147,6 +4201,23 @@ next_zip_page: mem_heap_free(heap); } +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + /* All pointers to externally stored columns in the record + must be valid. */ + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (!rec_offs_nth_extern(offsets, i)) { + continue; + } + + field_ref = btr_rec_get_field_ref(rec, offsets, i); + + /* The pointer must not be zero. */ + ut_a(0 != memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + /* The column must not be disowned by this record. */ + ut_a(!(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG)); + } +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ return(DB_SUCCESS); } @@ -4228,23 +4299,13 @@ btr_free_externally_stored_field( ulint page_no; ulint next_page_no; mtr_t mtr; -#ifdef UNIV_DEBUG + ut_ad(mtr_memo_contains(local_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains_page(local_mtr, field_ref, MTR_MEMO_PAGE_X_FIX)); ut_ad(!rec || rec_offs_validate(rec, index, offsets)); - - if (rec) { - ulint local_len; - const byte* f = rec_get_nth_field(rec, offsets, - i, &local_len); - ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); - local_len -= BTR_EXTERN_FIELD_REF_SIZE; - f += local_len; - ut_ad(f == field_ref); - } -#endif /* UNIV_DEBUG */ + ut_ad(!rec || field_ref == btr_rec_get_field_ref(rec, offsets, i)); if (UNIV_UNLIKELY(!memcmp(field_ref, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE))) { @@ -4409,13 +4470,8 @@ btr_rec_free_externally_stored_fields( for (i = 0; i < n_fields; i++) { if (rec_offs_nth_extern(offsets, i)) { - ulint len; - byte* data - = rec_get_nth_field(rec, offsets, i, &len); - ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); - btr_free_externally_stored_field( - index, data + len - BTR_EXTERN_FIELD_REF_SIZE, + index, btr_rec_get_field_ref(rec, offsets, i), rec, offsets, page_zip, i, rb_ctx, mtr); } } diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index cb8cb399715..ece3621fa97 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -512,8 +512,8 @@ file segment of the index tree. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ UNIV_INTERN ulint -btr_store_big_rec_extern_fields( -/*============================*/ +btr_store_big_rec_extern_fields_func( +/*=================================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ buf_block_t* rec_block, /*!< in/out: block containing rec */ @@ -522,10 +522,42 @@ btr_store_big_rec_extern_fields( the "external storage" flags in offsets will not correspond to rec when this function returns */ - big_rec_t* big_rec_vec, /*!< in: vector containing fields +#ifdef UNIV_DEBUG + mtr_t* local_mtr, /*!< in: mtr containing the + latch to rec and to the tree */ +#endif /* UNIV_DEBUG */ +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + ibool update_in_place,/*! in: TRUE if the record is updated + in place (not delete+insert) */ +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ + const big_rec_t*big_rec_vec) /*!< in: vector containing fields to be stored externally */ - mtr_t* local_mtr); /*!< in: mtr containing the latch to - rec and to the tree */ + __attribute__((nonnull)); + +/** Stores the fields in big_rec_vec to the tablespace and puts pointers to +them in rec. The extern flags in rec will have to be set beforehand. +The fields are stored on pages allocated from leaf node +file segment of the index tree. +@param index in: clustered index; MUST be X-latched by mtr +@param b in/out: block containing rec; MUST be X-latched by mtr +@param rec in/out: clustered index record +@param offsets in: rec_get_offsets(rec, index); + the "external storage" flags in offsets will not be adjusted +@param mtr in: mini-transaction that holds x-latch on index and b +@param upd in: TRUE if the record is updated in place (not delete+insert) +@param big in: vector containing fields to be stored externally +@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ +#ifdef UNIV_DEBUG +# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \ + btr_store_big_rec_extern_fields_func(index,b,rec,offsets,mtr,upd,big) +#elif defined UNIV_BLOB_LIGHT_DEBUG +# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \ + btr_store_big_rec_extern_fields_func(index,b,rec,offsets,upd,big) +#else +# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \ + btr_store_big_rec_extern_fields_func(index,b,rec,offsets,big) +#endif + /*******************************************************************//** Frees the space in an externally stored field to the file space management if the field in data is owned the externally stored field, diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index 4ba48b10896..690bfd5d6a9 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -177,6 +177,8 @@ command. Not tested on Windows. */ debugging without UNIV_DEBUG */ #define UNIV_BUF_DEBUG /* Enable buffer pool debugging without UNIV_DEBUG */ +#define UNIV_BLOB_LIGHT_DEBUG /* Enable off-page column + debugging without UNIV_DEBUG */ #define UNIV_DEBUG /* Enable ut_ad() assertions and disable UNIV_INLINE */ #define UNIV_DEBUG_LOCK_VALIDATE /* Enable diff --git a/storage/innodb_plugin/row/row0ins.c b/storage/innodb_plugin/row/row0ins.c index 298c601c7e3..8050c099751 100644 --- a/storage/innodb_plugin/row/row0ins.c +++ b/storage/innodb_plugin/row/row0ins.c @@ -2130,7 +2130,7 @@ function_exit: err = btr_store_big_rec_extern_fields( index, btr_cur_get_block(&cursor), - rec, offsets, big_rec, &mtr); + rec, offsets, &mtr, FALSE, big_rec); if (modify) { dtuple_big_rec_free(big_rec); diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index b5d4aeb434e..9ded3d68018 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1952,7 +1952,7 @@ row_upd_clust_rec( index, btr_cur_get_block(btr_cur), rec, rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap), - big_rec, mtr); + mtr, TRUE, big_rec); mtr_commit(mtr); } From cb10771fd820be7f6c28b689b9f93b78baa991af Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Thu, 3 Feb 2011 16:09:33 +0000 Subject: [PATCH 19/27] BUG#59147: rpl_circular_for_4_hosts fails sporadically There is one part of the test case that needs to break and re-establish the circular topology. For this the test stops the slave threads on a couple of servers and restarts them with START SLAVE. However, no check is done on the status of the IO or SQL threads before proceeding with the subsequent commands. Because rpl_only_running_threads is set to 1 this can lead to silently not syncing all slave threads as expected, ultimately resulting in unexpected results (and consequently on a failing test run). We fix this by replacing the START SLAVE instructions with calls to --source include/start_slave.inc, which will wait for the slave threads to be running (show 'Yes' in Slave_IO|SQL_Running fields of SHOW SLAVE STATUS) before proceeding. Additionally, we change rpl_sync.inc to make the IO thread report that it is running when its running status is any other than 'No'. --- mysql-test/include/rpl_sync.inc | 2 +- mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result | 4 ++-- mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/include/rpl_sync.inc b/mysql-test/include/rpl_sync.inc index a05bee23981..be2904528ff 100644 --- a/mysql-test/include/rpl_sync.inc +++ b/mysql-test/include/rpl_sync.inc @@ -88,7 +88,7 @@ while ($_rpl_i) { { --echo Sync IO: $_rpl_slave_io_running; Sync SQL: $_rpl_slave_sql_running } - --let $_rpl_slave_io_running= `SELECT IF('$_rpl_slave_io_running' = 'Yes', 1, '')` + --let $_rpl_slave_io_running= `SELECT IF('$_rpl_slave_io_running' != 'No', 1, '')` --let $_rpl_slave_sql_running= `SELECT IF('$_rpl_slave_sql_running' = 'Yes', 1, '')` if ($_rpl_slave_io_running) { diff --git a/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result b/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result index fb1d3f8258e..6981e549918 100644 --- a/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result +++ b/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result @@ -121,11 +121,11 @@ Master D 12 D * Remove wrong event from C and restore B->C->D * include/stop_slave.inc DELETE FROM t1 WHERE a = 6; -START SLAVE; +include/start_slave.inc RESET MASTER; RESET SLAVE; include/rpl_change_topology.inc [new topology=1->2->3->4->1] -START SLAVE; +include/start_slave.inc include/rpl_sync.inc * Check data inserted before restoring schema A->B->C->D->A * diff --git a/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test b/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test index 6099637e3e9..820ffc42933 100644 --- a/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test +++ b/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test @@ -175,7 +175,7 @@ SELECT 'Master D',a,b FROM t1 WHERE c = 3 ORDER BY a,b; source include/stop_slave.inc; --connection server_3 DELETE FROM t1 WHERE a = 6; -START SLAVE; +--source include/start_slave.inc --connection server_2 --sync_slave_with_master server_3 RESET MASTER; @@ -189,7 +189,7 @@ RESET SLAVE; --source include/rpl_change_topology.inc #--replace_result $SERVER_MYPORT_3 SERVER_MYPORT_3 $file_d LOG_FILE $pos_d LOG_POS #--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$SERVER_MYPORT_3,master_user='root',master_log_file='$file_d',master_log_pos=$pos_d -START SLAVE; +--source include/start_slave.inc --connection server_3 --sync_slave_with_master server_4 --source include/rpl_sync.inc From 5082c4d1de5d9fb70fef89f6e89e00097ce9374e Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 4 Feb 2011 12:46:47 +0200 Subject: [PATCH 20/27] Fix Bug#59874 Valgrind warning in InnoDB compression code Add suppressions for a bogus valgrind warnings. --- mysql-test/valgrind.supp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 1016923a360..367dc62898a 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -744,3 +744,37 @@ Memcheck:Addr1 fun:buf_buddy_relocate } + +{ + Bug 59874 Valgrind warning in InnoDB compression code + Memcheck:Cond + obj:*/libz.so* + obj:*/libz.so* + fun:deflate + fun:btr_store_big_rec_extern_fields_func + fun:row_ins_index_entry_low + fun:row_ins_index_entry + fun:row_ins_index_entry_step + fun:row_ins + fun:row_ins_step + fun:row_insert_for_mysql +} + +{ + In page0zip.c we have already checked that the memory is initialized before calling deflate() + Memcheck:Cond + obj:*/libz.so* + obj:*/libz.so* + fun:deflate + fun:page_zip_compress + fun:page_cur_insert_rec_zip_reorg + fun:page_cur_insert_rec_zip + fun:page_cur_tuple_insert + fun:btr_cur_optimistic_insert + fun:row_ins_index_entry_low + fun:row_ins_index_entry + fun:row_ins_index_entry_step + fun:row_ins + fun:row_ins_step + fun:row_insert_for_mysql +} From 980868eb4e4db550497ba40674b5b64f2e062cd9 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Sat, 5 Feb 2011 11:02:00 +0600 Subject: [PATCH 21/27] Fixed bug#57450 - mysql client enter in an infinite loop if the standard input is a directory. The problem is that mysql monitor try to read from stdin without checking input source type. The solution is to stop reading data from standard input if a call to read(2) failed. A new test case was added into mysql.test. client/my_readline.h: Data members error and truncated was added to LINE_BUFFER structure. These data members used instead of out parameters in functions batch_readline, intern_read_line. client/mysql.cc: read_and_execute() was modified: set status.exit_status to 1 when the error occured while reading the next command line in non-interactive mode. Also the value of the truncated attribute of structure LINE_BUFF is taken into account only for non-iteractive mode. client/readline.cc: intern_read_line() was modified: cancel reading from input if fill_buffer() returns -1, e.g. if call to read failed. batch_readline was modified: set the error data member of LINE_BUFFER structure to value of my_errno when system error happened during call to my_read/my_realloc. mysql-test/t/mysql.test: Test for bug#57450 was added. --- client/my_readline.h | 4 +++- client/mysql.cc | 16 ++++++++++------ client/readline.cc | 33 ++++++++++++++++++++------------- mysql-test/t/mysql.test | 6 ++++++ 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/client/my_readline.h b/client/my_readline.h index 62ad19bece9..3376fc81761 100644 --- a/client/my_readline.h +++ b/client/my_readline.h @@ -25,9 +25,11 @@ typedef struct st_line_buffer uint eof; ulong max_size; ulong read_length; /* Length of last read string */ + int error; + bool truncated; } LINE_BUFFER; extern LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file); extern LINE_BUFFER *batch_readline_command(LINE_BUFFER *buffer, char * str); -extern char *batch_readline(LINE_BUFFER *buffer, bool *truncated); +extern char *batch_readline(LINE_BUFFER *buffer); extern void batch_readline_end(LINE_BUFFER *buffer); diff --git a/client/mysql.cc b/client/mysql.cc index dafe76a2401..b19aaf61e58 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1872,14 +1872,13 @@ static int read_and_execute(bool interactive) ulong line_number=0; bool ml_comment= 0; COMMANDS *com; - bool truncated= 0; status.exit_status=1; - + for (;;) { if (!interactive) { - line=batch_readline(status.line_buff, &truncated); + line=batch_readline(status.line_buff); /* Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF. Editors like "notepad" put this marker in @@ -1953,9 +1952,13 @@ static int read_and_execute(bool interactive) if (opt_outfile && line) fprintf(OUTFILE, "%s\n", line); } - if (!line) // End of file + // End of file or system error + if (!line) { - status.exit_status=0; + if (status.line_buff && status.line_buff->error) + status.exit_status= 1; + else + status.exit_status= 0; break; } @@ -1976,7 +1979,8 @@ static int read_and_execute(bool interactive) #endif continue; } - if (add_line(glob_buffer,line,&in_string,&ml_comment, truncated)) + if (add_line(glob_buffer, line, &in_string, &ml_comment, + status.line_buff ? status.line_buff->truncated : 0)) break; } /* if in batch mode, send last query even if it doesn't end with \g or go */ diff --git a/client/readline.cc b/client/readline.cc index b32cb71b0de..7f283502ecb 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -24,7 +24,7 @@ static bool init_line_buffer(LINE_BUFFER *buffer,File file,ulong size, ulong max_size); static bool init_line_buffer_from_string(LINE_BUFFER *buffer,char * str); static size_t fill_buffer(LINE_BUFFER *buffer); -static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length, bool *truncated); +static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length); LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) @@ -42,13 +42,12 @@ LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) } -char *batch_readline(LINE_BUFFER *line_buff, bool *truncated) +char *batch_readline(LINE_BUFFER *line_buff) { char *pos; ulong out_length; - DBUG_ASSERT(truncated != NULL); - if (!(pos=intern_read_line(line_buff,&out_length, truncated))) + if (!(pos=intern_read_line(line_buff, &out_length))) return 0; if (out_length && pos[out_length-1] == '\n') if (--out_length && pos[out_length-1] == '\r') /* Remove '\n' */ @@ -162,7 +161,10 @@ static size_t fill_buffer(LINE_BUFFER *buffer) if (!(buffer->buffer = (char*) my_realloc(buffer->buffer, buffer->bufread+1, MYF(MY_WME | MY_FAE)))) - return (uint) -1; + { + buffer->error= my_errno; + return (size_t) -1; + } buffer->start_of_line=buffer->buffer+start_offset; buffer->end=buffer->buffer+bufbytes; } @@ -177,7 +179,10 @@ static size_t fill_buffer(LINE_BUFFER *buffer) /* Read in new stuff. */ if ((read_count= my_read(buffer->file, (uchar*) buffer->end, read_count, MYF(MY_WME))) == MY_FILE_ERROR) + { + buffer->error= my_errno; return (size_t) -1; + } DBUG_PRINT("fill_buff", ("Got %lu bytes", (ulong) read_count)); @@ -198,8 +203,7 @@ static size_t fill_buffer(LINE_BUFFER *buffer) } - -char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length, bool *truncated) +char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length) { char *pos; size_t length; @@ -214,22 +218,25 @@ char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length, bool *truncated) if (pos == buffer->end) { /* - fill_buffer() can return 0 either on EOF in which case we abort - or when the internal buffer has hit the size limit. In the latter case - return what we have read so far and signal string truncation. + fill_buffer() can return NULL on EOF (in which case we abort), + on error, or when the internal buffer has hit the size limit. + In the latter case return what we have read so far and signal + string truncation. */ - if (!(length=fill_buffer(buffer)) || length == (uint) -1) + if (!(length= fill_buffer(buffer))) { if (buffer->eof) DBUG_RETURN(0); } + else if (length == (size_t) -1) + DBUG_RETURN(NULL); else continue; pos--; /* break line here */ - *truncated= 1; + buffer->truncated= 1; } else - *truncated= 0; + buffer->truncated= 0; buffer->end_of_line=pos+1; *out_length=(ulong) (pos + 1 - buffer->eof - buffer->start_of_line); DBUG_RETURN(buffer->start_of_line); diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index 2dcc77a16c2..aa774036d10 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -412,6 +412,12 @@ drop table t1; --echo --exec $MYSQL --skip-column-names --vertical test -e "select 1 as a" +# +# Bug#57450: mysql client enter in an infinite loop if the standard input is a directory +# +--error 1 +--exec $MYSQL < . + --echo --echo # From e29b40f86fcd75c72eb622f3219fbe386d81b8aa Mon Sep 17 00:00:00 2001 From: Vinay Fisrekar Date: Mon, 7 Feb 2011 11:10:35 +0530 Subject: [PATCH 22/27] Bug#59955 - engines/funcs/ps_string_not_null test needs better cleanup Correcting clean up command at the start of test. --- .../engines/funcs/r/ps_string_not_null.result | Bin 11681 -> 11681 bytes .../engines/funcs/t/ps_string_not_null.test | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/suite/engines/funcs/r/ps_string_not_null.result b/mysql-test/suite/engines/funcs/r/ps_string_not_null.result index 859fab8b49074619ac3d54a1bf72e5754c120deb..5f2a630811ccb413f7685d39eaa7a6d43c8408b7 100644 GIT binary patch delta 14 VcmZ1&y)b%$*koo2v5h*Vx&SF)1mXYy delta 12 TcmZ1&y)b%$7^Bff@h)8eA7TVi diff --git a/mysql-test/suite/engines/funcs/t/ps_string_not_null.test b/mysql-test/suite/engines/funcs/t/ps_string_not_null.test index f9e937cb24d..662adfd7a88 100644 --- a/mysql-test/suite/engines/funcs/t/ps_string_not_null.test +++ b/mysql-test/suite/engines/funcs/t/ps_string_not_null.test @@ -1,5 +1,5 @@ --disable_warnings -DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t1; --enable_warnings CREATE TABLE t1(c1 CHAR(100) NOT NULL); PREPARE stmt1 FROM 'INSERT INTO t1 (c1) VALUES(?)'; From 165ed4f1a853568d7c4605434bae762a53dffdb3 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 7 Feb 2011 11:13:54 +0200 Subject: [PATCH 23/27] Use fun:* instead of obj:*/libz.so* because when the bundled zlib is used (--with-zlib-dir=bundled) then there is no libz.so involved. --- mysql-test/valgrind.supp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 367dc62898a..6078006abd7 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -748,8 +748,8 @@ { Bug 59874 Valgrind warning in InnoDB compression code Memcheck:Cond - obj:*/libz.so* - obj:*/libz.so* + fun:* + fun:* fun:deflate fun:btr_store_big_rec_extern_fields_func fun:row_ins_index_entry_low @@ -763,8 +763,8 @@ { In page0zip.c we have already checked that the memory is initialized before calling deflate() Memcheck:Cond - obj:*/libz.so* - obj:*/libz.so* + fun:* + fun:* fun:deflate fun:page_zip_compress fun:page_cur_insert_rec_zip_reorg From 221ce9223da6c386c4b904cd7b79ef02e7845882 Mon Sep 17 00:00:00 2001 From: Ole John Aske Date: Mon, 7 Feb 2011 10:36:21 +0100 Subject: [PATCH 24/27] Fix for bug#59308: Incorrect result for SELECT DISTINCT ... ORDER BY DESC. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fix bug#59110: Memory leak of QUICK_SELECT_I allocated memory. Includes Jørgen Lølands review comments. Root cause of these bugs are that test_if_skip_sort_order() decided to revert the 'skip_sort_order' descision (and use filesort) after the query plan has been updated to reflect a 'skip' of the sort order. This might happen in 'check_reverse_order:' if we have a select->quick which could not be made descending by appending a QUICK_SELECT_DESC. (). The original 'save_quick' was then restored after the QEP has been modified, which caused: - An incorrect 'precomputed_group_by= TRUE' may have been set, and not reverted, as part of the already modifified QEP (Bug#59308) - A 'select->quick' might have been created which we fail to delete (bug#59110). This fix is a refactorication of test_if_skip_sort_order() where all logic related to modification of QEP (controlled by argument 'bool no_changes'), is moved to the end of test_if_skip_sort_order(), and done after *all* 'test_if_skip' checks has been performed - including the 'check_reverse_order:' checks. The refactorication above contains now intentional changes to the logic which has been moved to the end of the function. Furthermore, a smaller part of the fix address the handling of the select->quick objects which may already exists when we call 'test_if_skip_sort_order()' (save_quick) -and new select->quick's created during test_if_skip_sort_order(): - Before new select->quick may be created by calling ::test_quick_select(), we set 'select->quick= 0' to avoid that ::test_quick_select() prematurely delete the save_quick's. (After this call we may have both a 'save_quick' and 'select->quick') - All returns from ::test_if_skip_sort_order() where we may have both a 'save_quick' and a 'select->quick' has been changed to goto's to the exit points 'skiped_sort_order:' or 'need_filesort:' where we decide which of the QUICK_SELECT's to keep, and delete the other. --- mysql-test/r/order_by.result | 25 ++++ mysql-test/t/order_by.test | 17 +++ sql/sql_select.cc | 231 +++++++++++++++++++++-------------- 3 files changed, 181 insertions(+), 92 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index ba639fa9763..30879af418a 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -1638,4 +1638,29 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index NULL a 8 NULL 10 Using index; Using temporary; Using filesort 1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using where DROP TABLE t1, t2; +# +# Bug #59110: Memory leak of QUICK_SELECT_I allocated memory +# and +# Bug #59308: Incorrect result for +SELECT DISTINCT ... ORDER BY DESC + +# Use Valgrind to detect #59110! +# +CREATE TABLE t1 (a INT,KEY (a)); +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index a a 5 NULL 10 Using where; Using index; Using filesort +SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +a 1 +10 1 +9 1 +8 1 +7 1 +6 1 +5 1 +4 1 +3 1 +2 1 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index a8a6ad00648..1064320b65c 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -1492,4 +1492,21 @@ LIMIT 2; DROP TABLE t1, t2; +--echo # +--echo # Bug #59110: Memory leak of QUICK_SELECT_I allocated memory +--echo # and +--echo # Bug #59308: Incorrect result for +--echo SELECT DISTINCT ... ORDER BY DESC +--echo +--echo # Use Valgrind to detect #59110! +--echo # + +CREATE TABLE t1 (a INT,KEY (a)); +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); + +EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; + +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e080dc9370c..ba3008e0c38 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13364,12 +13364,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, { int ref_key; uint ref_key_parts; - int order_direction; + int order_direction= 0; uint used_key_parts; TABLE *table=tab->table; SQL_SELECT *select=tab->select; key_map usable_keys; QUICK_SELECT_I *save_quick= 0; + int best_key= -1; + DBUG_ENTER("test_if_skip_sort_order"); LINT_INIT(ref_key_parts); @@ -13473,13 +13475,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, new_ref_key_map.clear_all(); // Force the creation of quick select new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key. + select->quick= 0; if (select->test_quick_select(tab->join->thd, new_ref_key_map, 0, (tab->join->select_options & OPTION_FOUND_ROWS) ? HA_POS_ERROR : tab->join->unit->select_limit_cnt,0) <= 0) - DBUG_RETURN(0); + goto use_filesort; } ref_key= new_ref_key; } @@ -13504,7 +13507,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, int best_key_direction= 0; ha_rows best_records= 0; double read_time; - int best_key= -1; bool is_best_covering= FALSE; double fanout= 1; JOIN *join= tab->join; @@ -13681,72 +13683,21 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, tab->join->tables > tab->join->const_tables + 1) && ((unsigned) best_key != table->s->primary_key || !table->file->primary_key_is_clustered())) - DBUG_RETURN(0); + goto use_filesort; if (best_key >= 0) { - bool quick_created= FALSE; if (table->quick_keys.is_set(best_key) && best_key != ref_key) { key_map map; map.clear_all(); // Force the creation of quick select map.set_bit(best_key); // only best_key. - quick_created= - select->test_quick_select(join->thd, map, 0, - join->select_options & OPTION_FOUND_ROWS ? - HA_POS_ERROR : - join->unit->select_limit_cnt, - 0) > 0; - } - if (!no_changes) - { - /* - If ref_key used index tree reading only ('Using index' in EXPLAIN), - and best_key doesn't, then revert the decision. - */ - if (!table->covering_keys.is_set(best_key)) - table->set_keyread(FALSE); - if (!quick_created) - { - tab->index= best_key; - tab->read_first_record= best_key_direction > 0 ? - join_read_first:join_read_last; - tab->type=JT_NEXT; // Read with index_first(), index_next() - if (select && select->quick) - { - delete select->quick; - select->quick= 0; - } - if (table->covering_keys.is_set(best_key)) - table->set_keyread(TRUE); - table->file->ha_index_or_rnd_end(); - if (join->select_options & SELECT_DESCRIBE) - { - tab->ref.key= -1; - tab->ref.key_parts= 0; - if (select_limit < table_records) - tab->limit= select_limit; - } - } - else if (tab->type != JT_ALL) - { - /* - We're about to use a quick access to the table. - We need to change the access method so as the quick access - method is actually used. - */ - DBUG_ASSERT(tab->select->quick); - tab->type=JT_ALL; - tab->use_quick=1; - tab->ref.key= -1; - tab->ref.key_parts=0; // Don't use ref key. - tab->read_first_record= join_init_read_record; - if (tab->is_using_loose_index_scan()) - join->tmp_table_param.precomputed_group_by= TRUE; - /* - TODO: update the number of records in join->best_positions[tablenr] - */ - } + select->quick= 0; + select->test_quick_select(join->thd, map, 0, + join->select_options & OPTION_FOUND_ROWS ? + HA_POS_ERROR : + join->unit->select_limit_cnt, + 0); } order_direction= best_key_direction; /* @@ -13759,10 +13710,12 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, saved_best_key_parts : best_key_parts; } else - DBUG_RETURN(0); + goto use_filesort; } check_reverse_order: + DBUG_ASSERT(order_direction != 0); + if (order_direction == -1) // If ORDER BY ... DESC { if (select && select->quick) @@ -13771,9 +13724,10 @@ check_reverse_order: Don't reverse the sort order, if it's already done. (In some cases test_if_order_by_key() can be called multiple times */ - if (!select->quick->reverse_sorted()) + if (select->quick->reverse_sorted()) + goto skipped_filesort; + else { - QUICK_SELECT_DESC *tmp; int quick_type= select->quick->get_type(); if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || @@ -13781,39 +13735,132 @@ check_reverse_order: quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) { tab->limit= 0; - select->quick= save_quick; - DBUG_RETURN(0); // Use filesort + goto use_filesort; // Use filesort } - - /* ORDER BY range_key DESC */ - tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), - used_key_parts); - if (!tmp || tmp->error) - { - delete tmp; - select->quick= save_quick; - tab->limit= 0; - DBUG_RETURN(0); // Reverse sort not supported - } - select->quick=tmp; } } - else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL && - tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts) - { - /* - SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC - - Use a traversal function that starts by reading the last row - with key part (A) and then traverse the index backwards. - */ - tab->read_first_record= join_read_last_key; - tab->read_record.read_record= join_read_prev_same; - } } - else if (select && select->quick) - select->quick->sorted= 1; + + /* + Update query plan with access pattern for doing + ordered access according to what we have decided + above. + */ + if (!no_changes) // We are allowed to update QEP + { + if (best_key >= 0) + { + bool quick_created= + (select && select->quick && select->quick!=save_quick); + + /* + If ref_key used index tree reading only ('Using index' in EXPLAIN), + and best_key doesn't, then revert the decision. + */ + if (!table->covering_keys.is_set(best_key)) + table->set_keyread(FALSE); + if (!quick_created) + { + if (select) // Throw any existing quick select + select->quick= 0; // Cleanup either reset to save_quick, + // or 'delete save_quick' + tab->index= best_key; + tab->read_first_record= order_direction > 0 ? + join_read_first:join_read_last; + tab->type=JT_NEXT; // Read with index_first(), index_next() + + if (table->covering_keys.is_set(best_key)) + table->set_keyread(TRUE); + table->file->ha_index_or_rnd_end(); + if (tab->join->select_options & SELECT_DESCRIBE) + { + tab->ref.key= -1; + tab->ref.key_parts= 0; + if (select_limit < table->file->stats.records) + tab->limit= select_limit; + } + } + else if (tab->type != JT_ALL) + { + /* + We're about to use a quick access to the table. + We need to change the access method so as the quick access + method is actually used. + */ + DBUG_ASSERT(tab->select->quick); + tab->type=JT_ALL; + tab->use_quick=1; + tab->ref.key= -1; + tab->ref.key_parts=0; // Don't use ref key. + tab->read_first_record= join_init_read_record; + if (tab->is_using_loose_index_scan()) + tab->join->tmp_table_param.precomputed_group_by= TRUE; + /* + TODO: update the number of records in join->best_positions[tablenr] + */ + } + } // best_key >= 0 + + if (order_direction == -1) // If ORDER BY ... DESC + { + if (select && select->quick) + { + QUICK_SELECT_DESC *tmp; + /* ORDER BY range_key DESC */ + tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), + used_key_parts); + if (tmp && select->quick == save_quick) + save_quick= 0; // ::QUICK_SELECT_DESC consumed it + + if (!tmp || tmp->error) + { + delete tmp; + tab->limit= 0; + goto use_filesort; // Reverse sort failed -> filesort + } + select->quick= tmp; + } + else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL && + tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts) + { + /* + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC + + Use a traversal function that starts by reading the last row + with key part (A) and then traverse the index backwards. + */ + tab->read_first_record= join_read_last_key; + tab->read_record.read_record= join_read_prev_same; + } + } + else if (select && select->quick) + select->quick->sorted= 1; + + } // QEP has been modified + + /* + Cleanup: + We may have both a 'select->quick' and 'save_quick' (original) + at this point. Delete the one that we wan't use. + */ + +skipped_filesort: + // Keep current (ordered) select->quick + if (select && save_quick != select->quick) + { + delete save_quick; + save_quick= NULL; + } DBUG_RETURN(1); + +use_filesort: + // Restore original save_quick + if (select && select->quick != save_quick) + { + delete select->quick; + select->quick= save_quick; + } + DBUG_RETURN(0); } From fe40394908ff018b4f6d0c2542152a90db875dfe Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 7 Feb 2011 16:45:37 +0200 Subject: [PATCH 25/27] Backport the fix for Bug#59875 Valgrind warning in buf0buddy.c from 5.5 This warning also happens in 5.1 with a slightly different codepath. --- mysql-test/valgrind.supp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 6078006abd7..1983a8e6137 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -778,3 +778,16 @@ fun:row_ins_step fun:row_insert_for_mysql } + +{ + Bug 59875 Valgrind warning in buf0buddy.c + Memcheck:Addr1 + fun:mach_read_from_4 + fun:buf_buddy_relocate + fun:buf_buddy_free_low + fun:buf_buddy_free + fun:buf_LRU_block_remove_hashed_page + fun:buf_LRU_invalidate_tablespace + fun:fil_delete_tablespace + fun:row_drop_table_for_mysql +} From 12c68dbf164aa5b666acb09f02a14b51c941ad94 Mon Sep 17 00:00:00 2001 From: Anitha Gopi Date: Tue, 8 Feb 2011 11:30:35 +0530 Subject: [PATCH 26/27] Removed the collections for mysql-5.1-bugteam. Removed 1st from weekly. This is part of default suites --- mysql-test/collections/default.weekly | 1 - mysql-test/collections/mysql-5.1-bugteam.daily | 5 ----- mysql-test/collections/mysql-5.1-bugteam.push | 4 ---- 3 files changed, 10 deletions(-) delete mode 100644 mysql-test/collections/mysql-5.1-bugteam.daily delete mode 100644 mysql-test/collections/mysql-5.1-bugteam.push diff --git a/mysql-test/collections/default.weekly b/mysql-test/collections/default.weekly index f10bc0776a0..a2765bcaf0c 100755 --- a/mysql-test/collections/default.weekly +++ b/mysql-test/collections/default.weekly @@ -1,4 +1,3 @@ -perl mysql-test-run.pl --timer --force --comment=1st --experimental=collections/default.experimental 1st perl mysql-test-run.pl --timer --force --comment=big-tests --experimental=collections/default.experimental --vardir=var-big-tests --big-test --testcase-timeout=60 --suite-timeout=600 parts.partition_alter1_2_ndb parts.part_supported_sql_func_innodb parts.partition_alter1_2_innodb parts.partition_alter4_innodb parts.partition_alter1_1_2_ndb parts.partition_alter1_1_2_innodb parts.partition_alter1_1_ndb rpl_ndb.rpl_truncate_7ndb_2 main.archive-big main.sum_distinct-big main.mysqlbinlog_row_big main.alter_table-big main.variables-big main.type_newdecimal-big main.read_many_rows_innodb main.log_tables-big main.count_distinct3 main.events_time_zone main.merge-big main.create-big main.events_stress main.ssl-big funcs_1.myisam_views-big perl mysql-test-run.pl --timer --force --parallel=auto --comment=eits-tests-myisam-engine --experimental=collections/default.experimental --vardir=var-stmt-eits-tests-myisam-engine --suite=engines/iuds,engines/funcs --suite-timeout=500 --max-test-fail=0 --retry-failure=0 --mysqld=--default-storage-engine=myisam perl mysql-test-run.pl --timer --force --parallel=auto --comment=eits-rpl-binlog-row-tests-myisam-engine --experimental=collections/default.experimental --vardir=var-binlog-row-eits-tests-myisam-engine --suite=engines/iuds,engines/funcs --suite-timeout=500 --max-test-fail=0 --retry-failure=0 --mysqld=--default-storage-engine=myisam --do-test=rpl --mysqld=--binlog-format=row diff --git a/mysql-test/collections/mysql-5.1-bugteam.daily b/mysql-test/collections/mysql-5.1-bugteam.daily deleted file mode 100644 index 0503bd49f73..00000000000 --- a/mysql-test/collections/mysql-5.1-bugteam.daily +++ /dev/null @@ -1,5 +0,0 @@ -perl mysql-test-run.pl --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=embedded --vardir=var-emebbed --embedded --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=rpl_binlog_row --vardir=var-rpl_binlog_row --suite=rpl,binlog --mysqld=--binlog-format=row --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 --experimental=collections/default.experimental diff --git a/mysql-test/collections/mysql-5.1-bugteam.push b/mysql-test/collections/mysql-5.1-bugteam.push deleted file mode 100644 index d01b98eb87f..00000000000 --- a/mysql-test/collections/mysql-5.1-bugteam.push +++ /dev/null @@ -1,4 +0,0 @@ -perl mysql-test-run.pl --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental --skip-ndb -perl mysql-test-run.pl --timer --force --parallel=auto --comment=ps_row --vardir=var-ps_row --suite=main --ps-protocol --mysqld=--binlog-format=row --experimental=collections/default.experimental --skip-ndb -perl mysql-test-run.pl --timer --force --parallel=auto --comment=embedded --vardir=var-emebbed --suite=main --embedded --experimental=collections/default.experimental --skip-ndb -perl mysql-test-run.pl --timer --force --parallel=auto --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 --experimental=collections/default.experimental --skip-ndb From 994132add6118b8d91893d495a8cb628c1017da4 Mon Sep 17 00:00:00 2001 From: "John H. Embretsen" Date: Tue, 8 Feb 2011 10:51:09 +0100 Subject: [PATCH 27/27] Fix for Bug#59837 - mysqlhotcopy fails with wrong error (11) on specific platforms: Test failed on a certain Linux platform in automated environment. It turns out that this platform has an old version of Perl modules DBI and DBD::mysql installed, as well as the OS itself being relatively old. Allowing error code 11 to be returned from mysqlhotcopy on expected error seems harmless and will make the test pass also with older libraries. --- mysql-test/include/mysqlhotcopy.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/include/mysqlhotcopy.inc b/mysql-test/include/mysqlhotcopy.inc index 110b0769ae1..fcf57a68644 100644 --- a/mysql-test/include/mysqlhotcopy.inc +++ b/mysql-test/include/mysqlhotcopy.inc @@ -107,7 +107,7 @@ DROP DATABASE hotcopy_save; --replace_result $MYSQLD_DATADIR MYSQLD_DATADIR --list_files $MYSQLD_DATADIR/hotcopy_save --replace_result $MASTER_MYSOCK MASTER_MYSOCK ---error 9,2304 +--error 9,11,2304 --exec $MYSQLHOTCOPY --quiet -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save --replace_result $MASTER_MYSOCK MASTER_MYSOCK --exec $MYSQLHOTCOPY --quiet --allowold -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save