From b64dbeed6ec47ae03ccbf8d492265af071cbead4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Tue, 6 Oct 2009 13:04:51 +0200 Subject: [PATCH 01/24] Bug#47857 strip_sp function in mysys/mf_strip.c never used and cause name clash - Remove mf_strip.c and the declaration of 'strip_sp' --- include/my_sys.h | 1 - mysys/CMakeLists.txt | 2 +- mysys/Makefile.am | 2 +- mysys/mf_strip.c | 45 -------------------------------------------- sql/mysql_priv.h.pp | 1 - 5 files changed, 2 insertions(+), 49 deletions(-) delete mode 100644 mysys/mf_strip.c diff --git a/include/my_sys.h b/include/my_sys.h index 166133251bc..b4aac3a17bd 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -720,7 +720,6 @@ extern int wild_compare(const char *str,const char *wildstr, extern WF_PACK *wf_comp(char * str); extern int wf_test(struct wild_file_pack *wf_pack,const char *name); extern void wf_end(struct wild_file_pack *buffer); -extern size_t strip_sp(char * str); extern my_bool array_append_string_unique(const char *str, const char **array, size_t size); extern void get_date(char * to,int timeflag,time_t use_time); diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 545278485d1..7afb800643c 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -29,7 +29,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c default_ errors.c hash.c list.c md5.c mf_brkhant.c mf_cache.c mf_dirname.c mf_fn_ext.c mf_format.c mf_getdate.c mf_iocache.c mf_iocache2.c mf_keycache.c mf_keycaches.c mf_loadpath.c mf_pack.c mf_path.c mf_qsort.c mf_qsort2.c - mf_radix.c mf_same.c mf_sort.c mf_soundex.c mf_strip.c mf_arr_appstr.c mf_tempdir.c + mf_radix.c mf_same.c mf_sort.c mf_soundex.c mf_arr_appstr.c mf_tempdir.c mf_tempfile.c mf_unixpath.c mf_wcomp.c mf_wfile.c mulalloc.c my_access.c my_aes.c my_alarm.c my_alloc.c my_append.c my_bit.c my_bitmap.c my_chsize.c my_clock.c my_compress.c my_conio.c my_copy.c my_crc32.c my_create.c my_delete.c diff --git a/mysys/Makefile.am b/mysys/Makefile.am index 16f0e1a9759..19017330654 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -35,7 +35,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \ my_error.c errors.c my_div.c my_messnc.c \ mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \ my_symlink.c my_symlink2.c \ - mf_pack.c mf_unixpath.c mf_strip.c mf_arr_appstr.c \ + mf_pack.c mf_unixpath.c mf_arr_appstr.c \ mf_wcomp.c mf_wfile.c my_gethwaddr.c \ mf_qsort.c mf_qsort2.c mf_sort.c \ ptr_cmp.c mf_radix.c queues.c my_getncpus.c \ diff --git a/mysys/mf_strip.c b/mysys/mf_strip.c deleted file mode 100644 index b33620b1b2d..00000000000 --- a/mysys/mf_strip.c +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* T|mmer en str{ng p{ slut_space */ - -#include "mysys_priv.h" - -/* - strip_sp(char * str) - Strips end-space from string and returns new length. -*/ - -size_t strip_sp(register char * str) -{ - reg2 char * found; - reg3 char * start; - - start=found=str; - - while (*str) - { - if (*str != ' ') - { - while (*++str && *str != ' ') {}; - if (!*str) - return (size_t) (str-start); /* Return stringlength */ - } - found=str; - while (*++str == ' ') {}; - } - *found= '\0'; /* Stripp at first space */ - return (size_t) (found-start); -} /* strip_sp */ diff --git a/sql/mysql_priv.h.pp b/sql/mysql_priv.h.pp index 8bb31f64587..d874a2591d1 100644 --- a/sql/mysql_priv.h.pp +++ b/sql/mysql_priv.h.pp @@ -773,7 +773,6 @@ extern int wild_compare(const char *str,const char *wildstr, extern WF_PACK *wf_comp(char * str); extern int wf_test(struct wild_file_pack *wf_pack,const char *name); extern void wf_end(struct wild_file_pack *buffer); -extern size_t strip_sp(char * str); extern my_bool array_append_string_unique(const char *str, const char **array, size_t size); extern void get_date(char * to,int timeflag,time_t use_time); From 5faf23bf55ee7a7110514cf0cc7aeab96dc1013d Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 7 Oct 2009 18:03:42 +0300 Subject: [PATCH 02/24] Bug #43029: FORCE INDEX FOR ORDER BY is ignored when join buffering is used FORCE INDEX FOR ORDER BY now prevents the optimizer from using join buffering. As a result the optimizer can use indexed access on the first table and doesn't need to sort the complete resultset at the end of the statement. --- mysql-test/r/order_by.result | 31 +++++++++++++++++++++++++++++++ mysql-test/t/order_by.test | 32 ++++++++++++++++++++++++++++++++ sql/sql_base.cc | 6 ++++-- sql/sql_parse.cc | 1 + sql/sql_select.cc | 19 ++++++++++++++----- sql/sql_select.h | 2 ++ sql/table.cc | 25 ++++++++++++++++++++----- sql/table.h | 12 ++++++++++++ 8 files changed, 116 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index 306fce1f3c2..0c72f816c21 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -1557,3 +1557,34 @@ a 2001 1991 DROP TABLE t1; +# +# Bug #43029: FORCE INDEX FOR ORDER BY is ignored when join buffering +# is used +# +CREATE TABLE t1 (a INT, b INT, KEY (a)); +INSERT INTO t1 VALUES (0, NULL), (1, NULL), (2, NULL), (3, NULL); +INSERT INTO t1 SELECT a+4, b FROM t1; +INSERT INTO t1 SELECT a+8, b FROM t1; +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t2 VALUES (0,NULL), (1,NULL), (2,NULL), (3,NULL), (4,NULL); +INSERT INTO t2 SELECT a+4, b FROM t2; +# shouldn't have "using filesort" +EXPLAIN +SELECT * FROM t1 FORCE INDEX FOR ORDER BY (a), t2 WHERE t1.a < 2 ORDER BY t1.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 2 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 +# should have "using filesort" +EXPLAIN +SELECT * FROM t1 USE INDEX FOR ORDER BY (a), t2 WHERE t1.a < 2 ORDER BY t1.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 2 Using where; Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using join buffer +# should have "using filesort" +EXPLAIN +SELECT * FROM t1 FORCE INDEX FOR JOIN (a), t2 WHERE t1.a < 2 ORDER BY t1.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 2 Using where; Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using join buffer +DROP TABLE t1, t2; +End of 5.1 tests diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index cca1e3209cc..ac2bbaaeeac 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -1402,3 +1402,35 @@ SELECT DISTINCT a FROM t1 WHERE b = 1 ORDER BY c DESC LIMIT 0, 9; SELECT DISTINCT a FROM t1 WHERE b = 1 ORDER BY c DESC LIMIT 0, 9; DROP TABLE t1; + +--echo # +--echo # Bug #43029: FORCE INDEX FOR ORDER BY is ignored when join buffering +--echo # is used +--echo # + +CREATE TABLE t1 (a INT, b INT, KEY (a)); + +INSERT INTO t1 VALUES (0, NULL), (1, NULL), (2, NULL), (3, NULL); +INSERT INTO t1 SELECT a+4, b FROM t1; +INSERT INTO t1 SELECT a+8, b FROM t1; + +CREATE TABLE t2 (a INT, b INT); + +INSERT INTO t2 VALUES (0,NULL), (1,NULL), (2,NULL), (3,NULL), (4,NULL); +INSERT INTO t2 SELECT a+4, b FROM t2; + +--echo # shouldn't have "using filesort" +EXPLAIN +SELECT * FROM t1 FORCE INDEX FOR ORDER BY (a), t2 WHERE t1.a < 2 ORDER BY t1.a; + +--echo # should have "using filesort" +EXPLAIN +SELECT * FROM t1 USE INDEX FOR ORDER BY (a), t2 WHERE t1.a < 2 ORDER BY t1.a; + +--echo # should have "using filesort" +EXPLAIN +SELECT * FROM t1 FORCE INDEX FOR JOIN (a), t2 WHERE t1.a < 2 ORDER BY t1.a; + +DROP TABLE t1, t2; + +--echo End of 5.1 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d1e96fcdbb3..e706bd04ea6 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2305,7 +2305,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in) table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->null_row= table->maybe_null= table->force_index= 0; + table->null_row= table->maybe_null= 0; + table->force_index= table->force_index_order= table->force_index_group= 0; table->status=STATUS_NO_RECORD; DBUG_RETURN(FALSE); } @@ -2963,7 +2964,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->null_row= table->maybe_null= table->force_index= 0; + table->null_row= table->maybe_null= 0; + table->force_index= table->force_index_order= table->force_index_group= 0; table->status=STATUS_NO_RECORD; table->insert_values= 0; table->fulltext_searched= 0; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 83ef525e3eb..27688e41d4f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6258,6 +6258,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->table_name_length=table->table.length; ptr->lock_type= lock_type; ptr->updating= test(table_options & TL_OPTION_UPDATING); + /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */ ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); ptr->derived= table->sel; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3f1432914a0..9dacb2c2ce4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1231,13 +1231,22 @@ JOIN::optimize() (!group_list && tmp_table_param.sum_func_count)) order=0; - // Can't use sort on head table if using row cache + // Can't use sort on head table if using join buffering if (full_join) { - if (group_list) - simple_group=0; - if (order) - simple_order=0; + TABLE *stable= (sort_by_table == (TABLE *) 1 ? + join_tab[const_tables].table : sort_by_table); + /* + FORCE INDEX FOR ORDER BY can be used to prevent join buffering when + sorting on the first table. + */ + if (!stable || !stable->force_index_order) + { + if (group_list) + simple_group= 0; + if (order) + simple_order= 0; + } } /* diff --git a/sql/sql_select.h b/sql/sql_select.h index a0366d47149..3f06b402638 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -357,6 +357,8 @@ public: simple_xxxxx is set if ORDER/GROUP BY doesn't include any references to other tables than the first non-constant table in the JOIN. It's also set if ORDER/GROUP BY is empty. + Used for deciding for or against using a temporary table to compute + GROUP/ORDER BY. */ bool simple_order, simple_group; /** diff --git a/sql/table.cc b/sql/table.cc index 04f2a3fbcf8..d2538eb4d59 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4637,7 +4637,8 @@ Item_subselect *TABLE_LIST::containing_subselect() (TABLE_LIST::index_hints). Using the information in this tagged list this function sets the members st_table::keys_in_use_for_query, st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by, - st_table::force_index and st_table::covering_keys. + st_table::force_index, st_table::force_index_order, + st_table::force_index_group and st_table::covering_keys. Current implementation of the runtime does not allow mixing FORCE INDEX and USE INDEX, so this is checked here. Then the FORCE INDEX list @@ -4765,14 +4766,28 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl) } /* process FORCE INDEX as USE INDEX with a flag */ + if (!index_order[INDEX_HINT_FORCE].is_clear_all()) + { + tbl->force_index_order= TRUE; + index_order[INDEX_HINT_USE].merge(index_order[INDEX_HINT_FORCE]); + } + + if (!index_group[INDEX_HINT_FORCE].is_clear_all()) + { + tbl->force_index_group= TRUE; + index_group[INDEX_HINT_USE].merge(index_group[INDEX_HINT_FORCE]); + } + + /* + TODO: get rid of tbl->force_index (on if any FORCE INDEX is specified) and + create tbl->force_index_join instead. + Then use the correct force_index_XX instead of the global one. + */ if (!index_join[INDEX_HINT_FORCE].is_clear_all() || - !index_order[INDEX_HINT_FORCE].is_clear_all() || - !index_group[INDEX_HINT_FORCE].is_clear_all()) + tbl->force_index_group || tbl->force_index_order) { tbl->force_index= TRUE; index_join[INDEX_HINT_USE].merge(index_join[INDEX_HINT_FORCE]); - index_order[INDEX_HINT_USE].merge(index_order[INDEX_HINT_FORCE]); - index_group[INDEX_HINT_USE].merge(index_group[INDEX_HINT_FORCE]); } /* apply USE INDEX */ diff --git a/sql/table.h b/sql/table.h index 40372fa91cf..e4a382c799f 100644 --- a/sql/table.h +++ b/sql/table.h @@ -752,6 +752,18 @@ struct st_table { bytes, it would take up 4. */ my_bool force_index; + + /** + Flag set when the statement contains FORCE INDEX FOR ORDER BY + See TABLE_LIST::process_index_hints(). + */ + my_bool force_index_order; + + /** + Flag set when the statement contains FORCE INDEX FOR GROUP BY + See TABLE_LIST::process_index_hints(). + */ + my_bool force_index_group; my_bool distinct,const_table,no_rows; /** From edd89cf7f75df785b3c59f5c72a853bcb4dc31ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Thu, 8 Oct 2009 13:25:11 +0200 Subject: [PATCH 03/24] Merge --- .../suite/binlog/t/binlog_stm_unsafe_warning.test | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test index a5472952f08..656eaae5721 100644 --- a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test +++ b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test @@ -94,15 +94,24 @@ DROP TABLE t1; SET GLOBAL log_warnings = @old_log_warnings; -let LOG_ERROR= `SELECT @@GLOBAL.log_error`; +let $log_error= `SELECT @@GLOBAL.log_error`; +if(!$log_error) +{ + # MySQL Server on windows is started with --console and thus + # does not know the location of its .err log, use default location + let $log_error = $MYSQLTEST_VARDIR/log/mysqld.1.err; +} +# Assign env variable LOG_ERROR +let LOG_ERROR=$log_error; --echo # Count the number of times the "Unsafe" message was printed --echo # to the error log. perl; - $log_error= $ENV{'LOG_ERROR'}; + use strict; + my $log_error= $ENV{'LOG_ERROR'} || die "LOG_ERROR not set"; open(FILE, "$log_error") or die("Unable to open $log_error: $!\n"); - $count = () = grep(/Bug#46265/g,); + my $count = () = grep(/Bug#46265/g,); print "Occurrences: $count\n"; close(FILE); EOF From 99318017d5c71853b6f3c7e3da1c81b19a2b9f09 Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Thu, 8 Oct 2009 16:56:31 +0500 Subject: [PATCH 04/24] Fix for bug #42803: Field_bit does not have unsigned_flag field, can lead to bad memory access Problem: Field_bit is the only field which returns INT_RESULT and doesn't have unsigned flag. As it's not a descendant of the Field_num, so using ((Field_num *) field_bit)->unsigned_flag may lead to unpredictable results. Fix: check the field type before casting. --- mysql-test/r/type_bit.result | 10 ++++++++++ mysql-test/t/type_bit.test | 11 +++++++++++ sql/opt_range.cc | 5 ++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/type_bit.result b/mysql-test/r/type_bit.result index 63dec0297d0..252f8165aec 100644 --- a/mysql-test/r/type_bit.result +++ b/mysql-test/r/type_bit.result @@ -749,4 +749,14 @@ bin(a1) 110000111111111 110001011111111 drop table t1bit7, t2bit7; +# +# Bug42803: Field_bit does not have unsigned_flag field, +# can lead to bad memory access +# +CREATE TABLE t1 (a BIT(7), b BIT(9), KEY(a, b)); +INSERT INTO t1 VALUES(0, 0), (5, 3), (5, 6), (6, 4), (7, 0); +EXPLAIN SELECT a+0, b+0 FROM t1 WHERE a > 4 and b < 7 ORDER BY 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 2 NULL 4 Using where; Using index; Using filesort +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/type_bit.test b/mysql-test/t/type_bit.test index bdc678688f1..7ec0649cbdd 100644 --- a/mysql-test/t/type_bit.test +++ b/mysql-test/t/type_bit.test @@ -397,4 +397,15 @@ insert into t2bit7 values (b'110011011111111'); select bin(a1) from t1bit7, t2bit7 where t1bit7.a1=t2bit7.b1; drop table t1bit7, t2bit7; + +--echo # +--echo # Bug42803: Field_bit does not have unsigned_flag field, +--echo # can lead to bad memory access +--echo # +CREATE TABLE t1 (a BIT(7), b BIT(9), KEY(a, b)); +INSERT INTO t1 VALUES(0, 0), (5, 3), (5, 6), (6, 4), (7, 0); +EXPLAIN SELECT a+0, b+0 FROM t1 WHERE a > 4 and b < 7 ORDER BY 2; +DROP TABLE t1; + + --echo End of 5.0 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fdf6cc03a44..355317fe280 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -4536,6 +4536,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, if (type == Item_func::LT_FUNC && (value->val_int() > 0)) type = Item_func::LE_FUNC; else if (type == Item_func::GT_FUNC && + (field->type() != FIELD_TYPE_BIT) && !((Field_num*)field)->unsigned_flag && !((Item_int*)value)->unsigned_flag && (value->val_int() < 0)) @@ -4572,7 +4573,9 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, */ if (field->result_type() == INT_RESULT && value->result_type() == INT_RESULT && - ((Field_num*)field)->unsigned_flag && !((Item_int*)value)->unsigned_flag) + ((field->type() == FIELD_TYPE_BIT || + ((Field_num *) field)->unsigned_flag) && + !((Item_int*) value)->unsigned_flag)) { longlong item_val= value->val_int(); if (item_val < 0) From ec025c694bbaf268479e705f77c096ce13a1c983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Thu, 8 Oct 2009 14:00:43 +0200 Subject: [PATCH 05/24] BUG#47129 fix small bug in test --- .../suite/binlog/t/binlog_stm_unsafe_warning.test | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test index 656eaae5721..21c11d5a3df 100644 --- a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test +++ b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test @@ -94,22 +94,22 @@ DROP TABLE t1; SET GLOBAL log_warnings = @old_log_warnings; -let $log_error= `SELECT @@GLOBAL.log_error`; -if(!$log_error) +let $log_error_= `SELECT @@GLOBAL.log_error`; +if(!`select LENGTH('$log_error_')`) { # MySQL Server on windows is started with --console and thus # does not know the location of its .err log, use default location - let $log_error = $MYSQLTEST_VARDIR/log/mysqld.1.err; + let $log_error_ = $MYSQLTEST_VARDIR/log/mysqld.1.err; } # Assign env variable LOG_ERROR -let LOG_ERROR=$log_error; +let LOG_ERROR=$log_error_; --echo # Count the number of times the "Unsafe" message was printed --echo # to the error log. perl; use strict; - my $log_error= $ENV{'LOG_ERROR'} || die "LOG_ERROR not set"; + my $log_error= $ENV{'LOG_ERROR'} or die "LOG_ERROR not set"; open(FILE, "$log_error") or die("Unable to open $log_error: $!\n"); my $count = () = grep(/Bug#46265/g,); print "Occurrences: $count\n"; From 5da8781d7cfa8935a60335ea9bf5f665f12ced69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Thu, 8 Oct 2009 14:54:11 +0200 Subject: [PATCH 06/24] Bug #47797 CMake, engine can't specify additional libraries to link with - Make it possible for the CmakeLists.txt files in an engine to use ${engine}_LIBS to set additional libraries to link with Example: NDBCLUSTER_LIBS = ndbclient --- storage/mysql_storage_engine.cmake | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/storage/mysql_storage_engine.cmake b/storage/mysql_storage_engine.cmake index bb368494898..af8c3a85cd1 100644 --- a/storage/mysql_storage_engine.cmake +++ b/storage/mysql_storage_engine.cmake @@ -7,6 +7,8 @@ # Remarks: # ${engine}_SOURCES variable containing source files to produce the library must set before # calling this macro +# ${engine}_LIBS variable containing extra libraries to link with may be set + MACRO(MYSQL_STORAGE_ENGINE engine) IF(NOT SOURCE_SUBLIBS) @@ -22,6 +24,9 @@ IF(NOT SOURCE_SUBLIBS) #Create static library. The name of the library is .lib ADD_LIBRARY(${libname} ${${engine}_SOURCES}) ADD_DEPENDENCIES(${libname} GenError) + IF(${engine}_LIBS) + TARGET_LINK_LIBRARIES(${libname} ${${engine}_LIBS}) + ENDIF(${engine}_LIBS) MESSAGE("build ${engine} as static library") ELSEIF(${ENGINE_BUILD_TYPE} STREQUAL "DYNAMIC") ADD_DEFINITIONS(-DMYSQL_DYNAMIC_PLUGIN) @@ -30,6 +35,9 @@ IF(NOT SOURCE_SUBLIBS) SET(dyn_libname ha_${libname}) ADD_LIBRARY(${dyn_libname} SHARED ${${engine}_SOURCES}) TARGET_LINK_LIBRARIES (${dyn_libname} mysqld) + IF(${engine}_LIBS) + TARGET_LINK_LIBRARIES(${dyn_libname} ${${engine}_LIBS}) + ENDIF(${engine}_LIBS) MESSAGE("build ${engine} as DLL") ENDIF(${ENGINE_BUILD_TYPE} STREQUAL "STATIC") ENDIF(NOT SOURCE_SUBLIBS) From b92892dc9cc30ba2c6fd83ed684afe3ef4279f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Thu, 8 Oct 2009 15:19:24 +0200 Subject: [PATCH 07/24] Bug #47795 CMake, storage engine name different from directory name - Read plug.in to fid the name of the engine to link with, does not have to be same as engine dir - Use engine dir when figuring out which libraries to build limbysqld with --- CMakeLists.txt | 9 +++++++-- libmysqld/CMakeLists.txt | 13 +++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c40312e32b..7da3bd05cef 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,12 +217,16 @@ ENDIF(WITHOUT_DYNAMIC_PLUGINS) FILE(GLOB STORAGE_SUBDIRS storage/*) FOREACH(SUBDIR ${STORAGE_SUBDIRS}) FILE(RELATIVE_PATH DIRNAME ${PROJECT_SOURCE_DIR}/storage ${SUBDIR}) - STRING(TOUPPER ${DIRNAME} ENGINE) - STRING(TOLOWER ${DIRNAME} ENGINE_LOWER) IF (EXISTS ${SUBDIR}/CMakeLists.txt) # Check MYSQL_STORAGE_ENGINE macro is present FILE(STRINGS ${SUBDIR}/CMakeLists.txt HAVE_STORAGE_ENGINE REGEX MYSQL_STORAGE_ENGINE) IF(HAVE_STORAGE_ENGINE) + # Extract name of engine from HAVE_STORAGE_ENGINE + STRING(REGEX REPLACE ".*MYSQL_STORAGE_ENGINE\\((.*\)\\).*" + "\\1" ENGINE_NAME ${HAVE_STORAGE_ENGINE}) + STRING(TOUPPER ${ENGINE_NAME} ENGINE) + STRING(TOLOWER ${ENGINE_NAME} ENGINE_LOWER) + SET(ENGINE_BUILD_TYPE "DYNAMIC") # Read plug.in to find out if a plugin is mandatory and whether it supports # build as shared library (dynamic). @@ -248,6 +252,7 @@ FOREACH(SUBDIR ${STORAGE_SUBDIRS}) SET (MYSQLD_STATIC_ENGINE_LIBS ${MYSQLD_STATIC_ENGINE_LIBS} ${ENGINE_LOWER}) SET (STORAGE_ENGINE_DEFS "${STORAGE_ENGINE_DEFS} -DWITH_${ENGINE}_STORAGE_ENGINE") SET (WITH_${ENGINE}_STORAGE_ENGINE TRUE) + SET (${ENGINE}_DIR ${DIRNAME}) ENDIF (ENGINE_BUILD_TYPE STREQUAL "STATIC") ENDIF(EXISTS ${SUBDIR}/plug.in) diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index bea6f6c0e1f..db398258b69 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -90,10 +90,11 @@ ENDFOREACH(rpath) FOREACH (ENGINE_LIB ${MYSQLD_STATIC_ENGINE_LIBS}) - INCLUDE(${CMAKE_SOURCE_DIR}/storage/${ENGINE_LIB}/CMakeLists.txt) STRING(TOUPPER ${ENGINE_LIB} ENGINE_LIB_UPPER) + SET(ENGINE_DIR ${${ENGINE_LIB_UPPER}_DIR}) + INCLUDE(${CMAKE_SOURCE_DIR}/storage/${ENGINE_DIR}/CMakeLists.txt) FOREACH(rpath ${${ENGINE_LIB_UPPER}_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ${CMAKE_SOURCE_DIR}/storage/${ENGINE_LIB}/${rpath}) + SET(LIB_SOURCES ${LIB_SOURCES} ${CMAKE_SOURCE_DIR}/storage/${ENGINE_DIR}/${rpath}) ENDFOREACH(rpath) ENDFOREACH(ENGINE_LIB) @@ -155,6 +156,14 @@ ADD_LIBRARY(mysqlserver STATIC ${LIBMYSQLD_SOURCES}) ADD_DEPENDENCIES(mysqlserver GenServerSource GenError) TARGET_LINK_LIBRARIES(mysqlserver) +# Add any additional libraries requested by engine(s) +FOREACH (ENGINE_LIB ${MYSQLD_STATIC_ENGINE_LIBS}) + STRING(TOUPPER ${ENGINE_LIB} ENGINE_LIB_UPPER) + IF(${ENGINE_LIB_UPPER}_LIBS) + TARGET_LINK_LIBRARIES(mysqlserver ${${ENGINE_LIB_UPPER}_LIBS}) + ENDIF(${ENGINE_LIB_UPPER}_LIBS) +ENDFOREACH(ENGINE_LIB) + ADD_LIBRARY(libmysqld SHARED cmake_dummy.c libmysqld.def) ADD_DEPENDENCIES(libmysqld mysqlserver) TARGET_LINK_LIBRARIES(libmysqld mysqlserver wsock32) From 03904aee4ef2fb72408053bd3ca4cc9ccc952c59 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 8 Oct 2009 16:21:07 +0300 Subject: [PATCH 08/24] Addendum to the fix for bug 43029 --- sql/mysql_priv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 6fde16d3049..c04b1f5ae38 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2433,6 +2433,7 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) table->tablenr= tablenr; table->map= (table_map) 1 << tablenr; table->force_index= table_list->force_index; + table->force_index_order= table->force_index_group= 0; table->covering_keys= table->s->keys_for_keyread; table->merge_keys.clear_all(); } From 87a4644db8715c1d04560f8c80a776d5a7ebfd9e Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 8 Oct 2009 15:36:43 +0200 Subject: [PATCH 09/24] Bug#46922: crash when adding partitions and open_files_limit is reached Problem was bad error handling, leaving some new temporary partitions locked and initialized and some not yet initialized and locked, leading to a crash when trying to unlock the not yet initialized and locked partitions Solution was to unlock the already locked partitions, and not include any of the new temporary partitions in later unlocks --- .../r/partition_open_files_limit.result | 22 +++++++++++++++++++ .../t/partition_open_files_limit-master.opt | 1 + mysql-test/t/partition_open_files_limit.test | 19 ++++++++++++++++ sql/ha_partition.cc | 2 +- 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/partition_open_files_limit.result create mode 100644 mysql-test/t/partition_open_files_limit-master.opt create mode 100644 mysql-test/t/partition_open_files_limit.test diff --git a/mysql-test/r/partition_open_files_limit.result b/mysql-test/r/partition_open_files_limit.result new file mode 100644 index 00000000000..1441ba4e78e --- /dev/null +++ b/mysql-test/r/partition_open_files_limit.result @@ -0,0 +1,22 @@ +DROP TABLE IF EXISTS `t1`; +# Bug#46922: crash when adding partitions and open_files_limit is reached +CREATE TABLE t1 (a INT PRIMARY KEY) +ENGINE=MyISAM PARTITION BY KEY () PARTITIONS 1; +INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); +# if the bug exists, then crash will happen here +ALTER TABLE t1 ADD PARTITION PARTITIONS 511; +ERROR HY000: Out of resources when opening file '' (Errcode: 24) +SELECT * FROM t1; +a +1 +10 +11 +2 +3 +4 +5 +6 +7 +8 +9 +DROP TABLE t1; diff --git a/mysql-test/t/partition_open_files_limit-master.opt b/mysql-test/t/partition_open_files_limit-master.opt new file mode 100644 index 00000000000..4c1ed0c3da3 --- /dev/null +++ b/mysql-test/t/partition_open_files_limit-master.opt @@ -0,0 +1 @@ +--open-files-limit=5 --max_connections=2 --table_open_cache=1 diff --git a/mysql-test/t/partition_open_files_limit.test b/mysql-test/t/partition_open_files_limit.test new file mode 100644 index 00000000000..92a9b18b573 --- /dev/null +++ b/mysql-test/t/partition_open_files_limit.test @@ -0,0 +1,19 @@ +--source include/have_partition.inc + +--disable_warnings +DROP TABLE IF EXISTS `t1`; +--enable_warnings + +# +--echo # Bug#46922: crash when adding partitions and open_files_limit is reached +# +CREATE TABLE t1 (a INT PRIMARY KEY) +ENGINE=MyISAM PARTITION BY KEY () PARTITIONS 1; +INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); +--echo # if the bug exists, then crash will happen here +--replace_regex /file '.*'/file ''/ +--error 23 +ALTER TABLE t1 ADD PARTITION PARTITIONS 511; +--sorted_result +SELECT * FROM t1; +DROP TABLE t1; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 5b053ab9cac..6b5350a82cd 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1280,10 +1280,10 @@ void ha_partition::cleanup_new_partition(uint part_count) m_file= m_added_file; m_added_file= NULL; + external_lock(ha_thd(), F_UNLCK); /* delete_table also needed, a bit more complex */ close(); - m_added_file= m_file; m_file= save_m_file; } DBUG_VOID_RETURN; From 27b80f9db8f611cc9f4d28d58efc34697363e875 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 8 Oct 2009 15:58:17 +0200 Subject: [PATCH 10/24] Bug#44059: Incorrect cardinality of indexes on a partitioned table backport for bug#44059 from mysql-pe to mysql-5.1-bugteam Using the partition with most rows instead of first partition to estimate the cardinality of indexes. --- mysql-test/r/partition.result | 15 +++++++++++++++ mysql-test/t/partition.test | 13 +++++++++++++ sql/ha_partition.cc | 34 +++++++++++++++++++++++++++------- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 2d54a66fe11..6611d39628f 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -50,6 +50,21 @@ t1 CREATE TABLE `t1` ( PARTITION p3 VALUES LESS THAN (733969) ENGINE = MyISAM, PARTITION pmax VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */ DROP TABLE t1; +create table t1 (a int, b int, key(a)) +partition by list (a) +( partition p0 values in (1), +partition p1 values in (2)); +insert into t1 values (1,1),(2,1),(2,2),(2,3); +show indexes from t1; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +t1 1 a 1 a A NULL NULL NULL YES BTREE +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +show indexes from t1; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +t1 1 a 1 a A 1 NULL NULL YES BTREE +drop table t1; CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a)) ENGINE=MyISAM PARTITION BY HASH (a); diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index c5ed098b678..1dfc53c6232 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -61,6 +61,19 @@ SELECT * FROM t1; SHOW CREATE TABLE t1; DROP TABLE t1; +# +# Bug#44059: rec_per_key on empty partition gives weird optimiser results +# +create table t1 (a int, b int, key(a)) +partition by list (a) +( partition p0 values in (1), + partition p1 values in (2)); +insert into t1 values (1,1),(2,1),(2,2),(2,3); +show indexes from t1; +analyze table t1; +show indexes from t1; +drop table t1; + # # Bug#36001: Partitions: spelling and using some error messages # diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index df5badccb1e..ac55c4e718e 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -5011,8 +5011,9 @@ int ha_partition::info(uint flag) If the handler doesn't support statistics, it should set all of the above to 0. - We will allow the first handler to set the rec_per_key and use - this as an estimate on the total table. + We first scans through all partitions to get the one holding most rows. + We will then allow the handler with the most rows to set + the rec_per_key and use this as an estimate on the total table. max_data_file_length: Maximum data file length We ignore it, is only used in @@ -5024,14 +5025,33 @@ int ha_partition::info(uint flag) ref_length: We set this to the value calculated and stored in local object create_time: Creation time of table - Set by first handler - So we calculate these constants by using the variables on the first - handler. + So we calculate these constants by using the variables from the + handler with most rows. */ - handler *file; + handler *file, **file_array; + ulonglong max_records= 0; + uint32 i= 0; + uint32 handler_instance= 0; - file= m_file[0]; + file_array= m_file; + do + { + file= *file_array; + /* Get variables if not already done */ + if (!(flag & HA_STATUS_VARIABLE) || + !bitmap_is_set(&(m_part_info->used_partitions), + (file_array - m_file))) + file->info(HA_STATUS_VARIABLE); + if (file->stats.records > max_records) + { + max_records= file->stats.records; + handler_instance= i; + } + i++; + } while (*(++file_array)); + + file= m_file[handler_instance]; file->info(HA_STATUS_CONST); stats.create_time= file->stats.create_time; ref_length= m_ref_length; From 844eb557e64cc4fe03db48493b7f55728f4f4f32 Mon Sep 17 00:00:00 2001 From: Frazer Clement Date: Thu, 8 Oct 2009 16:23:15 +0100 Subject: [PATCH 11/24] Fix compile break from bug#39663 fix --- client/mysqltest.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index fb33d30da81..442b2020a56 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -6214,8 +6214,10 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, MYSQL_STMT *stmt; DYNAMIC_STRING ds_prepare_warnings; DYNAMIC_STRING ds_execute_warnings; + ulonglong affected_rows; DBUG_ENTER("run_query_stmt"); DBUG_PRINT("query", ("'%-.60s'", query)); + LINT_INIT(affected_rows); /* Init a new stmt if it's not already one created for this connection @@ -6350,9 +6352,6 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, Need to grab affected rows information before getting warnings here */ - ulonglong affected_rows; - LINT_INIT(affected_rows); - if (!disable_info) affected_rows= mysql_affected_rows(mysql); From e94ae02cbcbf71157857a414a0c48bf60ea5f2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Fri, 9 Oct 2009 09:53:29 +0200 Subject: [PATCH 12/24] BUG#47850: too many files built in regex/ - Don't build split.c or debug.c since they are not part of the actual regex library --- regex/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/regex/CMakeLists.txt b/regex/CMakeLists.txt index a3088c00357..2e3b18c7bb0 100755 --- a/regex/CMakeLists.txt +++ b/regex/CMakeLists.txt @@ -18,7 +18,7 @@ SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -DSAFEMALLOC -DSAFE_MUT INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) -SET(REGEX_SOURCES debug.c regcomp.c regerror.c regexec.c regfree.c reginit.c split.c) +SET(REGEX_SOURCES regcomp.c regerror.c regexec.c regfree.c reginit.c) IF(NOT SOURCE_SUBLIBS) ADD_LIBRARY(regex ${REGEX_SOURCES}) From 090985ffe650cd78e875282089749c54a22830fc Mon Sep 17 00:00:00 2001 From: He Zhenxing Date: Fri, 9 Oct 2009 16:54:48 +0800 Subject: [PATCH 13/24] Bug#47323 : mysqlbinlog --verbose displays bad output when events contain subset of columns Commit the non-NDB specific part (originated by frazer) to 5.1 mainline. --- .../r/binlog_row_mysqlbinlog_verbose.result | 161 ++++++++++++++++++ .../binlog/std_data/update-full-row.binlog | Bin 0 -> 614 bytes .../binlog/std_data/update-partial-row.binlog | Bin 0 -> 606 bytes .../binlog/std_data/write-full-row.binlog | Bin 0 -> 571 bytes .../binlog/std_data/write-partial-row.binlog | Bin 0 -> 596 bytes .../t/binlog_row_mysqlbinlog_verbose.test | 82 +++++++++ sql/log_event.cc | 6 +- 7 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result create mode 100644 mysql-test/suite/binlog/std_data/update-full-row.binlog create mode 100644 mysql-test/suite/binlog/std_data/update-partial-row.binlog create mode 100644 mysql-test/suite/binlog/std_data/write-full-row.binlog create mode 100644 mysql-test/suite/binlog/std_data/write-partial-row.binlog create mode 100644 mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test diff --git a/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result b/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result new file mode 100644 index 00000000000..f1a3fafc498 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result @@ -0,0 +1,161 @@ +Verbose statements from : write-partial-row.binlog +select txt from raw_binlog_rows where txt like '###%'; +txt +### INSERT INTO mysql.ndb_apply_status +### SET +### @1=1 +### @2=25769803786 +### @3='' +### @4=0 +### @5=0 +### INSERT INTO test.ba +### SET +### @1=3 +### @2=3 +### @3=3 +### INSERT INTO test.ba +### SET +### @1=1 +### @2=1 +### @3=1 +### INSERT INTO test.ba +### SET +### @1=2 +### @2=2 +### @3=2 +### INSERT INTO test.ba +### SET +### @1=4 +### @2=4 +### @3=4 +### INSERT INTO test.ba +### SET +### @1=4 +### @3=40 +### DELETE FROM test.ba +### WHERE +### @1=2 +drop table raw_binlog_rows; +Verbose statements from : write-full-row.binlog +select txt from raw_binlog_rows where txt like '###%'; +txt +### INSERT INTO mysql.ndb_apply_status +### SET +### @1=2 +### @2=25769803786 +### @3='' +### @4=0 +### @5=0 +### INSERT INTO test.ba +### SET +### @1=3 +### @2=3 +### @3=3 +### INSERT INTO test.ba +### SET +### @1=1 +### @2=1 +### @3=1 +### INSERT INTO test.ba +### SET +### @1=2 +### @2=2 +### @3=2 +### INSERT INTO test.ba +### SET +### @1=4 +### @2=4 +### @3=4 +### INSERT INTO test.ba +### SET +### @1=4 +### @2=4 +### @3=40 +### DELETE FROM test.ba +### WHERE +### @1=2 +drop table raw_binlog_rows; +Verbose statements from : update-partial-row.binlog +select txt from raw_binlog_rows where txt like '###%'; +txt +### INSERT INTO mysql.ndb_apply_status +### SET +### @1=3 +### @2=25769803786 +### @3='' +### @4=0 +### @5=0 +### INSERT INTO test.ba +### SET +### @1=3 +### @2=3 +### @3=3 +### INSERT INTO test.ba +### SET +### @1=1 +### @2=1 +### @3=1 +### INSERT INTO test.ba +### SET +### @1=2 +### @2=2 +### @3=2 +### INSERT INTO test.ba +### SET +### @1=4 +### @2=4 +### @3=4 +### UPDATE test.ba +### WHERE +### @1=4 +### @3=4 +### SET +### @1=4 +### @3=40 +### DELETE FROM test.ba +### WHERE +### @1=2 +drop table raw_binlog_rows; +Verbose statements from : update-full-row.binlog +select txt from raw_binlog_rows where txt like '###%'; +txt +### INSERT INTO mysql.ndb_apply_status +### SET +### @1=4 +### @2=25769803786 +### @3='' +### @4=0 +### @5=0 +### INSERT INTO test.ba +### SET +### @1=3 +### @2=3 +### @3=3 +### INSERT INTO test.ba +### SET +### @1=1 +### @2=1 +### @3=1 +### INSERT INTO test.ba +### SET +### @1=2 +### @2=2 +### @3=2 +### INSERT INTO test.ba +### SET +### @1=4 +### @2=4 +### @3=4 +### UPDATE test.ba +### WHERE +### @1=4 +### @2=4 +### @3=4 +### SET +### @1=4 +### @2=4 +### @3=40 +### DELETE FROM test.ba +### WHERE +### @1=2 +drop table raw_binlog_rows; diff --git a/mysql-test/suite/binlog/std_data/update-full-row.binlog b/mysql-test/suite/binlog/std_data/update-full-row.binlog new file mode 100644 index 0000000000000000000000000000000000000000..866a351033e5fd95cd96736f7575cf1278cba11c GIT binary patch literal 614 zcma)4!Ab&A6g|#7^$~=ki_A^XDySLKsH`BYh%nHI+Ka=e6sFdU3%{gg`@W&yh_-#k zw9(FeF$3bJ3uk8Tz2}^JXWq=bhvE43<2t7R^oU0SP}p}X?%ts@^gZXmt-6&X$M1TR zzB3s04P*HtHpSX1Gzw6R0&oXa)?{j}EooaarB1v{kw~J;@q$uLGNbOhP?nvuHd^h6tperor<7dA?V>Gae77?I7%glK^R3 z=eXq@5cQf2Iw4w%>Wyz{tPmMuDH7=|TW>}h?`Y2>o5dKBG-E^-lNgaqh#`h0NovH` zqG2|>KuwY@Qbo5ex=rGTx;*|CNx@FdWRx(>ahx$ZX666KadFdZHtt_&pk%}j(GyFv QwcO1&_8tCO?yf}j7fK*a0{{R3 literal 0 HcmV?d00001 diff --git a/mysql-test/suite/binlog/std_data/update-partial-row.binlog b/mysql-test/suite/binlog/std_data/update-partial-row.binlog new file mode 100644 index 0000000000000000000000000000000000000000..67e3611aa3a5d8dd2bd9b1b0bd6a590b0562cdce GIT binary patch literal 606 zcma))%}T>S6opTdNf`ubX&2UR1XqG4v=#plq$(l`t%$BI4XH(`wI%7IPvX+O&)_?_ za_=j+%R*Yu9hpMu#tUDPnS0K?HNaXkmq}~+JixF+=`QS zGK%53I>#xefVfwr(IIKhyVt%Xu|%?DA!gQVre2NJ&gf24v073|T2hA#ORBiANWqeL zD&%9npy&NqnR(ctMD(koUnk$x=kYf{25x2|Ekt3C;h2jWcm972r&o=}#qE#BBq4l6=OG0Wzei zc=PUK}DQ3598U#ZP6y8C-4G@BprJ-@95MWhJkzH)xBE7 z4g0}l;6|f?R;)hQrgFAUipnGrWukkMRRyI|%I31otF*uyCqW)nj9NsNNjM@&L#ZHw z?DG1i-PQKozYo5&ZhNVgX+KHH4tx>_a&qZ81QqSMxs(=Or^z^?627bF$8j|6C8?iI z5-JE)#4Z;^+`YjIH9_}p_s%yL)_?<6B5!>}>0P&)J&HvpkThgmG-PaQ$Rs9)e6oim zyKk{<7kr|*M8ED932V*;R_HUnz=W^#|7U!4*Xgt$#xRiVbO4$>&|BfRKDf}u|Kc5T G)PDdA3P>dY literal 0 HcmV?d00001 diff --git a/mysql-test/suite/binlog/std_data/write-partial-row.binlog b/mysql-test/suite/binlog/std_data/write-partial-row.binlog new file mode 100644 index 0000000000000000000000000000000000000000..7424ec4e940b5bd3b3d261e5be16f685d883c241 GIT binary patch literal 596 zcmaJ?=e}h+l zf_HEJg&ef?&1xc2@WI>c%)I%?uru#=IC^-#C|E>v1Kxo|l6G}X?=+mD?>T$Au4@O5 z-}fd1Cm0QkV)5WI<^5$+R3ZtKh^|Ri6qHISJ4D##E=}->onVFvZcQSN8{|b4`%*y! z*~!^?tHb$aj&FjmMx@P&Y2$trlkK@A0Lo|5BM3|mRez+p`)M=|sEGgSxnUSgdr|Dh zlZbLc6)@$Th_hF?(IMC|6J^J>tdCN-T=%o71;%7lX{6B|a#M8Zg! z72JUH $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +create table raw_binlog_rows (txt varchar(1000)); +--eval load data local infile '$MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql' into table raw_binlog_rows columns terminated by '\n'; +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +--enable_query_log +--echo Verbose statements from : $binlog_file +select txt from raw_binlog_rows where txt like '###%'; +drop table raw_binlog_rows; + +--disable_query_log +--let $binlog_file=write-full-row.binlog +--exec $MYSQL_BINLOG --verbose suite/binlog/std_data/$binlog_file > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +create table raw_binlog_rows (txt varchar(1000)); +--eval load data local infile '$MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql' into table raw_binlog_rows columns terminated by '\n'; +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +--enable_query_log +--echo Verbose statements from : $binlog_file +select txt from raw_binlog_rows where txt like '###%'; +drop table raw_binlog_rows; + +--disable_query_log +--let $binlog_file=update-partial-row.binlog +--exec $MYSQL_BINLOG --verbose suite/binlog/std_data/$binlog_file > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +create table raw_binlog_rows (txt varchar(1000)); +--eval load data local infile '$MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql' into table raw_binlog_rows columns terminated by '\n'; +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +--enable_query_log +--echo Verbose statements from : $binlog_file +select txt from raw_binlog_rows where txt like '###%'; +drop table raw_binlog_rows; + +--disable_query_log +--let $binlog_file=update-full-row.binlog +--exec $MYSQL_BINLOG --verbose suite/binlog/std_data/$binlog_file > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +create table raw_binlog_rows (txt varchar(1000)); +--eval load data local infile '$MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql' into table raw_binlog_rows columns terminated by '\n'; +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql +--enable_query_log +--echo Verbose statements from : $binlog_file +select txt from raw_binlog_rows where txt like '###%'; +drop table raw_binlog_rows; diff --git a/sql/log_event.cc b/sql/log_event.cc index d7921ad3c27..ae7c4335f59 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1852,6 +1852,7 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, { const uchar *value0= value; const uchar *null_bits= value; + uint null_bit_index= 0; char typestr[64]= ""; value+= (m_width + 7) / 8; @@ -1860,7 +1861,8 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, for (size_t i= 0; i < td->size(); i ++) { - int is_null= (null_bits[i / 8] >> (i % 8)) & 0x01; + int is_null= (null_bits[null_bit_index / 8] + >> (null_bit_index % 8)) & 0x01; if (bitmap_is_set(cols_bitmap, i) == 0) continue; @@ -1897,6 +1899,8 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, } my_b_printf(file, "\n"); + + null_bit_index++; } return value - value0; } From eded60737d6bd233c50c4f68e76432154f768363 Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Fri, 9 Oct 2009 11:30:40 +0200 Subject: [PATCH 14/24] Bug#42846: wrong result returned for range scan when using covering index When two range predicates were combined under an OR predicate, the algorithm tried to merge overlapping ranges into one. But the case when a range overlapped several other ranges was not handled. This lead to 1) ranges overlapping, which gave repeated results and 2) a range that overlapped several other ranges was cut off. Fixed by 1) Making sure that a range got an upper bound equal to the next range with a greater minimum. 2) Removing a continue statement --- mysql-test/r/group_min_max.result | 6 +- mysql-test/r/range.result | 179 ++++++++++++++++++++++++++++++ mysql-test/t/range.test | 125 +++++++++++++++++++++ sql/opt_range.cc | 75 ++++++++++++- 4 files changed, 379 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index ac9a53ca238..620f5dc19ec 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -876,10 +876,10 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range NULL idx_t1_1 163 NULL 17 Using where; Using index for group-by explain select a1,a2,b, max(c) from t1 where (c > 'b1') or (c <= 'g1') group by a1,a2,b; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range NULL idx_t1_1 147 NULL 17 Using where; Using index for group-by +1 SIMPLE t1 range NULL idx_t1_1 163 NULL 17 Using where; Using index for group-by explain select a1,a2,b,min(c),max(c) from t1 where (c > 'b1') or (c <= 'g1') group by a1,a2,b; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range NULL idx_t1_1 147 NULL 17 Using where; Using index for group-by +1 SIMPLE t1 range NULL idx_t1_1 163 NULL 17 Using where; Using index for group-by explain select a1,a2,b,min(c),max(c) from t1 where (c > 'b111') and (c <= 'g112') group by a1,a2,b; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range NULL idx_t1_1 163 NULL 17 Using where; Using index for group-by @@ -924,7 +924,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 range NULL idx_t2_1 163 NULL # Using where; Using index for group-by explain select a1,a2,b, max(c) from t2 where (c > 'b1') or (c <= 'g1') group by a1,a2,b; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 range NULL idx_t2_1 146 NULL # Using where; Using index for group-by +1 SIMPLE t2 range NULL idx_t2_1 163 NULL # Using where; Using index for group-by explain select a1,a2,b,min(c),max(c) from t2 where (c > 'b1') or (c <= 'g1') group by a1,a2,b; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 range NULL idx_t2_1 163 NULL # Using where; Using index for group-by diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index cc5e8d2be96..c98a7696ea6 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -1219,3 +1219,182 @@ explain select * from t2 where a=1000 and b<11; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref a a 5 const 502 Using where drop table t1, t2; +CREATE TABLE t1( a INT, b INT, KEY( a, b ) ); +CREATE TABLE t2( a INT, b INT, KEY( a, b ) ); +CREATE TABLE t3( a INT, b INT, KEY( a, b ) ); +INSERT INTO t1( a, b ) +VALUES (0, 1), (1, 2), (1, 4), (2, 3), (5, 0), (9, 7); +INSERT INTO t2( a, b ) +VALUES ( 1, 1), ( 2, 1), ( 3, 1), ( 4, 1), ( 5, 1), +( 6, 1), ( 7, 1), ( 8, 1), ( 9, 1), (10, 1), +(11, 1), (12, 1), (13, 1), (14, 1), (15, 1), +(16, 1), (17, 1), (18, 1), (19, 1), (20, 1); +INSERT INTO t2 SELECT a, 2 FROM t2 WHERE b = 1; +INSERT INTO t2 SELECT a, 3 FROM t2 WHERE b = 1; +INSERT INTO t2 SELECT -1, -1 FROM t2; +INSERT INTO t2 SELECT -1, -1 FROM t2; +INSERT INTO t2 SELECT -1, -1 FROM t2; +INSERT INTO t3 +VALUES (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), +(6, 0), (7, 0), (8, 0), (9, 0), (10, 0); +INSERT INTO t3 SELECT * FROM t3 WHERE a = 10; +INSERT INTO t3 SELECT * FROM t3 WHERE a = 10; +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 < a AND b = 3 OR +3 <= a; +a b +5 0 +9 7 +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 < a AND b = 3 OR +3 <= a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 <= a AND b = 3 OR +3 <= a; +a b +5 0 +9 7 +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 <= a AND b = 3 OR +3 <= a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 4 Using where; Using index +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +5 <= a AND b = 3 OR +3 <= a; +a b +5 0 +9 7 +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +5 <= a AND b = 3 OR +3 <= a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +3 <= a; +a b +5 0 +9 7 +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +3 <= a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 1 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; +a b +1 1 +2 1 +3 1 +4 1 +5 1 +6 1 +7 1 +8 1 +9 1 +10 1 +11 1 +12 1 +13 1 +14 1 +15 1 +15 3 +16 1 +16 3 +17 1 +17 3 +18 1 +18 3 +19 1 +19 3 +20 1 +EXPLAIN +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 1 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range a a 10 NULL 50 Using where; Using index +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 2 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; +a b +1 1 +2 1 +3 1 +4 1 +5 1 +5 2 +6 1 +6 2 +7 1 +7 2 +8 1 +8 2 +9 1 +9 2 +10 1 +11 1 +12 1 +13 1 +14 1 +15 1 +15 3 +16 1 +16 3 +17 1 +17 3 +18 1 +18 3 +19 1 +19 3 +20 1 +EXPLAIN +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 2 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range a a 10 NULL 50 Using where; Using index +SELECT * FROM t3 WHERE +5 <= a AND a < 10 AND b = 3 OR +a < 5 OR +a < 10; +a b +1 0 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 0 +9 0 +EXPLAIN +SELECT * FROM t3 WHERE +5 <= a AND a < 10 AND b = 3 OR +a < 5 OR +a < 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t3 range a a 5 NULL 8 Using where; Using index +DROP TABLE t1, t2, t3; diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index e1411e7fd46..dc119b6a77e 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -1046,3 +1046,128 @@ explain select * from t2 where a=1000 and b<11; drop table t1, t2; +# +# Bug#42846: wrong result returned for range scan when using covering index +# +CREATE TABLE t1( a INT, b INT, KEY( a, b ) ); + +CREATE TABLE t2( a INT, b INT, KEY( a, b ) ); + +CREATE TABLE t3( a INT, b INT, KEY( a, b ) ); + +INSERT INTO t1( a, b ) +VALUES (0, 1), (1, 2), (1, 4), (2, 3), (5, 0), (9, 7); + +INSERT INTO t2( a, b ) +VALUES ( 1, 1), ( 2, 1), ( 3, 1), ( 4, 1), ( 5, 1), + ( 6, 1), ( 7, 1), ( 8, 1), ( 9, 1), (10, 1), + (11, 1), (12, 1), (13, 1), (14, 1), (15, 1), + (16, 1), (17, 1), (18, 1), (19, 1), (20, 1); + +INSERT INTO t2 SELECT a, 2 FROM t2 WHERE b = 1; +INSERT INTO t2 SELECT a, 3 FROM t2 WHERE b = 1; + +# To make range scan compelling to the optimizer +INSERT INTO t2 SELECT -1, -1 FROM t2; +INSERT INTO t2 SELECT -1, -1 FROM t2; +INSERT INTO t2 SELECT -1, -1 FROM t2; + +INSERT INTO t3 +VALUES (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), + (6, 0), (7, 0), (8, 0), (9, 0), (10, 0); + +# To make range scan compelling to the optimizer +INSERT INTO t3 SELECT * FROM t3 WHERE a = 10; +INSERT INTO t3 SELECT * FROM t3 WHERE a = 10; + + +# +# Problem#1 Test queries. Will give missing results unless Problem#1 is fixed. +# With one exception, they are independent of Problem#2. +# +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 < a AND b = 3 OR +3 <= a; + +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 < a AND b = 3 OR +3 <= a; + +# Query below: Tests both Problem#1 and Problem#2 (EXPLAIN differs as well) +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 <= a AND b = 3 OR +3 <= a; + +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a < 5 OR +5 <= a AND b = 3 OR +3 <= a; + +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +5 <= a AND b = 3 OR +3 <= a; + +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +5 <= a AND b = 3 OR +3 <= a; + +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +3 <= a; + +EXPLAIN +SELECT * FROM t1 WHERE +3 <= a AND a <= 5 OR +3 <= a; + +# +# Problem#2 Test queries. +# These queries will give missing results if Problem#1 is fixed. +# But Problem#1 also hides this bug. +# +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 1 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; + +EXPLAIN +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 1 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; + +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 2 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; + +EXPLAIN +SELECT * FROM t2 WHERE +5 <= a AND a < 10 AND b = 2 OR +15 <= a AND a < 20 AND b = 3 +OR +1 <= a AND b = 1; + +SELECT * FROM t3 WHERE +5 <= a AND a < 10 AND b = 3 OR +a < 5 OR +a < 10; + +EXPLAIN +SELECT * FROM t3 WHERE +5 <= a AND a < 10 AND b = 3 OR +a < 5 OR +a < 10; + +DROP TABLE t1, t2, t3; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 1b1d948b3b9..119f90bc97a 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -6512,6 +6512,63 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1) } +/** + Combine two range expression under a common OR. On a logical level, the + transformation is key_or( expr1, expr2 ) => expr1 OR expr2. + + Both expressions are assumed to be in the SEL_ARG format. In a logic sense, + theformat is reminiscent of DNF, since an expression such as the following + + ( 1 < kp1 < 10 AND p1 ) OR ( 10 <= kp2 < 20 AND p2 ) + + where there is a key consisting of keyparts ( kp1, kp2, ..., kpn ) and p1 + and p2 are valid SEL_ARG expressions over keyparts kp2 ... kpn, is a valid + SEL_ARG condition. The disjuncts appear ordered by the minimum endpoint of + the first range and ranges must not overlap. It follows that they are also + ordered by maximum endpoints. Thus + + ( 1 < kp1 <= 2 AND ( kp2 = 2 OR kp2 = 3 ) ) OR kp1 = 3 + + Is a a valid SER_ARG expression for a key of at least 2 keyparts. + + For simplicity, we will assume that expr2 is a single range predicate, + i.e. on the form ( a < x < b AND ... ). It is easy to generalize to a + disjunction of several predicates by subsequently call key_or for each + disjunct. + + The algorithm iterates over each disjunct of expr1, and for each disjunct + where the first keypart's range overlaps with the first keypart's range in + expr2: + + If the predicates are equal for the rest of the keyparts, or if there are + no more, the range in expr2 has its endpoints copied in, and the SEL_ARG + node in expr2 is deallocated. If more ranges became connected in expr1, the + surplus is also dealocated. If they differ, two ranges are created. + + - The range leading up to the overlap. Empty if endpoints are equal. + + - The overlapping sub-range. May be the entire range if they are equal. + + Finally, there may be one more range if expr2's first keypart's range has a + greater maximum endpoint than the last range in expr1. + + For the overlapping sub-range, we recursively call key_or. Thus in order to + compute key_or of + + (1) ( 1 < kp1 < 10 AND 1 < kp2 < 10 ) + + (2) ( 2 < kp1 < 20 AND 4 < kp2 < 20 ) + + We create the ranges 1 < kp <= 2, 2 < kp1 < 10, 10 <= kp1 < 20. For the + first one, we simply hook on the condition for the second keypart from (1) + : 1 < kp2 < 10. For the second range 2 < kp1 < 10, key_or( 1 < kp2 < 10, 4 + < kp2 < 20 ) is called, yielding 1 < kp2 < 20. For the last range, we reuse + the range 4 < kp2 < 20 from (2) for the second keypart. The result is thus + + ( 1 < kp1 <= 2 AND 1 < kp2 < 10 ) OR + ( 2 < kp1 < 10 AND 1 < kp2 < 20 ) OR + ( 10 <= kp1 < 20 AND 4 < kp2 < 20 ) +*/ static SEL_ARG * key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) { @@ -6663,7 +6720,21 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) key1=key1->tree_delete(save); } last->copy_min(tmp); - if (last->copy_min(key2) || last->copy_max(key2)) + bool full_range= last->copy_min(key2); + if (!full_range) + { + if (last->next && key2->cmp_max_to_min(last->next) >= 0) + { + last->max_value= last->next->min_value; + if (last->next->min_flag & NEAR_MIN) + last->max_flag&= ~NEAR_MAX; + else + last->max_flag|= NEAR_MAX; + } + else + full_range= last->copy_max(key2); + } + if (full_range) { // Full range key1->free_tree(); for (; key2 ; key2=key2->next) @@ -6673,8 +6744,6 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) return 0; } } - key2=key2->next; - continue; } if (cmp >= 0 && tmp->cmp_min_to_min(key2) < 0) From fa548cd0ffe4a029db71c27d783b8cbcd642c50f Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 9 Oct 2009 16:12:01 +0200 Subject: [PATCH 15/24] Bug#46922 post push update Disable the test when it will not hit the open_files_limit --- mysql-test/t/partition_open_files_limit.test | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mysql-test/t/partition_open_files_limit.test b/mysql-test/t/partition_open_files_limit.test index 92a9b18b573..e62ebd0ade7 100644 --- a/mysql-test/t/partition_open_files_limit.test +++ b/mysql-test/t/partition_open_files_limit.test @@ -4,6 +4,13 @@ DROP TABLE IF EXISTS `t1`; --enable_warnings +# On some platforms the lowest possible open_files_limit is too high... +let $max_open_files_limit= `SELECT @@open_files_limit > 511`; +if ($max_open_files_limit) +{ + skip Need open_files_limit to be lower than 512; +} + # --echo # Bug#46922: crash when adding partitions and open_files_limit is reached # From 858713edd376ccb7eaf4826732869be8e71d0190 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Fri, 9 Oct 2009 21:16:29 +0500 Subject: [PATCH 16/24] BUG#47073 - valgrind errs, corruption,failed repair of partition, low myisam_sort_buffer_size Repair by sort (default) or parallel repair of a MyISAM table (doesn't matter partitioned or not) as well as bulk inserts and enable indexes some times didn't failover to repair with key cache. The problem was that after unsuccessful attempt, data file was closed. Whereas repair with key cache requires open data file. Fixed by reopening data file. Also fixed a valgrind warning, which may appear during repair by sort or parallel repair with certain myisam_sort_buffer_size number of rows and length of an index entry (very dependent). --- mysql-test/r/myisam.result | 28 ++++++++++++++++++++++++++++ mysql-test/t/myisam.test | 28 ++++++++++++++++++++++++++++ storage/myisam/ha_myisam.cc | 16 ---------------- storage/myisam/mi_check.c | 10 ++++++---- storage/myisam/sort.c | 4 ++++ 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 58e2e451a0d..df97e96c334 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -2271,4 +2271,32 @@ checksum table t3; Table Checksum test.t3 326284887 drop table t1,t2,t3; +CREATE TABLE t1(a INT, b CHAR(10), KEY(a), KEY(b)); +INSERT INTO t1 VALUES(1,'0'),(2,'0'),(3,'0'),(4,'0'),(5,'0'), +(6,'0'),(7,'0'); +INSERT INTO t1 SELECT a+10,b FROM t1; +INSERT INTO t1 SELECT a+20,b FROM t1; +INSERT INTO t1 SELECT a+40,b FROM t1; +INSERT INTO t1 SELECT a+80,b FROM t1; +INSERT INTO t1 SELECT a+160,b FROM t1; +INSERT INTO t1 SELECT a+320,b FROM t1; +INSERT INTO t1 SELECT a+640,b FROM t1; +INSERT INTO t1 SELECT a+1280,b FROM t1; +INSERT INTO t1 SELECT a+2560,b FROM t1; +INSERT INTO t1 SELECT a+5120,b FROM t1; +SET myisam_sort_buffer_size=4; +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair error myisam_sort_buffer_size is too small +test.t1 repair warning Number of rows changed from 0 to 7168 +test.t1 repair status OK +SET myisam_repair_threads=2; +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair error myisam_sort_buffer_size is too small +test.t1 repair warning Number of rows changed from # to 7168 +test.t1 repair status OK +SET myisam_repair_threads=@@global.myisam_repair_threads; +SET myisam_sort_buffer_size=@@global.myisam_sort_buffer_size; +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 5de7c997a24..faeb5ee686a 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -1518,5 +1518,33 @@ CREATE TABLE t3 select * from t1; checksum table t3; drop table t1,t2,t3; + +# +# BUG#47073 - valgrind errs, corruption,failed repair of partition, +# low myisam_sort_buffer_size +# +CREATE TABLE t1(a INT, b CHAR(10), KEY(a), KEY(b)); +INSERT INTO t1 VALUES(1,'0'),(2,'0'),(3,'0'),(4,'0'),(5,'0'), + (6,'0'),(7,'0'); +INSERT INTO t1 SELECT a+10,b FROM t1; +INSERT INTO t1 SELECT a+20,b FROM t1; +INSERT INTO t1 SELECT a+40,b FROM t1; +INSERT INTO t1 SELECT a+80,b FROM t1; +INSERT INTO t1 SELECT a+160,b FROM t1; +INSERT INTO t1 SELECT a+320,b FROM t1; +INSERT INTO t1 SELECT a+640,b FROM t1; +INSERT INTO t1 SELECT a+1280,b FROM t1; +INSERT INTO t1 SELECT a+2560,b FROM t1; +INSERT INTO t1 SELECT a+5120,b FROM t1; +SET myisam_sort_buffer_size=4; +REPAIR TABLE t1; +SET myisam_repair_threads=2; +# May report different values depending on threads activity. +--replace_regex /changed from [0-9]+/changed from #/ +REPAIR TABLE t1; +SET myisam_repair_threads=@@global.myisam_repair_threads; +SET myisam_sort_buffer_size=@@global.myisam_sort_buffer_size; +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 5198e685817..aa9a2eeb77a 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1087,22 +1087,6 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool do_optimize) ha_rows rows= file->state->records; DBUG_ENTER("ha_myisam::repair"); - /* - Normally this method is entered with a properly opened table. If the - repair fails, it can be repeated with more elaborate options. Under - special circumstances it can happen that a repair fails so that it - closed the data file and cannot re-open it. In this case file->dfile - is set to -1. We must not try another repair without an open data - file. (Bug #25289) - */ - if (file->dfile == -1) - { - sql_print_information("Retrying repair of: '%s' failed. " - "Please try REPAIR EXTENDED or myisamchk", - table->s->path.str); - DBUG_RETURN(HA_ADMIN_FAILED); - } - param.db_name= table->s->db.str; param.table_name= table->alias; param.tmpfile_createflag = O_RDWR | O_TRUNC; diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index 1c33ffa90f5..8f7b1399aa2 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -2561,8 +2561,9 @@ err: VOID(my_close(new_file,MYF(0))); VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks, MYF(MY_WME))); - if (info->dfile == new_file) - info->dfile= -1; + if (info->dfile == new_file) /* Retry with key cache */ + if (unlikely(mi_open_datafile(info, share, name, -1))) + param->retry_repair= 0; /* Safety */ } mi_mark_crashed_on_repair(info); } @@ -3095,8 +3096,9 @@ err: VOID(my_close(new_file,MYF(0))); VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks, MYF(MY_WME))); - if (info->dfile == new_file) - info->dfile= -1; + if (info->dfile == new_file) /* Retry with key cache */ + if (unlikely(mi_open_datafile(info, share, name, -1))) + param->retry_repair= 0; /* Safety */ } mi_mark_crashed_on_repair(info); } diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c index f31edbe1249..fb16af9cddf 100644 --- a/storage/myisam/sort.c +++ b/storage/myisam/sort.c @@ -788,7 +788,11 @@ static int NEAR_F merge_many_buff(MI_SORT_PARAM *info, uint keys, cleanup: close_cached_file(to_file); /* This holds old result */ if (to_file == t_file) + { *t_file=t_file2; /* Copy result file */ + t_file->current_pos= &t_file->write_pos; + t_file->current_end= &t_file->write_end; + } DBUG_RETURN(*maxbuffer >= MERGEBUFF2); /* Return 1 if interrupted */ } /* merge_many_buff */ From 3b02f76aaf17d423f70cb817a601ff61bdbea024 Mon Sep 17 00:00:00 2001 From: V Narayanan Date: Mon, 12 Oct 2009 13:13:15 +0530 Subject: [PATCH 17/24] Bug#46448 trailing spaces are not ignored when user collation maps space != 0x20 In MySQL when the mapping for space is changed to something other than 0x20 by defining a different collation, then space is not ignored when comparing two strings. This was happening because the function that performs the comparison of two strings while ignoring ending spaces, was comparing the collation value of a space with the ascii value of the ' ' character. This should be changed to do comparison between the collated values. --- mysql-test/r/ctype_ldml.result | 8 ++ mysql-test/std_data/Index.xml | 13 ++++ mysql-test/std_data/latin1.xml | 135 +++++++++++++++++++++++++++++++++ mysql-test/t/ctype_ldml.test | 5 ++ strings/ctype-simple.c | 4 +- 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 mysql-test/std_data/latin1.xml diff --git a/mysql-test/r/ctype_ldml.result b/mysql-test/r/ctype_ldml.result index 711921eb526..fe870f82743 100644 --- a/mysql-test/r/ctype_ldml.result +++ b/mysql-test/r/ctype_ldml.result @@ -321,3 +321,11 @@ Vv Xx YyÝýỲỳỴỵỶỷỸỹ drop table t1; +Bug#46448 trailing spaces are not ignored when user collation maps space != 0x20 +set names latin1; +show collation like 'latin1_test'; +Collation Charset Id Default Compiled Sortlen +latin1_test latin1 99 Yes 1 +select "foo" = "foo " collate latin1_test; +"foo" = "foo " collate latin1_test +1 diff --git a/mysql-test/std_data/Index.xml b/mysql-test/std_data/Index.xml index 988dddcc68a..3dc647d8195 100644 --- a/mysql-test/std_data/Index.xml +++ b/mysql-test/std_data/Index.xml @@ -68,4 +68,17 @@ + + Western + cp1252 West European + csisolatin1 + iso-8859-1 + iso-ir-100 + iso_8859-1 + iso_8859-1:1987 + l1 + latin1 + + + diff --git a/mysql-test/std_data/latin1.xml b/mysql-test/std_data/latin1.xml new file mode 100644 index 00000000000..42b4342c1ae --- /dev/null +++ b/mysql-test/std_data/latin1.xml @@ -0,0 +1,135 @@ + + + + + + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + + 00 + 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20 + 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 + 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 + 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10 + 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01 + 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10 + 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02 + 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20 + 10 00 10 02 10 10 10 10 10 10 01 10 01 00 01 00 + 00 10 10 10 10 10 10 10 10 10 02 10 02 00 02 01 + 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 + 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 + 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 + 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02 + 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 + 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02 + + + + + + + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F + 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F + 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F + 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F + 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F + 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F + 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F + 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F + A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF + B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF + E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF + F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF + E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF + F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF + + + + + + + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F + 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F + 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F + 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F + 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F + 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F + A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF + B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF + C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF + D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF + C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF + D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF + + + + + + + 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000A 000B 000C 000D 000E 000F + 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001A 001B 001C 001D 001E 001F + 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002A 002B 002C 002D 002E 002F + 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003A 003B 003C 003D 003E 003F + 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004A 004B 004C 004D 004E 004F + 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005A 005B 005C 005D 005E 005F + 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006A 006B 006C 006D 006E 006F + 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007A 007B 007C 007D 007E 007F + 20AC 0081 201A 0192 201E 2026 2020 2021 02C6 2030 0160 2039 0152 008D 017D 008F + 0090 2018 2019 201C 201D 2022 2013 2014 02DC 2122 0161 203A 0153 009D 017E 0178 + 00A0 00A1 00A2 00A3 00A4 00A5 00A6 00A7 00A8 00A9 00AA 00AB 00AC 00AD 00AE 00AF + 00B0 00B1 00B2 00B3 00B4 00B5 00B6 00B7 00B8 00B9 00BA 00BB 00BC 00BD 00BE 00BF + 00C0 00C1 00C2 00C3 00C4 00C5 00C6 00C7 00C8 00C9 00CA 00CB 00CC 00CD 00CE 00CF + 00D0 00D1 00D2 00D3 00D4 00D5 00D6 00D7 00D8 00D9 00DA 00DB 00DC 00DD 00DE 00DF + 00E0 00E1 00E2 00E3 00E4 00E5 00E6 00E7 00E8 00E9 00EA 00EB 00EC 00ED 00EE 00EF + 00F0 00F1 00F2 00F3 00F4 00F5 00F6 00F7 00F8 00F9 00FA 00FB 00FC 00FD 00FE 00FF + + + + + + 00 01 02 03 37 2D 2E 2F 16 05 25 0B 0C 0D 0E 0F + 10 11 12 13 3C 3D 32 26 18 19 3F 27 1C 1D 1E 1F + 40 4F 7F 7B 5B 6C 50 7D 4D 5D 5C 4E 6B 60 4B 61 + F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 7A 5E 4C 7E 6E 6F + 7C C1 C2 C3 C4 C5 C6 C7 C8 C9 D1 D2 D3 D4 D5 D6 + D7 D8 D9 E2 E3 E4 E5 E6 E7 E8 E9 4A E0 5A 5F 6D + 79 81 82 83 84 85 86 87 88 89 91 92 93 94 95 96 + 97 98 99 A2 A3 A4 A5 A6 A7 A8 A9 C0 6A D0 A1 07 + 20 21 22 23 24 15 06 17 28 29 2A 2B 2C 09 0A 1B + 30 31 1A 33 34 35 36 08 38 39 3A 3B 04 14 3E E1 + 41 42 43 44 45 46 47 48 49 51 52 53 54 55 56 57 + 58 59 62 63 64 65 66 67 68 69 70 71 72 73 74 75 + 76 77 78 80 8A 8B 8C 8D 8E 8F 90 9A 9B 9C 9D 9E + 9F A0 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 + B8 B9 BA BB BC BD BE BF CA CB CC CD CE CF DA DB + DC DD DE DF EA EB EC ED EE EF FA FB FC FD FE FF + + + + + + diff --git a/mysql-test/t/ctype_ldml.test b/mysql-test/t/ctype_ldml.test index db9461bfbf7..bc04b93a935 100644 --- a/mysql-test/t/ctype_ldml.test +++ b/mysql-test/t/ctype_ldml.test @@ -86,3 +86,8 @@ select hex(c1) as h, c1 from t1 order by c1, h; select group_concat(hex(c1) order by hex(c1)) from t1 group by c1; select group_concat(c1 order by hex(c1) SEPARATOR '') from t1 group by c1; drop table t1; + +--echo Bug#46448 trailing spaces are not ignored when user collation maps space != 0x20 +set names latin1; +show collation like 'latin1_test'; +select "foo" = "foo " collate latin1_test; diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index 7de00025eda..4f3aaa6f668 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -185,8 +185,8 @@ int my_strnncollsp_simple(CHARSET_INFO * cs, const uchar *a, size_t a_length, } for (end= a + a_length-length; a < end ; a++) { - if (map[*a] != ' ') - return (map[*a] < ' ') ? -swap : swap; + if (map[*a] != map[' ']) + return (map[*a] < map[' ']) ? -swap : swap; } } return res; From db4e61acd41bf95bbb66c041db78196eca160088 Mon Sep 17 00:00:00 2001 From: V Narayanan Date: Mon, 12 Oct 2009 15:05:40 +0530 Subject: [PATCH 18/24] Bug#46448 trailing spaces are not ignored when user collation maps space != 0x20 Fixing copyright header in test collation file. --- mysql-test/std_data/latin1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/std_data/latin1.xml b/mysql-test/std_data/latin1.xml index 42b4342c1ae..a2dab731656 100644 --- a/mysql-test/std_data/latin1.xml +++ b/mysql-test/std_data/latin1.xml @@ -3,7 +3,7 @@ - Copyright (C) 2003 MySQL AB + Copyright (C) 2008 Sun Microsystems, Inc 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 7ad93d9b2ca5af173a983d5e6d93f1590db48fba Mon Sep 17 00:00:00 2001 From: V Narayanan Date: Mon, 12 Oct 2009 15:25:59 +0530 Subject: [PATCH 19/24] Bug#46448 trailing spaces are not ignored when user collation maps space != 0x20 changing year in copyright header to 2009. --- mysql-test/std_data/latin1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/std_data/latin1.xml b/mysql-test/std_data/latin1.xml index a2dab731656..458b1c34da1 100644 --- a/mysql-test/std_data/latin1.xml +++ b/mysql-test/std_data/latin1.xml @@ -3,7 +3,7 @@ - Copyright (C) 2008 Sun Microsystems, Inc + Copyright (C) 2009 Sun Microsystems, Inc 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 814a3cc2469e365064ab495d378d00faa9abd41e Mon Sep 17 00:00:00 2001 From: Date: Tue, 13 Oct 2009 10:26:15 +0800 Subject: [PATCH 20/24] Bug#45578: Test binlog_tmp_table fails ramdonly on PB2: Unknown table 't2' The bug has been closed. --- mysql-test/collections/default.experimental | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index 50c5a71e252..214b732e5cb 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -1,6 +1,5 @@ funcs_1.charset_collation_1 # depends on compile-time decisions main.plugin_load @solaris # Bug#42144 -binlog.binlog_tmp_table* # Bug#45578: Test binlog_tmp_table fails ramdonly on PB2: Unknown table 't2' main.ctype_gbk_binlog @solaris # Bug#46010: main.ctype_gbk_binlog fails sporadically : Table 't2' already exists rpl.rpl_row_create_table* # Bug#45576: rpl_row_create_table fails on PB2 rpl_ndb.rpl_ndb_log # Bug#38998 From 0ece5891a28d9e7f8387573a4c69f2c278edd225 Mon Sep 17 00:00:00 2001 From: Date: Wed, 14 Oct 2009 09:39:05 +0800 Subject: [PATCH 21/24] Bug#46640: output from mysqlbinlog command in 5.1 breaks replication The BINLOG statement was sharing too much code with the slave SQL thread, introduced with the patch for Bug#32407. This caused statements to be logged with the wrong server_id, the id stored inside the events of the BINLOG statement rather than the id of the running server. Fix by rearranging code a bit so that only relevant parts of the code are executed by the BINLOG statement, and the server_id of the server executing the statements will not be overrided by the server_id stored in the 'format description BINLOG statement'. --- mysql-test/extra/binlog_tests/binlog.test | 39 ++++++ .../suite/binlog/r/binlog_row_binlog.result | 24 ++++ .../suite/binlog/r/binlog_stm_binlog.result | 21 +++ sql/log_event.cc | 66 +++++----- sql/log_event_old.cc | 124 +++++++++--------- sql/slave.cc | 61 ++++----- sql/slave.h | 3 +- sql/sql_binlog.cc | 51 +++---- 8 files changed, 233 insertions(+), 156 deletions(-) diff --git a/mysql-test/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test index 5d898d41a54..b819996acb0 100644 --- a/mysql-test/extra/binlog_tests/binlog.test +++ b/mysql-test/extra/binlog_tests/binlog.test @@ -270,3 +270,42 @@ INSERT INTO test.t1 VALUES (1), (2); CREATE TABLE test.t2 SELECT * FROM test.t1; USE test; DROP TABLES t1, t2; + +# +# Bug#46640 +# This test verifies if the server_id stored in the "format +# description BINLOG statement" will override the server_id +# of the server executing the statements. +# + +connect (fresh,localhost,root,,test); +connection fresh; + +RESET MASTER; +CREATE TABLE t1 (a INT PRIMARY KEY); + +# Format description event, with server_id = 10; +BINLOG ' +3u9kSA8KAAAAZgAAAGoAAAABAAQANS4xLjM1LW1hcmlhLWJldGExLWRlYnVnLWxvZwAAAAAAAAAA +AAAAAAAAAAAAAAAAAADe72RIEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC +'; + +# What server_id is logged for a statement? Should be our own, not the +# one from the format description event. +INSERT INTO t1 VALUES (1); + +# INSERT INTO t1 VALUES (2), with server_id=20. Check that this is logged +# with our own server id, not the 20 from the BINLOG statement. +BINLOG ' +3u9kSBMUAAAAKQAAAJEBAAAAABoAAAAAAAAABHRlc3QAAnQxAAEDAAA= +3u9kSBcUAAAAIgAAALMBAAAQABoAAAAAAAEAAf/+AgAAAA== +'; + +# Show binlog events to check that server ids are correct. +--replace_column 1 # 2 # 5 # +--replace_regex /Server ver: .*, Binlog ver: .*/Server ver: #, Binlog ver: #/ /table_id: [0-9]+/table_id: #/ +SHOW BINLOG EVENTS; + +DROP TABLE t1; +disconnect fresh; + diff --git a/mysql-test/suite/binlog/r/binlog_row_binlog.result b/mysql-test/suite/binlog/r/binlog_row_binlog.result index f6b5392dbc8..4d32a4f4739 100644 --- a/mysql-test/suite/binlog/r/binlog_row_binlog.result +++ b/mysql-test/suite/binlog/r/binlog_row_binlog.result @@ -1309,3 +1309,27 @@ INSERT INTO test.t1 VALUES (1), (2); CREATE TABLE test.t2 SELECT * FROM test.t1; USE test; DROP TABLES t1, t2; +RESET MASTER; +CREATE TABLE t1 (a INT PRIMARY KEY); +BINLOG ' +3u9kSA8KAAAAZgAAAGoAAAABAAQANS4xLjM1LW1hcmlhLWJldGExLWRlYnVnLWxvZwAAAAAAAAAA +AAAAAAAAAAAAAAAAAADe72RIEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC +'; +INSERT INTO t1 VALUES (1); +BINLOG ' +3u9kSBMUAAAAKQAAAJEBAAAAABoAAAAAAAAABHRlc3QAAnQxAAEDAAA= +3u9kSBcUAAAAIgAAALMBAAAQABoAAAAAAAEAAf/+AgAAAA== +'; +SHOW BINLOG EVENTS; +Log_name Pos Event_type Server_id End_log_pos Info +# # Format_desc 1 # Server ver: #, Binlog ver: # +# # Query 1 # use `test`; CREATE TABLE t1 (a INT PRIMARY KEY) +# # Query 1 # BEGIN +# # Table_map 1 # table_id: # (test.t1) +# # Write_rows 1 # table_id: # flags: STMT_END_F +# # Query 1 # COMMIT +# # Query 1 # BEGIN +# # Table_map 1 # table_id: # (test.t1) +# # Write_rows 1 # table_id: # flags: STMT_END_F +# # Query 1 # COMMIT +DROP TABLE t1; diff --git a/mysql-test/suite/binlog/r/binlog_stm_binlog.result b/mysql-test/suite/binlog/r/binlog_stm_binlog.result index d05d3ccdb7a..eebcfceaa25 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_binlog.result +++ b/mysql-test/suite/binlog/r/binlog_stm_binlog.result @@ -784,3 +784,24 @@ INSERT INTO test.t1 VALUES (1), (2); CREATE TABLE test.t2 SELECT * FROM test.t1; USE test; DROP TABLES t1, t2; +RESET MASTER; +CREATE TABLE t1 (a INT PRIMARY KEY); +BINLOG ' +3u9kSA8KAAAAZgAAAGoAAAABAAQANS4xLjM1LW1hcmlhLWJldGExLWRlYnVnLWxvZwAAAAAAAAAA +AAAAAAAAAAAAAAAAAADe72RIEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC +'; +INSERT INTO t1 VALUES (1); +BINLOG ' +3u9kSBMUAAAAKQAAAJEBAAAAABoAAAAAAAAABHRlc3QAAnQxAAEDAAA= +3u9kSBcUAAAAIgAAALMBAAAQABoAAAAAAAEAAf/+AgAAAA== +'; +SHOW BINLOG EVENTS; +Log_name Pos Event_type Server_id End_log_pos Info +# # Format_desc 1 # Server ver: #, Binlog ver: # +# # Query 1 # use `test`; CREATE TABLE t1 (a INT PRIMARY KEY) +# # Query 1 # use `test`; INSERT INTO t1 VALUES (1) +# # Query 1 # BEGIN +# # Table_map 1 # table_id: # (test.t1) +# # Write_rows 1 # table_id: # flags: STMT_END_F +# # Query 1 # COMMIT +DROP TABLE t1; diff --git a/sql/log_event.cc b/sql/log_event.cc index ae7c4335f59..0e1500dac39 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3859,6 +3859,7 @@ bool Format_description_log_event::write(IO_CACHE* file) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) int Format_description_log_event::do_apply_event(Relay_log_info const *rli) { + int ret= 0; DBUG_ENTER("Format_description_log_event::do_apply_event"); #ifdef USING_TRANSACTIONS @@ -3900,17 +3901,21 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli) 0, then 96, then jump to first really asked event (which is >96). So this is ok. */ - DBUG_RETURN(Start_log_event_v3::do_apply_event(rli)); + ret= Start_log_event_v3::do_apply_event(rli); } - DBUG_RETURN(0); + + if (!ret) + { + /* Save the information describing this binlog */ + delete rli->relay_log.description_event_for_exec; + const_cast(rli)->relay_log.description_event_for_exec= this; + } + + DBUG_RETURN(ret); } int Format_description_log_event::do_update_pos(Relay_log_info *rli) { - /* save the information describing this binlog */ - delete rli->relay_log.description_event_for_exec; - rli->relay_log.description_event_for_exec= this; - if (server_id == (uint32) ::server_id) { /* @@ -7506,6 +7511,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) thd->reset_current_stmt_binlog_row_based(); const_cast(rli)->cleanup_context(thd, error); thd->is_slave_error= 1; + DBUG_RETURN(error); } /* This code would ideally be placed in do_update_pos() instead, but @@ -7534,6 +7540,14 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) const_cast(rli)->last_event_start_time= my_time(0); } + if (get_flags(STMT_END_F)) + if (error= rows_event_stmt_cleanup(rli, thd)) + rli->report(ERROR_LEVEL, error, + "Error in %s event: commit of row events failed, " + "table `%s`.`%s`", + get_type_str(), m_table->s->db.str, + m_table->s->table_name.str); + DBUG_RETURN(error); } @@ -7632,33 +7646,19 @@ Rows_log_event::do_update_pos(Relay_log_info *rli) if (get_flags(STMT_END_F)) { - if ((error= rows_event_stmt_cleanup(rli, thd)) == 0) - { - /* - Indicate that a statement is finished. - Step the group log position if we are not in a transaction, - otherwise increase the event log position. - */ - rli->stmt_done(log_pos, when); - - /* - Clear any errors pushed in thd->net.last_err* if for example "no key - found" (as this is allowed). This is a safety measure; apparently - those errors (e.g. when executing a Delete_rows_log_event of a - non-existing row, like in rpl_row_mystery22.test, - thd->net.last_error = "Can't find record in 't1'" and last_errno=1032) - do not become visible. We still prefer to wipe them out. - */ - thd->clear_error(); - } - else - { - rli->report(ERROR_LEVEL, error, - "Error in %s event: commit of row events failed, " - "table `%s`.`%s`", - get_type_str(), m_table->s->db.str, - m_table->s->table_name.str); - } + /* + Indicate that a statement is finished. + Step the group log position if we are not in a transaction, + otherwise increase the event log position. + */ + rli->stmt_done(log_pos, when); + /* + Clear any errors in thd->net.last_err*. It is not known if this is + needed or not. It is believed that any errors that may exist in + thd->net.last_err* are allowed. Examples of errors are "key not + found", which is produced in the test case rpl_row_conflicts.test + */ + thd->clear_error(); } else { diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index f14ebe49706..3389821a718 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1814,7 +1814,56 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) const_cast(rli)->last_event_start_time= my_time(0); } - DBUG_RETURN(0); + if (get_flags(STMT_END_F)) + { + /* + This is the end of a statement or transaction, so close (and + unlock) the tables we opened when processing the + Table_map_log_event starting the statement. + + OBSERVER. This will clear *all* mappings, not only those that + are open for the table. There is not good handle for on-close + actions for tables. + + NOTE. Even if we have no table ('table' == 0) we still need to be + here, so that we increase the group relay log position. If we didn't, we + could have a group relay log position which lags behind "forever" + (assume the last master's transaction is ignored by the slave because of + replicate-ignore rules). + */ + thd->binlog_flush_pending_rows_event(true); + + /* + If this event is not in a transaction, the call below will, if some + transactional storage engines are involved, commit the statement into + them and flush the pending event to binlog. + If this event is in a transaction, the call will do nothing, but a + Xid_log_event will come next which will, if some transactional engines + are involved, commit the transaction and flush the pending event to the + binlog. + */ + if (error= ha_autocommit_or_rollback(thd, 0)) + rli->report(ERROR_LEVEL, error, + "Error in %s event: commit of row events failed, " + "table `%s`.`%s`", + get_type_str(), m_table->s->db.str, + m_table->s->table_name.str); + + /* + Now what if this is not a transactional engine? we still need to + flush the pending event to the binlog; we did it with + thd->binlog_flush_pending_rows_event(). Note that we imitate + what is done for real queries: a call to + ha_autocommit_or_rollback() (sometimes only if involves a + transactional engine), and a call to be sure to have the pending + event flushed. + */ + + thd->reset_current_stmt_binlog_row_based(); + const_cast(rli)->cleanup_context(thd, 0); + } + + DBUG_RETURN(error); } @@ -1844,71 +1893,18 @@ Old_rows_log_event::do_update_pos(Relay_log_info *rli) if (get_flags(STMT_END_F)) { /* - This is the end of a statement or transaction, so close (and - unlock) the tables we opened when processing the - Table_map_log_event starting the statement. - - OBSERVER. This will clear *all* mappings, not only those that - are open for the table. There is not good handle for on-close - actions for tables. - - NOTE. Even if we have no table ('table' == 0) we still need to be - here, so that we increase the group relay log position. If we didn't, we - could have a group relay log position which lags behind "forever" - (assume the last master's transaction is ignored by the slave because of - replicate-ignore rules). - */ - thd->binlog_flush_pending_rows_event(true); - + Indicate that a statement is finished. + Step the group log position if we are not in a transaction, + otherwise increase the event log position. + */ + rli->stmt_done(log_pos, when); /* - If this event is not in a transaction, the call below will, if some - transactional storage engines are involved, commit the statement into - them and flush the pending event to binlog. - If this event is in a transaction, the call will do nothing, but a - Xid_log_event will come next which will, if some transactional engines - are involved, commit the transaction and flush the pending event to the - binlog. + Clear any errors in thd->net.last_err*. It is not known if this is + needed or not. It is believed that any errors that may exist in + thd->net.last_err* are allowed. Examples of errors are "key not + found", which is produced in the test case rpl_row_conflicts.test */ - error= ha_autocommit_or_rollback(thd, 0); - - /* - Now what if this is not a transactional engine? we still need to - flush the pending event to the binlog; we did it with - thd->binlog_flush_pending_rows_event(). Note that we imitate - what is done for real queries: a call to - ha_autocommit_or_rollback() (sometimes only if involves a - transactional engine), and a call to be sure to have the pending - event flushed. - */ - - thd->reset_current_stmt_binlog_row_based(); - rli->cleanup_context(thd, 0); - if (error == 0) - { - /* - Indicate that a statement is finished. - Step the group log position if we are not in a transaction, - otherwise increase the event log position. - */ - rli->stmt_done(log_pos, when); - - /* - Clear any errors pushed in thd->net.client_last_err* if for - example "no key found" (as this is allowed). This is a safety - measure; apparently those errors (e.g. when executing a - Delete_rows_log_event_old of a non-existing row, like in - rpl_row_mystery22.test, thd->net.last_error = "Can't - find record in 't1'" and last_errno=1032) do not become - visible. We still prefer to wipe them out. - */ - thd->clear_error(); - } - else - rli->report(ERROR_LEVEL, error, - "Error in %s event: commit of row events failed, " - "table `%s`.`%s`", - get_type_str(), m_table->s->db.str, - m_table->s->table_name.str); + thd->clear_error(); } else { diff --git a/sql/slave.cc b/sql/slave.cc index 82c9d035fd2..9718b54ea9e 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2082,8 +2082,7 @@ static int has_temporary_error(THD *thd) @retval 2 No error calling ev->apply_event(), but error calling ev->update_pos(). */ -int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli, - bool skip) +int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli) { int exec_res= 0; @@ -2128,38 +2127,34 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli, ev->when= my_time(0); ev->thd = thd; // because up to this point, ev->thd == 0 - if (skip) - { - int reason= ev->shall_skip(rli); - if (reason == Log_event::EVENT_SKIP_COUNT) - --rli->slave_skip_counter; - pthread_mutex_unlock(&rli->data_lock); - if (reason == Log_event::EVENT_SKIP_NOT) - exec_res= ev->apply_event(rli); -#ifndef DBUG_OFF - /* - This only prints information to the debug trace. - - TODO: Print an informational message to the error log? - */ - static const char *const explain[] = { - // EVENT_SKIP_NOT, - "not skipped", - // EVENT_SKIP_IGNORE, - "skipped because event should be ignored", - // EVENT_SKIP_COUNT - "skipped because event skip counter was non-zero" - }; - DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d", - thd->options & OPTION_BEGIN ? 1 : 0, - rli->get_flag(Relay_log_info::IN_STMT))); - DBUG_PRINT("skip_event", ("%s event was %s", - ev->get_type_str(), explain[reason])); -#endif - } - else + int reason= ev->shall_skip(rli); + if (reason == Log_event::EVENT_SKIP_COUNT) + --rli->slave_skip_counter; + pthread_mutex_unlock(&rli->data_lock); + if (reason == Log_event::EVENT_SKIP_NOT) exec_res= ev->apply_event(rli); +#ifndef DBUG_OFF + /* + This only prints information to the debug trace. + + TODO: Print an informational message to the error log? + */ + static const char *const explain[] = { + // EVENT_SKIP_NOT, + "not skipped", + // EVENT_SKIP_IGNORE, + "skipped because event should be ignored", + // EVENT_SKIP_COUNT + "skipped because event skip counter was non-zero" + }; + DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d", + thd->options & OPTION_BEGIN ? 1 : 0, + rli->get_flag(Relay_log_info::IN_STMT))); + DBUG_PRINT("skip_event", ("%s event was %s", + ev->get_type_str(), explain[reason])); +#endif + DBUG_PRINT("info", ("apply_event error = %d", exec_res)); if (exec_res == 0) { @@ -2278,7 +2273,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) delete ev; DBUG_RETURN(1); } - exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE); + exec_res= apply_event_and_update_pos(ev, thd, rli); /* Format_description_log_event should not be deleted because it will be diff --git a/sql/slave.h b/sql/slave.h index a44a7eed83e..f356d28b626 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -190,8 +190,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, void set_slave_thread_options(THD* thd); void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli); void rotate_relay_log(Master_info* mi); -int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli, - bool skip); +int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli); pthread_handler_t handle_slave_io(void *arg); pthread_handler_t handle_slave_sql(void *arg); diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 96e99b57e3c..ee51480411b 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -56,17 +56,20 @@ void mysql_client_binlog_statement(THD* thd) Format_description_event. */ my_bool have_fd_event= TRUE; - if (!thd->rli_fake) + int err; + Relay_log_info *rli; + rli= thd->rli_fake; + if (!rli) { - thd->rli_fake= new Relay_log_info; + rli= thd->rli_fake= new Relay_log_info; #ifdef HAVE_purify - thd->rli_fake->is_fake= TRUE; + rli->is_fake= TRUE; #endif have_fd_event= FALSE; } - if (thd->rli_fake && !thd->rli_fake->relay_log.description_event_for_exec) + if (rli && !rli->relay_log.description_event_for_exec) { - thd->rli_fake->relay_log.description_event_for_exec= + rli->relay_log.description_event_for_exec= new Format_description_log_event(4); have_fd_event= FALSE; } @@ -78,16 +81,16 @@ void mysql_client_binlog_statement(THD* thd) /* Out of memory check */ - if (!(thd->rli_fake && - thd->rli_fake->relay_log.description_event_for_exec && + if (!(rli && + rli->relay_log.description_event_for_exec && buf)) { my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */ goto end; } - thd->rli_fake->sql_thd= thd; - thd->rli_fake->no_storage= TRUE; + rli->sql_thd= thd; + rli->no_storage= TRUE; for (char const *strptr= thd->lex->comment.str ; strptr < thd->lex->comment.str + thd->lex->comment.length ; ) @@ -170,8 +173,7 @@ void mysql_client_binlog_statement(THD* thd) } ev= Log_event::read_log_event(bufptr, event_len, &error, - thd->rli_fake->relay_log. - description_event_for_exec); + rli->relay_log.description_event_for_exec); DBUG_PRINT("info",("binlog base64 err=%s", error)); if (!ev) @@ -209,18 +211,10 @@ void mysql_client_binlog_statement(THD* thd) reporting. */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - if (apply_event_and_update_pos(ev, thd, thd->rli_fake, FALSE)) - { - delete ev; - /* - TODO: Maybe a better error message since the BINLOG statement - now contains several events. - */ - my_error(ER_UNKNOWN_ERROR, MYF(0), "Error executing BINLOG statement"); - goto end; - } + err= ev->apply_event(rli); +#else + err= 0; #endif - /* Format_description_log_event should not be deleted because it will be used to read info about the relay log's format; it @@ -228,8 +222,17 @@ void mysql_client_binlog_statement(THD* thd) i.e. when this thread terminates. */ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT) - delete ev; + delete ev; ev= 0; + if (err) + { + /* + TODO: Maybe a better error message since the BINLOG statement + now contains several events. + */ + my_error(ER_UNKNOWN_ERROR, MYF(0), "Error executing BINLOG statement"); + goto end; + } } } @@ -238,7 +241,7 @@ void mysql_client_binlog_statement(THD* thd) my_ok(thd); end: - thd->rli_fake->clear_tables_to_lock(); + rli->clear_tables_to_lock(); my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); DBUG_VOID_RETURN; } From bf0aa2bd3460039a0b44d5c0f1411ac2ad47b310 Mon Sep 17 00:00:00 2001 From: Jorgen Loland Date: Wed, 14 Oct 2009 10:46:50 +0200 Subject: [PATCH 22/24] Bug#47280 - strange results from count(*) with order by multiple columns without where/group Simple SELECT with implicit grouping used to return many rows if the query was ordered by the aggregated column in the SELECT list. This was incorrect because queries with implicit grouping should only return a single record. The problem was that when JOIN:exec() decided if execution needed to handle grouping, it was assumed that sum_func_count==0 meant that there were no aggregate functions in the query. This assumption was not correct in JOIN::exec() because the aggregate functions might have been optimized away during JOIN::optimize(). The reason why queries without ordering behaved correctly was that sum_func_count is only recalculated if the optimizer chooses to use temporary tables (which it does in the ordered case). Hence, non-ordered queries were correctly treated as grouped. The fix for this bug was to remove the assumption that sum_func_count==0 means that there is no need for grouping. This was done by introducing variable "bool implicit_grouping" in the JOIN object. --- mysql-test/r/func_group.result | 44 +++++++++++++++++++++++++++++++ mysql-test/t/func_group.test | 48 ++++++++++++++++++++++++++++++++++ sql/opt_sum.cc | 3 ++- sql/sql_class.h | 27 ++++++++++++++++++- sql/sql_select.cc | 27 +++++++++++++++---- sql/sql_select.h | 15 ++++++++++- 6 files changed, 156 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 3d989ad1730..94147640cde 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -1477,3 +1477,47 @@ COUNT(*) SET SQL_MODE=default; DROP TABLE t1; End of 5.0 tests +# +# BUG#47280 - strange results from count(*) with order by multiple +# columns without where/group +# +# +# Initialize test +# +CREATE TABLE t1 ( +pk INT NOT NULL, +i INT, +PRIMARY KEY (pk) +); +INSERT INTO t1 VALUES (1,11),(2,12),(3,13); +# +# Start test +# All the following queries shall return 1 record +# + +# Masking all correct values {11...13} for column i in this result. +SELECT MAX(pk) as max, i +FROM t1 +ORDER BY max; +max i +3 # + +EXPLAIN +SELECT MAX(pk) as max, i +FROM t1 +ORDER BY max; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using temporary + +# Only 11 is correct for collumn i in this result +SELECT MAX(pk) as max, i +FROM t1 +WHERE pk<2 +ORDER BY max; +max i +1 11 +# +# Cleanup +# +DROP TABLE t1; +End of 5.1 tests diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index b0a3d0feb79..6e39795a5d6 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -1006,3 +1006,51 @@ DROP TABLE t1; ### --echo End of 5.0 tests + +--echo # +--echo # BUG#47280 - strange results from count(*) with order by multiple +--echo # columns without where/group +--echo # + +--echo # +--echo # Initialize test +--echo # + +CREATE TABLE t1 ( + pk INT NOT NULL, + i INT, + PRIMARY KEY (pk) +); +INSERT INTO t1 VALUES (1,11),(2,12),(3,13); + +--echo # +--echo # Start test +--echo # All the following queries shall return 1 record +--echo # + +--echo +--echo # Masking all correct values {11...13} for column i in this result. +--replace_column 2 # +SELECT MAX(pk) as max, i +FROM t1 +ORDER BY max; + +--echo +EXPLAIN +SELECT MAX(pk) as max, i +FROM t1 +ORDER BY max; + +--echo +--echo # Only 11 is correct for collumn i in this result +SELECT MAX(pk) as max, i +FROM t1 +WHERE pk<2 +ORDER BY max; + +--echo # +--echo # Cleanup +--echo # +DROP TABLE t1; + +--echo End of 5.1 tests diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 8e7265ba1ad..e009cf1ca9f 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -97,7 +97,8 @@ static ulonglong get_exact_record_count(TABLE_LIST *tables) @note This function is only called for queries with sum functions and no - GROUP BY part. + GROUP BY part. This means that the result set shall contain a single + row only @retval 0 no errors diff --git a/sql/sql_class.h b/sql/sql_class.h index 024a6587b43..922e960c805 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2647,7 +2647,32 @@ public: MI_COLUMNDEF *recinfo,*start_recinfo; KEY *keyinfo; ha_rows end_write_records; - uint field_count,sum_func_count,func_count; + /** + Number of normal fields in the query, including those referred to + from aggregate functions. Hence, "SELECT `field1`, + SUM(`field2`) from t1" sets this counter to 2. + + @see count_field_types + */ + uint field_count; + /** + Number of fields in the query that have functions. Includes both + aggregate functions (e.g., SUM) and non-aggregates (e.g., RAND). + Also counts functions referred to from aggregate functions, i.e., + "SELECT SUM(RAND())" sets this counter to 2. + + @see count_field_types + */ + uint func_count; + /** + Number of fields in the query that have aggregate functions. Note + that the optimizer may choose to optimize away these fields by + replacing them with constants, in which case sum_func_count will + need to be updated. + + @see opt_sum_query, count_field_types + */ + uint sum_func_count; uint hidden_field_count; uint group_parts,group_length,group_null_parts; uint quick_group; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9dacb2c2ce4..02e52e0c518 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -644,8 +644,11 @@ JOIN::prepare(Item ***rref_pointer_array, this->group= group_list != 0; unit= unit_arg; + if (tmp_table_param.sum_func_count && !group_list) + implicit_grouping= TRUE; + #ifdef RESTRICTED_GROUP - if (sum_func_count && !group_list && (func_count || field_count)) + if (implicit_grouping) { my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0)); goto err; @@ -881,15 +884,23 @@ JOIN::optimize() } #endif - /* Optimize count(*), min() and max() */ - if (tables_list && tmp_table_param.sum_func_count && ! group_list) + /* + Try to optimize count(*), min() and max() to const fields if + there is implicit grouping (aggregate functions but no + group_list). In this case, the result set shall only contain one + row. + */ + if (tables_list && implicit_grouping) { int res; /* opt_sum_query() returns HA_ERR_KEY_NOT_FOUND if no rows match to the WHERE conditions, - or 1 if all items were resolved, + or 1 if all items were resolved (optimized away), or 0, or an error number HA_ERR_... + + If all items were resolved by opt_sum_query, there is no need to + open any tables. */ if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds))) { @@ -2024,7 +2035,7 @@ JOIN::exec() count_field_types(select_lex, &curr_join->tmp_table_param, *curr_all_fields, 0); - if (curr_join->group || curr_join->tmp_table_param.sum_func_count || + if (curr_join->group || curr_join->implicit_grouping || (procedure && (procedure->flags & PROC_GROUP))) { if (make_group_fields(this, curr_join)) @@ -10811,6 +10822,12 @@ Next_select_func setup_end_select_func(JOIN *join) } else { + /* + Choose method for presenting result to user. Use end_send_group + if the query requires grouping (has a GROUP BY clause and/or one or + more aggregate functions). Use end_send if the query should not + be grouped. + */ if ((join->sort_and_group || (join->procedure && join->procedure->flags & PROC_GROUP)) && !tmp_tbl->precomputed_group_by) diff --git a/sql/sql_select.h b/sql/sql_select.h index 3f06b402638..92356a4c96f 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -278,7 +278,14 @@ public: TABLE **table,**all_tables,*sort_by_table; uint tables,const_tables; uint send_group_parts; - bool sort_and_group,first_record,full_join,group, no_field_update; + /** + Indicates that grouping will be performed on the result set during + query execution. This field belongs to query execution. + + @see make_group_fields, alloc_group_fields, JOIN::exec + */ + bool sort_and_group; + bool first_record,full_join,group, no_field_update; bool do_send_rows; /** TRUE when we want to resume nested loop iterations when @@ -428,6 +435,7 @@ public: tables= 0; const_tables= 0; join_list= 0; + implicit_grouping= FALSE; sort_and_group= 0; first_record= 0; do_send_rows= 1; @@ -533,6 +541,11 @@ public: select_lex == unit->fake_select_lex)); } private: + /** + TRUE if the query contains an aggregate function but has no GROUP + BY clause. + */ + bool implicit_grouping; bool make_simple_join(JOIN *join, TABLE *tmp_table); }; From adab17b9e6f68aef9c5f30b4d95b26e498a304a4 Mon Sep 17 00:00:00 2001 From: He Zhenxing Date: Wed, 14 Oct 2009 21:25:11 +0800 Subject: [PATCH 23/24] Attempt to fix Windows testcase output issue --- .../r/binlog_row_mysqlbinlog_verbose.result | 16 ++++++++-------- .../binlog/t/binlog_row_mysqlbinlog_verbose.test | 12 ++++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result b/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result index f1a3fafc498..2687b21213a 100644 --- a/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result +++ b/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result @@ -1,6 +1,6 @@ Verbose statements from : write-partial-row.binlog -select txt from raw_binlog_rows where txt like '###%'; -txt +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; +stmt ### INSERT INTO mysql.ndb_apply_status ### SET ### @1=1 @@ -37,8 +37,8 @@ txt ### @1=2 drop table raw_binlog_rows; Verbose statements from : write-full-row.binlog -select txt from raw_binlog_rows where txt like '###%'; -txt +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; +stmt ### INSERT INTO mysql.ndb_apply_status ### SET ### @1=2 @@ -76,8 +76,8 @@ txt ### @1=2 drop table raw_binlog_rows; Verbose statements from : update-partial-row.binlog -select txt from raw_binlog_rows where txt like '###%'; -txt +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; +stmt ### INSERT INTO mysql.ndb_apply_status ### SET ### @1=3 @@ -117,8 +117,8 @@ txt ### @1=2 drop table raw_binlog_rows; Verbose statements from : update-full-row.binlog -select txt from raw_binlog_rows where txt like '###%'; -txt +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; +stmt ### INSERT INTO mysql.ndb_apply_status ### SET ### @1=4 diff --git a/mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test b/mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test index b68e56d0215..42d92e1a44d 100644 --- a/mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test +++ b/mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test @@ -45,7 +45,8 @@ create table raw_binlog_rows (txt varchar(1000)); --remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql --enable_query_log --echo Verbose statements from : $binlog_file -select txt from raw_binlog_rows where txt like '###%'; +# Output --verbose lines, with extra Windows CR's trimmed +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; drop table raw_binlog_rows; --disable_query_log @@ -56,7 +57,8 @@ create table raw_binlog_rows (txt varchar(1000)); --remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql --enable_query_log --echo Verbose statements from : $binlog_file -select txt from raw_binlog_rows where txt like '###%'; +# Output --verbose lines, with extra Windows CR's trimmed +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; drop table raw_binlog_rows; --disable_query_log @@ -67,7 +69,8 @@ create table raw_binlog_rows (txt varchar(1000)); --remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql --enable_query_log --echo Verbose statements from : $binlog_file -select txt from raw_binlog_rows where txt like '###%'; +# Output --verbose lines, with extra Windows CR's trimmed +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; drop table raw_binlog_rows; --disable_query_log @@ -78,5 +81,6 @@ create table raw_binlog_rows (txt varchar(1000)); --remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql --enable_query_log --echo Verbose statements from : $binlog_file -select txt from raw_binlog_rows where txt like '###%'; +# Output --verbose lines, with extra Windows CR's trimmed +select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; drop table raw_binlog_rows; From a91b18262c7b2e704e8014b4cb157198cfba5ebe Mon Sep 17 00:00:00 2001 From: Jorgen Loland Date: Wed, 14 Oct 2009 18:20:01 +0200 Subject: [PATCH 24/24] Followup patch for BUG#47280 Temporary tables may set join->group to 0 even though there is grouping. Also need to test if sum_func_count>0 when JOIN::exec() decides whether to present results in a grouped manner. --- sql/sql_select.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 02e52e0c518..c5e0bce498d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2036,6 +2036,7 @@ JOIN::exec() *curr_all_fields, 0); if (curr_join->group || curr_join->implicit_grouping || + curr_join->tmp_table_param.sum_func_count || (procedure && (procedure->flags & PROC_GROUP))) { if (make_group_fields(this, curr_join))