From 32d280ba3202c416ecf3ad15f5d2a6316c7dd128 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 24 Apr 2007 18:42:46 +0500 Subject: [PATCH 01/12] bug #27405 (Partitioning InnoDB auto_increment bug) InnoDB engine changes internal auto_increment counter only after ha_innodb::write_row, so two threads can't simultaneously operate between ha_innodb::update_autoincrement and ha_innodb::write_row. So concurrent execution of ha_partition::write_row prevented sql/ha_partition.cc: use table_share->mutex to avoid concurrent write_row for AUTO_INCREMENTed table --- sql/ha_partition.cc | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 3c25dcd202f..5247e665b0f 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2647,6 +2647,7 @@ int ha_partition::write_row(byte * buf) uint32 part_id; int error; longlong func_value; + bool autoincrement_lock= false; #ifdef NOT_NEEDED byte *rec0= m_rec0; #endif @@ -2662,7 +2663,21 @@ int ha_partition::write_row(byte * buf) or a new row, then update the auto_increment value in the record. */ if (table->next_number_field && buf == table->record[0]) + { + /* + Some engines (InnoDB for example) can change autoincrement + counter only after 'table->write_row' operation. + So if another thread gets inside the ha_partition::write_row + before it is complete, it gets same auto_increment value, + which means DUP_KEY error (bug #27405) + Here we separate the access using table_share->mutex, and + use autoincrement_lock variable to avoid unnecessary locks. + Probably not an ideal solution. + */ + autoincrement_lock= true; + pthread_mutex_lock(&table_share->mutex); update_auto_increment(); + } my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); #ifdef NOT_NEEDED @@ -2683,11 +2698,15 @@ int ha_partition::write_row(byte * buf) if (unlikely(error)) { m_part_info->err_value= func_value; - DBUG_RETURN(error); + goto exit; } m_last_part= part_id; DBUG_PRINT("info", ("Insert in partition %d", part_id)); - DBUG_RETURN(m_file[part_id]->write_row(buf)); + error= m_file[part_id]->write_row(buf); +exit: + if (autoincrement_lock) + pthread_mutex_unlock(&table_share->mutex); + DBUG_RETURN(error); } From e34aa82da9eca8b219a88bc255938f2c7094567a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Aug 2007 17:28:51 +0400 Subject: [PATCH 02/12] Bug#29948: Unchecked NULL pointer caused server crash. The cli_read_binary_rows function is used to fetch data from the server after a prepared statement execution. It accepts a statement handler and gets the connection handler from it. But when the auto-reconnect option is set the connection handler is reset to NULL after reconnection because the prepared statement is lost and the handler became useless. This case wasn't checked in the cli_read_binary_rows function and caused server crash. Now the cli_read_binary_rows function checks the connection handler to be not NULL and returns an error if it is. tests/mysql_client_test.c: Added a test case for the bug#29948: Unchecked NULL pointer caused server crash. libmysql/libmysql.c: Bug#29948: Unchecked NULL pointer caused server crash. Now the cli_read_binary_rows function checks the connection handler to be not NULL and returns an error if it is. --- libmysql/libmysql.c | 10 +++++- tests/mysql_client_test.c | 75 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 1a0aae414ed..85c56a7ea40 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -4679,9 +4679,17 @@ int cli_read_binary_rows(MYSQL_STMT *stmt) MYSQL *mysql= stmt->mysql; MYSQL_DATA *result= &stmt->result; MYSQL_ROWS *cur, **prev_ptr= &result->data; - NET *net = &mysql->net; + NET *net; + + if (!mysql) + { + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + return 1; + } + DBUG_ENTER("cli_read_binary_rows"); + net = &mysql->net; mysql= mysql->last_used_con; while ((pkt_len= cli_safe_read(mysql)) != packet_error) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 75c86902972..ca76270d8b8 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15735,6 +15735,80 @@ static void test_bug27592() } +static void test_bug29948() +{ + MYSQL *dbc=NULL; + MYSQL_STMT *stmt=NULL; + MYSQL_BIND bind; + + int res=0; + my_bool auto_reconnect=1, error=0, is_null=0; + char kill_buf[20]; + const char *query; + int buf; + unsigned long length; + + dbc = mysql_init(NULL); + DIE_UNLESS(dbc); + + mysql_options(dbc, MYSQL_OPT_RECONNECT, (char*)&auto_reconnect); + if (!mysql_real_connect(dbc, opt_host, opt_user, + opt_password, current_db, opt_port, + opt_unix_socket, + (CLIENT_FOUND_ROWS | CLIENT_MULTI_STATEMENTS | + CLIENT_MULTI_RESULTS))) + { + printf("connection failed: %s (%d)", mysql_error(dbc), + mysql_errno(dbc)); + exit(1); + } + + bind.buffer_type= MYSQL_TYPE_LONG; + bind.buffer= (char *)&buf; + bind.is_null= &is_null; + bind.error= &error; + bind.length= &length; + + res= mysql_query(dbc, "DROP TABLE IF EXISTS t1"); + myquery(res); + res= mysql_query(dbc, "CREATE TABLE t1 (a INT)"); + myquery(res); + res= mysql_query(dbc, "INSERT INTO t1 VALUES(1)"); + myquery(res); + + stmt= mysql_stmt_init(dbc); + check_stmt(stmt); + + buf= CURSOR_TYPE_READ_ONLY; + res= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *)&buf); + myquery(res); + + query= "SELECT * from t1 where a=?"; + res= mysql_stmt_prepare(stmt, query, strlen(query)); + myquery(res); + + res= mysql_stmt_bind_param(stmt, &bind); + myquery(res); + + res= mysql_stmt_execute(stmt); + check_execute(stmt, res); + + res= mysql_stmt_bind_result(stmt,&bind); + check_execute(stmt, res); + + sprintf(kill_buf, "kill %ld", dbc->thread_id); + mysql_query(dbc, kill_buf); + + res= mysql_stmt_store_result(stmt); + DIE_UNLESS(res); + + mysql_stmt_free_result(stmt); + mysql_stmt_close(stmt); + mysql_query(dbc, "DROP TABLE t1"); + mysql_close(dbc); +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -16019,6 +16093,7 @@ static struct my_tests_st my_tests[]= { { "test_bug28505", test_bug28505 }, { "test_bug28934", test_bug28934 }, { "test_bug27592", test_bug27592 }, + { "test_bug29948", test_bug29948 }, { 0, 0 } }; From 01f8130aa68392d14f36cc17c01e1b349bf844f5 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Aug 2007 09:23:44 +0200 Subject: [PATCH 03/12] bug#28570: handler::index_read() is called with different find_flag when ORDER BY is used The range analysis module did not correctly signal to the handler that a range represents a ref (EQ_RANGE flag). This causes non-range queries like SELECT ... FROM ... WHERE keypart_1=const, ..., keypart_n=const ORDER BY ... FOR UPDATE to wait for a lock unneccesarily if another running transaction uses SELECT ... FOR UPDATE on the same table. Fixed by setting EQ_RANGE for all range accesses that represent an equality predicate. mysql-test/r/innodb_mysql.result: bug#28570: Test Result mysql-test/t/innodb_mysql.test: bug#28570: Test Case sql/handler.cc: bug#28570: Updated comment sql/opt_range.cc: bug#28570: Removed the criterion that key has to be unique (HA_NOSAME) in order for the EQ_RANGE flag to be set. It is sufficient that the range represent a ref access. --- mysql-test/r/innodb_mysql.result | 18 ++++++++++++++++++ mysql-test/t/innodb_mysql.test | 29 +++++++++++++++++++++++++++++ sql/handler.cc | 3 ++- sql/opt_range.cc | 3 +-- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 5b3ca972467..0bb93318557 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1007,4 +1007,22 @@ CALL p1(); CALL p1(); DROP PROCEDURE p1; DROP TABLE t1; +CREATE TABLE t1 ( +a INT, +b INT, +KEY (b) +) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,10), (2,10), (2,20), (3,30); +START TRANSACTION; +SELECT * FROM t1 WHERE b=20 FOR UPDATE; +a b +2 20 +START TRANSACTION; +SELECT * FROM t1 WHERE b=10 ORDER BY A FOR UPDATE; +a b +1 10 +2 10 +ROLLBACK; +ROLLBACK; +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 63431e10bbf..e27db9944fe 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -840,5 +840,34 @@ DISCONNECT con2; DROP PROCEDURE p1; DROP TABLE t1; +# +# Bug #28570: handler::index_read() is called with different find_flag when +# ORDER BY is used +# + +CREATE TABLE t1 ( + a INT, + b INT, + KEY (b) +) ENGINE=InnoDB; + +INSERT INTO t1 VALUES (1,10), (2,10), (2,20), (3,30); + +START TRANSACTION; +SELECT * FROM t1 WHERE b=20 FOR UPDATE; + +--connect (conn2, localhost, root,,test) + +# This statement gives a "failed: 1205: Lock wait timeout exceeded; try +# restarting transaction" message when the bug is present. +START TRANSACTION; +SELECT * FROM t1 WHERE b=10 ORDER BY A FOR UPDATE; +ROLLBACK; + +--disconnect conn2 +--connection default + +ROLLBACK; +DROP TABLE t1; --echo End of 5.0 tests diff --git a/sql/handler.cc b/sql/handler.cc index 8ef14ce8906..bfb7e8c369f 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2610,7 +2610,8 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) read_range_first() start_key Start key. Is 0 if no min range end_key End key. Is 0 if no max range - eq_range_arg Set to 1 if start_key == end_key + eq_range_arg Set to 1 if start_key == end_key and the range endpoints + will not change during query execution. sorted Set to 1 if result should be sorted per key NOTES diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 247f0eada49..39f0321955d 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -6369,8 +6369,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, range->min_key=range->max_key=(char*) ref->key_buff; range->min_length=range->max_length=ref->key_length; range->flag= ((ref->key_length == key_info->key_length && - (key_info->flags & (HA_NOSAME | HA_END_SPACE_KEY)) == - HA_NOSAME) ? EQ_RANGE : 0); + (key_info->flags & HA_END_SPACE_KEY) == 0) ? EQ_RANGE : 0); if (!(quick->key_parts=key_part=(KEY_PART *) alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts))) From bbf2211bdcd43581b9ddf54f1bfc731384e289c7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Aug 2007 18:21:58 +0400 Subject: [PATCH 04/12] mysql_client_test.c: Post fix for the bug#29948. tests/mysql_client_test.c: Post fix for the bug#29948. --- tests/mysql_client_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index dd6b0b5585f..c335b0128f7 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15746,7 +15746,7 @@ static void test_bug29948() char kill_buf[20]; const char *query; int buf; - unsigned long length; + unsigned long length, cursor_type; dbc = mysql_init(NULL); DIE_UNLESS(dbc); @@ -15779,8 +15779,8 @@ static void test_bug29948() stmt= mysql_stmt_init(dbc); check_stmt(stmt); - buf= CURSOR_TYPE_READ_ONLY; - res= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *)&buf); + cursor_type= CURSOR_TYPE_READ_ONLY; + res= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *)&cursor_type); myquery(res); query= "SELECT * from t1 where a=?"; From a8f8e5483e7925ce804c3900752f88fdf43cb3ee Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Aug 2007 10:24:18 -0700 Subject: [PATCH 05/12] Fixed bug #30396. The bug caused memory corruption for some queries with top OR level in the WHERE condition if they contained equality predicates and other sargable predicates in disjunctive parts of the condition. The corruption happened because the upper bound of the memory allocated for KEY_FIELD and SARGABLE_PARAM internal structures containing info about potential lookup keys was calculated incorrectly in some cases. In particular it was calculated incorrectly when the WHERE condition was an OR formula with disjuncts being AND formulas including equalities and other sargable predicates. mysql-test/r/select.result: Added a test case for bug #30396. mysql-test/t/select.test: Added a test case for bug #30396. sql/item_cmpfunc.h: Removed max_members from the COND_EQUAL class as not useful anymore. sql/sql_base.cc: Added the max_equal_elems field to the st_select_lex structure. sql/sql_lex.cc: Added the max_equal_elems field to the st_select_lex structure. sql/sql_lex.h: Added the max_equal_elems field to the st_select_lex structure. The field contains the maximal number of elements in multiple equalities built for the query conditions. sql/sql_select.cc: Fixed bug #30396. The bug caused memory corruption for some queries with top OR level in the WHERE condition if they contained equality predicates and other sargable predicates in disjunctive parts of the condition. The corruption happened because the upper bound of the memory allocated for KEY_FIELD and SARGABLE_PARAM internal structures containing info about potential lookup keys was calculated incorrectly in some cases. In particular it was calculated incorrectly when the WHERE condition was an OR formula with disjuncts being AND formulas including equalities and other sargable predicates. The max_equal_elems field to the st_select_lex structure is used now to calculate the above mentioned upper bound. The field contains the maximal number of elements in multiple equalities built for the query conditions. --- mysql-test/r/select.result | 57 ++++++++++++++++++++++++++++++++++++ mysql-test/t/select.test | 60 ++++++++++++++++++++++++++++++++++++++ sql/item_cmpfunc.h | 1 - sql/sql_base.cc | 1 + sql/sql_lex.cc | 1 + sql/sql_lex.h | 3 +- sql/sql_select.cc | 38 ++++++++---------------- 7 files changed, 133 insertions(+), 28 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 5930c36029f..2cf1316bb47 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -4005,4 +4005,61 @@ id select_type table type possible_keys key key_len ref rows Extra EXPLAIN SELECT c1 FROM t1 WHERE (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT (SELECT COUNT(c2))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) > 0; ERROR HY000: Too high level of nesting for select DROP TABLE t1; +CREATE TABLE t1 ( +c1 int(11) NOT NULL AUTO_INCREMENT, +c2 varchar(1000) DEFAULT NULL, +c3 bigint(20) DEFAULT NULL, +c4 bigint(20) DEFAULT NULL, +PRIMARY KEY (c1) +); +EXPLAIN EXTENDED +SELECT join_2.c1 +FROM +t1 AS join_0, +t1 AS join_1, +t1 AS join_2, +t1 AS join_3, +t1 AS join_4, +t1 AS join_5, +t1 AS join_6, +t1 AS join_7 +WHERE +join_0.c1=join_1.c1 AND +join_1.c1=join_2.c1 AND +join_2.c1=join_3.c1 AND +join_3.c1=join_4.c1 AND +join_4.c1=join_5.c1 AND +join_5.c1=join_6.c1 AND +join_6.c1=join_7.c1 +OR +join_0.c2 < '?' AND +join_1.c2 < '?' AND +join_2.c2 > '?' AND +join_2.c2 < '!' AND +join_3.c2 > '?' AND +join_4.c2 = '?' AND +join_5.c2 <> '?' AND +join_6.c2 <> '?' AND +join_7.c2 >= '?' AND +join_0.c1=join_1.c1 AND +join_1.c1=join_2.c1 AND +join_2.c1=join_3.c1 AND +join_3.c1=join_4.c1 AND +join_4.c1=join_5.c1 AND +join_5.c1=join_6.c1 AND +join_6.c1=join_7.c1 +GROUP BY +join_3.c1, +join_2.c1, +join_7.c1, +join_1.c1, +join_0.c1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Note 1003 select '0' AS `c1` from `test`.`t1` `join_0` join `test`.`t1` `join_1` join `test`.`t1` `join_2` join `test`.`t1` `join_3` join `test`.`t1` `join_4` join `test`.`t1` `join_5` join `test`.`t1` `join_6` join `test`.`t1` `join_7` where 0 group by '0','0','0','0','0' +SHOW WARNINGS; +Level Code Message +Note 1003 select '0' AS `c1` from `test`.`t1` `join_0` join `test`.`t1` `join_1` join `test`.`t1` `join_2` join `test`.`t1` `join_3` join `test`.`t1` `join_4` join `test`.`t1` `join_5` join `test`.`t1` `join_6` join `test`.`t1` `join_7` where 0 group by '0','0','0','0','0' +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 8bfa12539fa..abf55745080 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -3400,4 +3400,64 @@ eval EXPLAIN SELECT c1 FROM t1 WHERE $q > 0; DROP TABLE t1; +# +# Bug #30396: crash for a join with equalities and sargable predicates +# in disjunctive parts of the WHERE condition +# + +CREATE TABLE t1 ( + c1 int(11) NOT NULL AUTO_INCREMENT, + c2 varchar(1000) DEFAULT NULL, + c3 bigint(20) DEFAULT NULL, + c4 bigint(20) DEFAULT NULL, + PRIMARY KEY (c1) +); + +EXPLAIN EXTENDED +SELECT join_2.c1 +FROM + t1 AS join_0, + t1 AS join_1, + t1 AS join_2, + t1 AS join_3, + t1 AS join_4, + t1 AS join_5, + t1 AS join_6, + t1 AS join_7 +WHERE + join_0.c1=join_1.c1 AND + join_1.c1=join_2.c1 AND + join_2.c1=join_3.c1 AND + join_3.c1=join_4.c1 AND + join_4.c1=join_5.c1 AND + join_5.c1=join_6.c1 AND + join_6.c1=join_7.c1 + OR + join_0.c2 < '?' AND + join_1.c2 < '?' AND + join_2.c2 > '?' AND + join_2.c2 < '!' AND + join_3.c2 > '?' AND + join_4.c2 = '?' AND + join_5.c2 <> '?' AND + join_6.c2 <> '?' AND + join_7.c2 >= '?' AND + join_0.c1=join_1.c1 AND + join_1.c1=join_2.c1 AND + join_2.c1=join_3.c1 AND + join_3.c1=join_4.c1 AND + join_4.c1=join_5.c1 AND + join_5.c1=join_6.c1 AND + join_6.c1=join_7.c1 +GROUP BY + join_3.c1, + join_2.c1, + join_7.c1, + join_1.c1, + join_0.c1; + +SHOW WARNINGS; + +DROP TABLE t1; + --echo End of 5.0 tests diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 9afc0507817..7b6eaef08ec 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1506,7 +1506,6 @@ public: the current and level */ COND_EQUAL() { - max_members= 0; upper_levels= 0; } }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1c01248c283..e8562cdffbd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5611,6 +5611,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, thd->set_query_id=1; select_lex->cond_count= 0; select_lex->between_count= 0; + select_lex->max_equal_elems= 0; for (table= tables; table; table= table->next_local) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7911da69862..d11c2b21635 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1183,6 +1183,7 @@ void st_select_lex::init_query() */ parent_lex->push_context(&context); cond_count= between_count= with_wild= 0; + max_equal_elems= 0; conds_processed_with_permanent_arena= 0; ref_pointer_array= 0; select_n_where_fields= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index b9c6abd2b06..94015a9fe07 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -533,7 +533,8 @@ public: */ uint select_n_having_items; uint cond_count; /* number of arguments of and/or/xor in where/having/on */ - uint between_count; /* number of between predicates in where/having/on */ + uint between_count; /* number of between predicates in where/having/on */ + uint max_equal_elems; /* maximal number of elements in multiple equalities */ /* Number of fields used in select list or where clause of current select and all inner subselects. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ac8dc84f118..b7846a7433d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3524,10 +3524,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, uint and_level,i,found_eq_constant; KEY_FIELD *key_fields, *end, *field; uint sz; - uint m= 1; - - if (cond_equal && cond_equal->max_members) - m= cond_equal->max_members; + uint m= max(select_lex->max_equal_elems,1); /* We use the same piece of memory to store both KEY_FIELD @@ -3547,7 +3544,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, it is considered as sargable only for its first argument. Multiple equality can add elements that are filled after substitution of field arguments by equal fields. There - can be not more than cond_equal->max_members such substitutions. + can be not more than select_lex->max_equal_elems such + substitutions. */ sz= max(sizeof(KEY_FIELD),sizeof(SARGABLE_PARAM))* (((thd->lex->current_select->cond_count+1)*2 + @@ -7189,8 +7187,7 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal, just an argument of a comparison predicate. The function also determines the maximum number of members in equality lists of each Item_cond_and object assigning it to - cond_equal->max_members of this object and updating accordingly - the upper levels COND_EQUAL structures. + thd->lex->current_select->max_equal_elems. NOTES Multiple equality predicate =(f1,..fn) is equivalent to the conjuction of @@ -7235,7 +7232,6 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, COND_EQUAL *inherited) { Item_equal *item_equal; - uint members; COND_EQUAL cond_equal; cond_equal.upper_levels= inherited; @@ -7273,19 +7269,8 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, { item_equal->fix_length_and_dec(); item_equal->update_used_tables(); - members= item_equal->members(); - if (cond_equal.max_members < members) - cond_equal.max_members= members; - } - members= cond_equal.max_members; - if (inherited && inherited->max_members < members) - { - do - { - inherited->max_members= members; - inherited= inherited->upper_levels; - } - while (inherited); + set_if_bigger(thd->lex->current_select->max_equal_elems, + item_equal->members()); } ((Item_cond_and*)cond)->cond_equal= cond_equal; @@ -7340,10 +7325,12 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, { item_equal->fix_length_and_dec(); item_equal->update_used_tables(); - return item_equal; } else - return eq_list.pop(); + item_equal= (Item_equal *) eq_list.pop(); + set_if_bigger(thd->lex->current_select->max_equal_elems, + item_equal->members()); + return item_equal; } else { @@ -7359,9 +7346,8 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, { item_equal->fix_length_and_dec(); item_equal->update_used_tables(); - members= item_equal->members(); - if (cond_equal.max_members < members) - cond_equal.max_members= members; + set_if_bigger(thd->lex->current_select->max_equal_elems, + item_equal->members()); } and_cond->cond_equal= cond_equal; args->concat((List *)&cond_equal.current_level); From 0d9046ecaf3107371e0b2a59792104385da74349 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 16 Aug 2007 19:27:44 -0700 Subject: [PATCH 06/12] Post-merge fix. --- mysql-test/r/select.result | 4 +-- tests/mysql_client_test.c | 73 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index f1bfbec1555..48e8bf933e1 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -4039,8 +4039,8 @@ join_2.c1, join_7.c1, join_1.c1, join_0.c1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: Note 1003 select '0' AS `c1` from `test`.`t1` `join_0` join `test`.`t1` `join_1` join `test`.`t1` `join_2` join `test`.`t1` `join_3` join `test`.`t1` `join_4` join `test`.`t1` `join_5` join `test`.`t1` `join_6` join `test`.`t1` `join_7` where 0 group by '0','0','0','0','0' SHOW WARNINGS; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 3656364820e..a6ad0d5bc80 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -16269,6 +16269,78 @@ static void test_bug27592() DBUG_VOID_RETURN; } +static void test_bug29948() +{ + MYSQL *dbc=NULL; + MYSQL_STMT *stmt=NULL; + MYSQL_BIND bind; + + int res=0; + my_bool auto_reconnect=1, error=0, is_null=0; + char kill_buf[20]; + const char *query; + int buf; + unsigned long length, cursor_type; + + dbc = mysql_init(NULL); + DIE_UNLESS(dbc); + + mysql_options(dbc, MYSQL_OPT_RECONNECT, (char*)&auto_reconnect); + if (!mysql_real_connect(dbc, opt_host, opt_user, + opt_password, current_db, opt_port, + opt_unix_socket, + (CLIENT_FOUND_ROWS | CLIENT_MULTI_STATEMENTS | + CLIENT_MULTI_RESULTS))) + { + printf("connection failed: %s (%d)", mysql_error(dbc), + mysql_errno(dbc)); + exit(1); + } + + bind.buffer_type= MYSQL_TYPE_LONG; + bind.buffer= (char *)&buf; + bind.is_null= &is_null; + bind.error= &error; + bind.length= &length; + + res= mysql_query(dbc, "DROP TABLE IF EXISTS t1"); + myquery(res); + res= mysql_query(dbc, "CREATE TABLE t1 (a INT)"); + myquery(res); + res= mysql_query(dbc, "INSERT INTO t1 VALUES(1)"); + myquery(res); + + stmt= mysql_stmt_init(dbc); + check_stmt(stmt); + + cursor_type= CURSOR_TYPE_READ_ONLY; + res= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *)&cursor_type); + myquery(res); + + query= "SELECT * from t1 where a=?"; + res= mysql_stmt_prepare(stmt, query, strlen(query)); + myquery(res); + + res= mysql_stmt_bind_param(stmt, &bind); + myquery(res); + + res= mysql_stmt_execute(stmt); + check_execute(stmt, res); + + res= mysql_stmt_bind_result(stmt,&bind); + check_execute(stmt, res); + + sprintf(kill_buf, "kill %ld", dbc->thread_id); + mysql_query(dbc, kill_buf); + + res= mysql_stmt_store_result(stmt); + DIE_UNLESS(res); + + mysql_stmt_free_result(stmt); + mysql_stmt_close(stmt); + mysql_query(dbc, "DROP TABLE t1"); + mysql_close(dbc); +} /* @@ -16670,6 +16742,7 @@ static struct my_tests_st my_tests[]= { { "test_bug28505", test_bug28505 }, { "test_bug28934", test_bug28934 }, { "test_bug27592", test_bug27592 }, + { "test_bug29948", test_bug29948 }, { "test_bug29687", test_bug29687 }, { "test_bug29692", test_bug29692 }, { "test_bug29306", test_bug29306 }, From 29ee5ed0a04a2db4e5e23a359a3acf9b4e474ab0 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 20 Aug 2007 11:23:08 +0500 Subject: [PATCH 07/12] Bug#27629 Possible security flaw in INFORMATION_SCHEMA and SHOW statements added SUPER_ACL check for I_S.TRIGGERS mysql-test/r/information_schema.result: result fix mysql-test/r/information_schema_db.result: result fix mysql-test/t/information_schema.test: test case sql/sql_show.cc: added SUPER_ACL check for I_S.TRIGGERS --- mysql-test/r/information_schema.result | 26 ++++++++++++++++++++++- mysql-test/r/information_schema_db.result | 2 -- mysql-test/t/information_schema.test | 26 +++++++++++++++++++++++ sql/sql_show.cc | 10 +++++++-- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 9d0e41b341a..612e744a0f4 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -180,7 +180,6 @@ t1 a select show columns from mysqltest.t1; Field Type Null Key Default Extra a int(11) YES NULL -b varchar(30) YES MUL NULL select table_name, column_name, privileges from information_schema.columns where table_schema = 'mysqltest' and table_name = 'v1'; table_name column_name privileges @@ -1330,4 +1329,29 @@ alter database; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 alter database test; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 +create database mysqltest; +create table mysqltest.t1(a int, b int, c int); +create trigger mysqltest.t1_ai after insert on mysqltest.t1 +for each row set @a = new.a + new.b + new.c; +grant select(b) on mysqltest.t1 to mysqltest_1@localhost; +select trigger_name from information_schema.triggers +where event_object_table='t1'; +trigger_name +t1_ai +show triggers from mysqltest; +Trigger Event Table Statement Timing Created sql_mode Definer +t1_ai INSERT t1 set @a = new.a + new.b + new.c AFTER NULL root@localhost +show columns from t1; +Field Type Null Key Default Extra +b int(11) YES NULL +select column_name from information_schema.columns where table_name='t1'; +column_name +b +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +select trigger_name from information_schema.triggers +where event_object_table='t1'; +trigger_name +drop user mysqltest_1@localhost; +drop database mysqltest; End of 5.0 tests. diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index 2d330dda333..dd1f0295277 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -140,13 +140,11 @@ create view v2 as select f1 from testdb_1.v1; create view v4 as select f1,f2 from testdb_1.v3; show fields from testdb_1.v5; Field Type Null Key Default Extra -f1 char(4) YES NULL show create view testdb_1.v5; View Create View v5 CREATE ALGORITHM=UNDEFINED DEFINER=`testdb_1`@`localhost` SQL SECURITY DEFINER VIEW `testdb_1`.`v5` AS select `testdb_1`.`t1`.`f1` AS `f1` from `testdb_1`.`t1` show fields from testdb_1.v6; Field Type Null Key Default Extra -f1 char(4) YES NULL show create view testdb_1.v6; View Create View v6 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `testdb_1`.`v6` AS select `testdb_1`.`t1`.`f1` AS `f1` from `testdb_1`.`t1` diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 6cf4ad8f576..7637a027e8f 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -1045,4 +1045,30 @@ drop table t1,t2; alter database; --error ER_PARSE_ERROR alter database test; + +# +# Bug#27629 Possible security flaw in INFORMATION_SCHEMA and SHOW statements +# + +create database mysqltest; +create table mysqltest.t1(a int, b int, c int); +create trigger mysqltest.t1_ai after insert on mysqltest.t1 + for each row set @a = new.a + new.b + new.c; +grant select(b) on mysqltest.t1 to mysqltest_1@localhost; + +select trigger_name from information_schema.triggers +where event_object_table='t1'; +show triggers from mysqltest; + +connect (con27629,localhost,mysqltest_1,,mysqltest); +show columns from t1; +select column_name from information_schema.columns where table_name='t1'; + +show triggers; +select trigger_name from information_schema.triggers +where event_object_table='t1'; +connection default; +drop user mysqltest_1@localhost; +drop database mysqltest; + --echo End of 5.0 tests. diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 05a847b3830..e21de81fbdb 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2684,8 +2684,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, col_access= get_column_grant(thd, &tables->grant, base_name, file_name, field->field_name) & COL_ACLS; - if (lex->orig_sql_command != SQLCOM_SHOW_FIELDS && - !tables->schema_table && !col_access) + if (!tables->schema_table && !col_access) continue; end= tmp; for (uint bitnr=0; col_access ; col_access>>=1,bitnr++) @@ -3381,6 +3380,12 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, { Table_triggers_list *triggers= tables->table->triggers; int event, timing; + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!(thd->security_ctx->master_access & SUPER_ACL)) + goto ret; +#endif + for (event= 0; event < (int)TRG_EVENT_MAX; event++) { for (timing= 0; timing < (int)TRG_ACTION_MAX; timing++) @@ -3407,6 +3412,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, } } } +ret: DBUG_RETURN(0); } From 7c3d5bc5c49e054bd48f632f95a228f426394977 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Aug 2007 01:39:39 +0500 Subject: [PATCH 08/12] Fixed bug #30287. The server created temporary tables for filesort in the working directory instead of the specified tmpdir directory. sql/item.cc: Fixed bug #30287. The Item_field::set_field method has been modified to reset the any_privileges flag to false in case of system temporary table. This modification prevents the server from unnecessary checking of user privileges to access system temporary tables. sql/sql_select.cc: Fixed bug #30287. Bugfix for #29015 has been removed: TABLE_SHARE::table_name of system temporary tables contains full path to table file basename again. sql/sql_view.cc: Fixed bug #30287. Commentary has been added. --- sql/item.cc | 2 ++ sql/sql_select.cc | 19 ++++++++----------- sql/sql_view.cc | 6 ++++++ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 8ce77e9cd4f..66379d5dcf9 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1765,6 +1765,8 @@ void Item_field::set_field(Field *field_par) unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); collation.set(field_par->charset(), field_par->derivation()); fixed= 1; + if (field->table->s->tmp_table == SYSTEM_TMP_TABLE) + any_privileges= 0; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ac8dc84f118..6b0f823f1ce 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9169,7 +9169,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, bool using_unique_constraint= 0; bool use_packed_rows= 0; bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS); - char *tmpname, *tmppath, path[FN_REFLEN], table_name[NAME_LEN+1]; + char *tmpname,path[FN_REFLEN]; byte *pos,*group_buff; uchar *null_flags; Field **reg_field, **from_field, **default_field; @@ -9192,12 +9192,12 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, temp_pool_slot = bitmap_set_next(&temp_pool); if (temp_pool_slot != MY_BIT_NONE) // we got a slot - sprintf(table_name, "%s_%lx_%i", tmp_file_prefix, + sprintf(path, "%s_%lx_%i", tmp_file_prefix, current_pid, temp_pool_slot); else { /* if we run out of slots or we are not using tempool */ - sprintf(table_name, "%s%lx_%lx_%x", tmp_file_prefix,current_pid, + sprintf(path,"%s%lx_%lx_%x", tmp_file_prefix,current_pid, thd->thread_id, thd->tmp_table++); } @@ -9205,8 +9205,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, No need to change table name to lower case as we are only creating MyISAM or HEAP tables here */ - fn_format(path, table_name, mysql_tmpdir, "", - MY_REPLACE_EXT|MY_UNPACK_FILENAME); + fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME); + if (group) { @@ -9251,8 +9251,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, sizeof(*key_part_info)*(param->group_parts+1), ¶m->start_recinfo, sizeof(*param->recinfo)*(field_count*2+4), - &tmppath, (uint) strlen(path)+1, - &tmpname, (uint) strlen(table_name)+1, + &tmpname, (uint) strlen(path)+1, &group_buff, group && ! using_unique_constraint ? param->group_length : 0, NullS)) @@ -9270,8 +9269,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, DBUG_RETURN(NULL); /* purecov: inspected */ } param->items_to_copy= copy_func; - strmov(tmppath, path); - strmov(tmpname, table_name); + strmov(tmpname,path); /* make table according to fields */ bzero((char*) table,sizeof(*table)); @@ -9297,8 +9295,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, table->s= &table->share_not_to_be_used; table->s->blob_field= blob_field; - table->s->table_name= tmpname; - table->s->path= tmppath; + table->s->table_name= table->s->path= tmpname; table->s->db= ""; table->s->blob_ptr_size= mi_portable_sizeof_char_ptr; table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 7857ba267c5..56d7a3f8a9d 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -397,7 +397,13 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { Item_field *field; if ((field= item->filed_for_view_update())) + { + /* + any_privileges may be reset later by the Item_field::set_field + method in case of a system temporary table. + */ field->any_privileges= 1; + } } } #endif From 700c3786188e8e2c112615841d3be77e3d8e0f59 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Aug 2007 17:55:49 +0500 Subject: [PATCH 09/12] Bug#27629 Possible security flaw in INFORMATION_SCHEMA and SHOW statements(addon for 5.1) added TRIGGER_ACL check for I_S.TRIGGERS mysql-test/r/information_schema.result: result fix --- mysql-test/r/information_schema.result | 6 +++--- sql/sql_show.cc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 1ad30824214..e87b46ac90f 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1419,8 +1419,8 @@ where event_object_table='t1'; trigger_name t1_ai show triggers from mysqltest; -Trigger Event Table Statement Timing Created sql_mode Definer -t1_ai INSERT t1 set @a = new.a + new.b + new.c AFTER NULL root@localhost +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +t1_ai INSERT t1 set @a = new.a + new.b + new.c AFTER NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci show columns from t1; Field Type Null Key Default Extra b int(11) YES NULL @@ -1428,7 +1428,7 @@ select column_name from information_schema.columns where table_name='t1'; column_name b show triggers; -Trigger Event Table Statement Timing Created sql_mode Definer +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation select trigger_name from information_schema.triggers where event_object_table='t1'; trigger_name diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9c4bb1e0c0b..eebce82be62 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4351,7 +4351,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, int event, timing; #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!(thd->security_ctx->master_access & SUPER_ACL)) + if (check_table_access(thd, TRIGGER_ACL, tables, 1)) goto ret; #endif From 42aa480b4345aa920e0ff60001b09eb52c9c04c5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Aug 2007 18:59:42 +0500 Subject: [PATCH 10/12] compiler warning fix --- sql/sql_show.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e21de81fbdb..bba5eeefc73 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3412,7 +3412,9 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, } } } +#ifndef NO_EMBEDDED_ACCESS_CHECKS ret: +#endif DBUG_RETURN(0); } From 891b1659fcd0d0a65b2a6d8ccd40e9225b76bb88 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Aug 2007 18:15:54 +0500 Subject: [PATCH 11/12] Fixed bug #30201. Killing a SELECT query with KILL QUERY or KILL CONNECTION causes a server crash if the query cache is enabled. Normal evaluation of a query may be interrupted by the KILL QUERY/CONNECTION statement, in this case the mysql_execute_command function returns TRUE, and the thd->killed flag has true value. In this case the result of the query may be cached incompletely (omitting call to query_cache_insert inside the net_real_write function), and next call to query_cache_end_of_result may lead to server crash. Thus, the query_cache_end_of_result function has been modified to abort query cache in the case of killed thread. sql/sql_cache.cc: Fixed bug #30201. The query_cache_end_of_result function has been modified to abort query cache in the case of query execution failure. Also this function has been modified to remove incomplete query block. --- sql/sql_cache.cc | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 33d658ce6a1..0d8ef568550 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -710,6 +710,12 @@ void query_cache_end_of_result(THD *thd) if (thd->net.query_cache_query == 0) DBUG_VOID_RETURN; + if (thd->killed) + { + query_cache_abort(&thd->net); + DBUG_VOID_RETURN; + } + #ifdef EMBEDDED_LIBRARY query_cache_insert(&thd->net, (char*)thd, emb_count_querycache_size(thd)); @@ -727,27 +733,30 @@ void query_cache_end_of_result(THD *thd) DUMP(&query_cache); BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); - Query_cache_block *last_result_block= header->result()->prev; - ulong allign_size= ALIGN_SIZE(last_result_block->used); - ulong len= max(query_cache.min_allocation_unit, allign_size); + Query_cache_block *last_result_block; + ulong allign_size; + ulong len; + + if (header->result() == 0) + { + DBUG_PRINT("error", ("End of data with no result blocks; " + "Query '%s' removed from cache.", header->query())); + /* + Extra safety: empty result should not happen in the normal call + to this function. In the release version that query should be ignored + and removed from QC. + */ + DBUG_ASSERT(0); + query_cache.free_query(query_block); + goto end; + } + + last_result_block= header->result()->prev; + allign_size= ALIGN_SIZE(last_result_block->used); + len= max(query_cache.min_allocation_unit, allign_size); if (last_result_block->length >= query_cache.min_allocation_unit + len) query_cache.split_block(last_result_block,len); -#ifndef DBUG_OFF - if (header->result() == 0) - { - DBUG_PRINT("error", ("end of data whith no result. query '%s'", - header->query())); - query_cache.wreck(__LINE__, ""); - - /* - We do not need call of BLOCK_UNLOCK_WR(query_block); here because - query_cache.wreck() switched query cache off but left content - untouched for investigation (it is debugging method). - */ - goto end; - } -#endif header->found_rows(current_thd->limit_found_rows); header->result()->type= Query_cache_block::RESULT; header->writer(0); From f0d1ac25c349f09f2512d75f4e12f39d3a7729c3 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Aug 2007 23:34:48 +0500 Subject: [PATCH 12/12] Bug #28430 Failure in replication of innodb partitioned tables on row/mixed format. In the ha_partition::position() we didn't calculate the number of the partition of the record. We used m_last_part value instead, relying on that it is set in other place like previous call of a method like ::write_row(). In replication we don't call any of these befor position(). Delete_rows_log_event::do_exec_row calls find_and_fetch_row. In case of InnoDB-based PARTITION table, we have HA_PRIMARY_KEY_REQUIRED_FOR_POSITION enabled, so use position() / rnd_pos() calls to fetch the record. Fixed by adding partition_id calculation to the ha_partition::position() sql/ha_partition.h: Bug #28430 Failure in replication of innodb partitioned tables on row/mixed format. column_bitmaps_signal interface added sql/ha_partition.cc: Bug #28430 Failure in replication of innodb partitioned tables on row/mixed format. Calculate the number of the partition in ha_partition::position(). ha_partition::column_bitmaps_signal() implemented mysql-test/r/partition_pruning.result: Bug #28430 Failure in replication of innodb partitioned tables on row/mixed format. test result fixed --- mysql-test/r/partition_pruning.result | 4 ++-- sql/ha_partition.cc | 14 +++++++++++++- sql/ha_partition.h | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/partition_pruning.result b/mysql-test/r/partition_pruning.result index 9595676016c..776e6f3a15a 100644 --- a/mysql-test/r/partition_pruning.result +++ b/mysql-test/r/partition_pruning.result @@ -631,7 +631,7 @@ flush status; delete from t2 where b > 5; show status like 'Handler_read_rnd_next'; Variable_name Value -Handler_read_rnd_next 1215 +Handler_read_rnd_next 1115 show status like 'Handler_read_key'; Variable_name Value Handler_read_key 0 @@ -645,7 +645,7 @@ flush status; delete from t2 where b < 5 or b > 3; show status like 'Handler_read_rnd_next'; Variable_name Value -Handler_read_rnd_next 1215 +Handler_read_rnd_next 1115 show status like 'Handler_read_key'; Variable_name Value Handler_read_key 0 diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 2c28d5087d4..91eaf66e4e3 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3235,9 +3235,14 @@ end_dont_reset_start_part: void ha_partition::position(const uchar *record) { - handler *file= m_file[m_last_part]; + handler *file; DBUG_ENTER("ha_partition::position"); + if (unlikely(get_part_for_delete(record, m_rec0, m_part_info, &m_last_part))) + m_last_part= 0; + + file= m_file[m_last_part]; + file->position(record); int2store(ref, m_last_part); memcpy((ref + PARTITION_BYTES_IN_POS), file->ref, @@ -5587,6 +5592,13 @@ int ha_partition::indexes_are_disabled(void) } +void ha_partition::column_bitmaps_signal() +{ + handler::column_bitmaps_signal(); + bitmap_union(table->read_set, &m_part_info->full_part_field_set); +} + + /**************************************************************************** MODULE Partition Share ****************************************************************************/ diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 895f001fa6a..c3145b7c14c 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -449,6 +449,7 @@ private: int handle_ordered_next(uchar * buf, bool next_same); int handle_ordered_prev(uchar * buf); void return_top_record(uchar * buf); + void column_bitmaps_signal(); public: /* -------------------------------------------------------------------------