From 44ea4e7c1f90f80ab5cf55f435856acca7a176db Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 14 Mar 2012 21:16:24 +0100 Subject: [PATCH 01/12] restore my_safe_printf_stderr for "crash-safe sigsegv handler" use vsnprintf() use write() on windows, not WriteFile or fwrite() localtime_r is still a problem --- include/my_global.h | 4 + include/my_stacktrace.h | 44 --------- mysys/stacktrace.c | 194 ++-------------------------------------- sql/signal_handler.cc | 2 +- 4 files changed, 13 insertions(+), 231 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index 5dca5710055..6f3103f930f 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -569,6 +569,10 @@ int __void__; #endif #endif /* DONT_DEFINE_VOID */ +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + /* Deprecated workaround for false-positive uninitialized variables warnings. Those should be silenced using tool-specific heuristics. diff --git a/include/my_stacktrace.h b/include/my_stacktrace.h index a2fd89852fc..7585f00da90 100644 --- a/include/my_stacktrace.h +++ b/include/my_stacktrace.h @@ -61,50 +61,6 @@ void my_set_exception_pointers(EXCEPTION_POINTERS *ep); void my_write_core(int sig); #endif - - -/** - Async-signal-safe utility functions used by signal handler routines. - Declared here in order to unit-test them. - These are not general-purpose, but tailored to the signal handling routines. -*/ -/** - Converts a longlong value to string. - @param base 10 for decimal, 16 for hex values (0..9a..f) - @param val The value to convert - @param buf Assumed to point to the *end* of the buffer. - @returns Pointer to the first character of the converted string. - Negative values: - for base-10 the return string will be prepended with '-' - for base-16 the return string will contain 16 characters - Implemented with simplicity, and async-signal-safety in mind. -*/ -char *my_safe_itoa(int base, longlong val, char *buf); - -/** - Converts a ulonglong value to string. - @param base 10 for decimal, 16 for hex values (0..9a..f) - @param val The value to convert - @param buf Assumed to point to the *end* of the buffer. - @returns Pointer to the first character of the converted string. - Implemented with simplicity, and async-signal-safety in mind. -*/ -char *my_safe_utoa(int base, ulonglong val, char *buf); - -/** - A (very) limited version of snprintf. - @param to Destination buffer. - @param n Size of destination buffer. - @param fmt printf() style format string. - @returns Number of bytes written, including terminating '\0' - Supports 'd' 'i' 'u' 'x' 'p' 's' conversion. - Supports 'l' and 'll' modifiers for integral types. - Does not support any width/precision. - Implemented with simplicity, and async-signal-safety in mind. -*/ -size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...) - ATTRIBUTE_FORMAT(printf, 3, 4); - /** A (very) limited version of snprintf, which writes the result to STDERR. @sa my_safe_snprintf diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index c59be6b1f48..2ec526f2535 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -685,7 +685,7 @@ void my_print_stacktrace(uchar* unused1, ulong unused2) &(package.sym)); have_source= pSymGetLineFromAddr64(hProcess, addr, &line_offset, &line); - fprintf(stderr,"%p ", addr); + my_safe_printf_stderr("%p ", addr); if(have_module) { char *base_image_name= strrchr(module.ImageName, '\\'); @@ -693,12 +693,13 @@ void my_print_stacktrace(uchar* unused1, ulong unused2) base_image_name++; else base_image_name= module.ImageName; - fprintf(stderr,"%s!", base_image_name); + my_safe_printf_stderr("%s!", base_image_name); } if(have_symbol) - fprintf(stderr, "%s()", package.sym.Name); + my_safe_printf_stderr("%s()", package.sym.Name); + else if(have_module) - fprintf(stderr,"%s", "???"); + my_safe_printf_stderr("%s", "???"); if(have_source) { @@ -707,10 +708,10 @@ void my_print_stacktrace(uchar* unused1, ulong unused2) base_file_name++; else base_file_name= line.FileName; - fprintf(stderr, "[%s:%u]", + my_safe_printf_stderr("[%s:%u]", base_file_name, line.LineNumber); } - fprintf(stderr,"%s", "\n"); + my_safe_printf_stderr("%s", "\n"); } } @@ -781,189 +782,10 @@ void my_safe_print_str(const char *val, int len) #endif /*__WIN__*/ -#ifdef __WIN__ -size_t my_write_stderr(const void *buf, size_t count) -{ - return fwrite(buf, 1, count, stderr); -} -#else size_t my_write_stderr(const void *buf, size_t count) { return (size_t) write(STDERR_FILENO, buf, count); } -#endif - - -static const char digits[]= "0123456789abcdef"; - -char *my_safe_utoa(int base, ulonglong val, char *buf) -{ - *buf--= 0; - do { - *buf--= digits[val % base]; - } while ((val /= base) != 0); - return buf + 1; -} - - -char *my_safe_itoa(int base, longlong val, char *buf) -{ - char *orig_buf= buf; - const my_bool is_neg= (val < 0); - *buf--= 0; - - if (is_neg) - val= -val; - if (is_neg && base == 16) - { - int ix; - val-= 1; - for (ix= 0; ix < 16; ++ix) - buf[-ix]= '0'; - } - - do { - *buf--= digits[val % base]; - } while ((val /= base) != 0); - - if (is_neg && base == 10) - *buf--= '-'; - - if (is_neg && base == 16) - { - int ix; - buf= orig_buf - 1; - for (ix= 0; ix < 16; ++ix, --buf) - { - switch (*buf) - { - case '0': *buf= 'f'; break; - case '1': *buf= 'e'; break; - case '2': *buf= 'd'; break; - case '3': *buf= 'c'; break; - case '4': *buf= 'b'; break; - case '5': *buf= 'a'; break; - case '6': *buf= '9'; break; - case '7': *buf= '8'; break; - case '8': *buf= '7'; break; - case '9': *buf= '6'; break; - case 'a': *buf= '5'; break; - case 'b': *buf= '4'; break; - case 'c': *buf= '3'; break; - case 'd': *buf= '2'; break; - case 'e': *buf= '1'; break; - case 'f': *buf= '0'; break; - } - } - } - return buf+1; -} - - -static const char *check_longlong(const char *fmt, my_bool *have_longlong) -{ - *have_longlong= FALSE; - if (*fmt == 'l') - { - fmt++; - if (*fmt != 'l') - *have_longlong= (sizeof(long) == sizeof(longlong)); - else - { - fmt++; - *have_longlong= TRUE; - } - } - return fmt; -} - -static size_t my_safe_vsnprintf(char *to, size_t size, - const char* format, va_list ap) -{ - char *start= to; - char *end= start + size - 1; - for (; *format; ++format) - { - my_bool have_longlong = FALSE; - if (*format != '%') - { - if (to == end) /* end of buffer */ - break; - *to++= *format; /* copy ordinary char */ - continue; - } - ++format; /* skip '%' */ - - format= check_longlong(format, &have_longlong); - - switch (*format) - { - case 'd': - case 'i': - case 'u': - case 'x': - case 'p': - { - longlong ival= 0; - ulonglong uval = 0; - if (*format == 'p') - have_longlong= (sizeof(void *) == sizeof(longlong)); - if (have_longlong) - { - if (*format == 'u') - uval= va_arg(ap, ulonglong); - else - ival= va_arg(ap, longlong); - } - else - { - if (*format == 'u') - uval= va_arg(ap, unsigned int); - else - ival= va_arg(ap, int); - } - - { - char buff[22]; - const int base= (*format == 'x' || *format == 'p') ? 16 : 10; - char *val_as_str= (*format == 'u') ? - my_safe_utoa(base, uval, &buff[sizeof(buff)-1]) : - my_safe_itoa(base, ival, &buff[sizeof(buff)-1]); - - /* Strip off "ffffffff" if we have 'x' format without 'll' */ - if (*format == 'x' && !have_longlong && ival < 0) - val_as_str+= 8; - - while (*val_as_str && to < end) - *to++= *val_as_str++; - continue; - } - } - case 's': - { - const char *val= va_arg(ap, char*); - if (!val) - val= "(null)"; - while (*val && to < end) - *to++= *val++; - continue; - } - } - } - *to= 0; - return to - start; -} - - -size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...) -{ - size_t result; - va_list args; - va_start(args,fmt); - result= my_safe_vsnprintf(to, n, fmt, args); - va_end(args); - return result; -} size_t my_safe_printf_stderr(const char* fmt, ...) @@ -972,7 +794,7 @@ size_t my_safe_printf_stderr(const char* fmt, ...) size_t result; va_list args; va_start(args,fmt); - result= my_safe_vsnprintf(to, sizeof(to), fmt, args); + result= vsnprintf(to, sizeof(to), fmt, args); va_end(args); my_write_stderr(to, result); return result; diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 15be12b50ec..ae0cf6e1bb0 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -72,7 +72,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) curr_time= my_time(0); localtime_r(&curr_time, &tm); - fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d ", + my_safe_printf_stderr("%02d%02d%02d %2d:%02d:%02d ", tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); if (opt_expect_abort From eb30253d690c302f0219be36b71bb40559d3a054 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 15 Mar 2012 15:06:06 +0100 Subject: [PATCH 02/12] Fix access to uninitialized variable in innodb error message in case WriteFile() fails. --- storage/innobase/os/os0file.c | 2 +- storage/xtradb/os/os0file.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/os/os0file.c b/storage/innobase/os/os0file.c index 566c50381e7..4913f23ef67 100644 --- a/storage/innobase/os/os0file.c +++ b/storage/innobase/os/os0file.c @@ -2556,7 +2556,7 @@ retry: "InnoDB: Check also that the disk is not full" " or a disk quota exceeded.\n", name, (ulong) offset_high, (ulong) offset, - (ulong) n, (ulong) len, (ulong) err); + (ulong) n, ret ? len : 0, (ulong) err); if (strerror((int)err) != NULL) { fprintf(stderr, diff --git a/storage/xtradb/os/os0file.c b/storage/xtradb/os/os0file.c index 1e863109bed..b502be1eb00 100644 --- a/storage/xtradb/os/os0file.c +++ b/storage/xtradb/os/os0file.c @@ -2785,7 +2785,7 @@ retry: "InnoDB: Check also that the disk is not full" " or a disk quota exceeded.\n", name, (ulong) offset_high, (ulong) offset, - (ulong) n, (ulong) len, (ulong) err); + (ulong) n, ret ? len : 0, (ulong) err); if (strerror((int)err) != NULL) { fprintf(stderr, From 5805908bf98b1de5c1fefed4778dcc22bb70c6fd Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sun, 18 Mar 2012 23:58:20 +0400 Subject: [PATCH 03/12] BUG#952372: Server crashes on 2nd execution of PS in find_field_in_tables with semijoin+materialization - The problem was that convert_subq_to_jtbm() attached the semi-join TABLE_LIST object into the wrong list: they used to attach it to the end of parent_lex->leaf_tables.head()->next_local->...->next_local. This was apparently inccorect, as one can construct an example where JTBM nest is attached to a table that is inside some mergeable VIEW, which breaks (causes crash) for name resolution on the subsequent statement re-execution. - Solution: Attach to the "right" list. The "wording" was copied from st_select_lex::handle_derived. --- mysql-test/r/subselect_sj.result | 22 ++++++++++++++++++++++ mysql-test/r/subselect_sj_jcl6.result | 22 ++++++++++++++++++++++ mysql-test/t/subselect_sj.test | 22 ++++++++++++++++++++++ sql/opt_subselect.cc | 7 +++++-- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 1d0aae275ed..24ad20a2b1c 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -2628,4 +2628,26 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 100 Using where; Using join buffer (flat, BNL join) drop table t0,t1,t3; set optimizer_switch= @tmp_923246; +# +# BUG#952372: Server crashes on 2nd execution of PS in find_field_in_tables with semijoin+materialization +# +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (2),(3); +CREATE VIEW v1 AS SELECT * FROM t1; +CREATE TABLE t2 ( b VARCHAR(1) ); +INSERT INTO t2 VALUES ('v'),('v'); +PREPARE pstmt FROM +'SELECT DISTINCT a FROM v1, t2 + WHERE b IN ( SELECT MIN(b) FROM t2 )'; +EXECUTE pstmt; +a +2 +3 +EXECUTE pstmt; +a +2 +3 +DEALLOCATE PREPARE pstmt; +DROP VIEW v1; +DROP TABLE t1, t2; set optimizer_switch=@subselect_sj_tmp; diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index baa2aec8aea..e9ae4508dd8 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -2642,6 +2642,28 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 100 Using where; Using join buffer (flat, BNL join) drop table t0,t1,t3; set optimizer_switch= @tmp_923246; +# +# BUG#952372: Server crashes on 2nd execution of PS in find_field_in_tables with semijoin+materialization +# +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (2),(3); +CREATE VIEW v1 AS SELECT * FROM t1; +CREATE TABLE t2 ( b VARCHAR(1) ); +INSERT INTO t2 VALUES ('v'),('v'); +PREPARE pstmt FROM +'SELECT DISTINCT a FROM v1, t2 + WHERE b IN ( SELECT MIN(b) FROM t2 )'; +EXECUTE pstmt; +a +2 +3 +EXECUTE pstmt; +a +2 +3 +DEALLOCATE PREPARE pstmt; +DROP VIEW v1; +DROP TABLE t1, t2; set optimizer_switch=@subselect_sj_tmp; # # BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off diff --git a/mysql-test/t/subselect_sj.test b/mysql-test/t/subselect_sj.test index 6b8a757b9e8..23294bbe95a 100644 --- a/mysql-test/t/subselect_sj.test +++ b/mysql-test/t/subselect_sj.test @@ -2331,5 +2331,27 @@ explain select * from t3 where a in (select kp1 from t1 where kp1<20); drop table t0,t1,t3; set optimizer_switch= @tmp_923246; + +--echo # +--echo # BUG#952372: Server crashes on 2nd execution of PS in find_field_in_tables with semijoin+materialization +--echo # +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (2),(3); +CREATE VIEW v1 AS SELECT * FROM t1; + +CREATE TABLE t2 ( b VARCHAR(1) ); +INSERT INTO t2 VALUES ('v'),('v'); + +PREPARE pstmt FROM + 'SELECT DISTINCT a FROM v1, t2 + WHERE b IN ( SELECT MIN(b) FROM t2 )'; + +EXECUTE pstmt; +EXECUTE pstmt; + +DEALLOCATE PREPARE pstmt; +DROP VIEW v1; +DROP TABLE t1, t2; + # The following command must be the last one the file set optimizer_switch=@subselect_sj_tmp; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 4559b76764e..24305ec194f 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1250,7 +1250,10 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) (a theory: a next_local chain always starts with ::leaf_tables because view's tables are inserted after the view) */ - for (tl= parent_lex->leaf_tables.head(); tl->next_local; tl= tl->next_local) ; + + for (tl= (TABLE_LIST*)(parent_lex->table_list.first); tl->next_local; tl= tl->next_local) + {} + tl->next_local= subq_lex->leaf_tables.head(); /* A theory: no need to re-connect the next_global chain */ @@ -1463,7 +1466,7 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, (a theory: a next_local chain always starts with ::leaf_tables because view's tables are inserted after the view) */ - for (tl= parent_lex->leaf_tables.head(); tl->next_local; tl= tl->next_local) + for (tl= (TABLE_LIST*)(parent_lex->table_list.first); tl->next_local; tl= tl->next_local) {} tl->next_local= jtbm; From 91d17a9c209627dab852d7904479aa254e344fa2 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 19 Mar 2012 01:04:55 +0400 Subject: [PATCH 04/12] BUG#952583: Server crashes in Item_field::fix_after_pullout on INSERT .. SELECT - Take into account that there may exist Item_field objects with context==NULL. --- mysql-test/r/subselect_sj.result | 11 +++++++++++ mysql-test/r/subselect_sj_jcl6.result | 11 +++++++++++ mysql-test/t/subselect_sj.test | 14 ++++++++++++++ sql/item.cc | 25 ++++++++++++++----------- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 1d0aae275ed..96ddb12521b 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -2628,4 +2628,15 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 100 Using where; Using join buffer (flat, BNL join) drop table t0,t1,t3; set optimizer_switch= @tmp_923246; +# +# BUG#952583: Server crashes in Item_field::fix_after_pullout on INSERT .. SELECT +# +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 ( b INT ); +INSERT INTO t2 VALUES (3),(4); +INSERT INTO t1 +SELECT * FROM ( SELECT * FROM t1 ) AS alias +WHERE a IN ( SELECT b FROM t2 ); +DROP TABLE t1, t2; set optimizer_switch=@subselect_sj_tmp; diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index baa2aec8aea..b6e87bfeec3 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -2642,6 +2642,17 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 100 Using where; Using join buffer (flat, BNL join) drop table t0,t1,t3; set optimizer_switch= @tmp_923246; +# +# BUG#952583: Server crashes in Item_field::fix_after_pullout on INSERT .. SELECT +# +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 ( b INT ); +INSERT INTO t2 VALUES (3),(4); +INSERT INTO t1 +SELECT * FROM ( SELECT * FROM t1 ) AS alias +WHERE a IN ( SELECT b FROM t2 ); +DROP TABLE t1, t2; set optimizer_switch=@subselect_sj_tmp; # # BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off diff --git a/mysql-test/t/subselect_sj.test b/mysql-test/t/subselect_sj.test index 6b8a757b9e8..fe290cf4a97 100644 --- a/mysql-test/t/subselect_sj.test +++ b/mysql-test/t/subselect_sj.test @@ -2331,5 +2331,19 @@ explain select * from t3 where a in (select kp1 from t1 where kp1<20); drop table t0,t1,t3; set optimizer_switch= @tmp_923246; +--echo # +--echo # BUG#952583: Server crashes in Item_field::fix_after_pullout on INSERT .. SELECT +--echo # +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 ( b INT ); +INSERT INTO t2 VALUES (3),(4); + +INSERT INTO t1 +SELECT * FROM ( SELECT * FROM t1 ) AS alias +WHERE a IN ( SELECT b FROM t2 ); + +DROP TABLE t1, t2; + # The following command must be the last one the file set optimizer_switch=@subselect_sj_tmp; diff --git a/sql/item.cc b/sql/item.cc index f9bbc4aeead..8169e1d35b4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2436,17 +2436,20 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) { if (new_parent == get_depended_from()) depended_from= NULL; - Name_resolution_context *ctx= new Name_resolution_context(); - ctx->outer_context= NULL; // We don't build a complete name resolver - ctx->table_list= NULL; // We rely on first_name_resolution_table instead - ctx->select_lex= new_parent; - ctx->first_name_resolution_table= context->first_name_resolution_table; - ctx->last_name_resolution_table= context->last_name_resolution_table; - ctx->error_processor= context->error_processor; - ctx->error_processor_data= context->error_processor_data; - ctx->resolve_in_select_list= context->resolve_in_select_list; - ctx->security_ctx= context->security_ctx; - this->context=ctx; + if (context) + { + Name_resolution_context *ctx= new Name_resolution_context(); + ctx->outer_context= NULL; // We don't build a complete name resolver + ctx->table_list= NULL; // We rely on first_name_resolution_table instead + ctx->select_lex= new_parent; + ctx->first_name_resolution_table= context->first_name_resolution_table; + ctx->last_name_resolution_table= context->last_name_resolution_table; + ctx->error_processor= context->error_processor; + ctx->error_processor_data= context->error_processor_data; + ctx->resolve_in_select_list= context->resolve_in_select_list; + ctx->security_ctx= context->security_ctx; + this->context=ctx; + } } From 357331af8a450dfc975575e99c640b8f6893360b Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 20 Mar 2012 17:03:28 -0700 Subject: [PATCH 05/12] Fixed LP bug #954262. This bug in the constructor SEL_IMERGE::SEL_IMERGE could cause huge excessive memory requests. --- mysql-test/r/range_vs_index_merge.result | 176 ++++++++++++++++++ .../r/range_vs_index_merge_innodb.result | 176 ++++++++++++++++++ mysql-test/t/range_vs_index_merge.test | 56 ++++++ sql/opt_range.cc | 2 +- 4 files changed, 409 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/range_vs_index_merge.result b/mysql-test/r/range_vs_index_merge.result index f8738457dd9..faaa6d2429e 100644 --- a/mysql-test/r/range_vs_index_merge.result +++ b/mysql-test/r/range_vs_index_merge.result @@ -1045,6 +1045,182 @@ ID Name Country Population 4030 Sandy USA 101853 4031 Athens-Clarke County USA 101489 4032 Cambridge USA 101355 +set @save_optimizer_switch=@@optimizer_switch; +CREATE INDEX CityName on City(Name); +EXPLAIN SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE City index_merge Country,CountryPopulation,CountryName,CityName CountryName,CityName 38,35 NULL 28 Using sort_union(CountryName,CityName); Using where +SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +Name Country Population +Addis Abeba ETH 2495000 +Manila PHL 1581082 +Jakarta IDN 9604900 +Delhi IND 7206704 +Bangalore IND 2660088 +Teheran IRN 6758845 +Roma ITA 2643581 +Venezia ITA 277305 +Tokyo JPN 7980230 +Toronto CAN 688275 +Vancouver CAN 514008 +Peking CHN 7472000 +Seoul KOR 9981619 +Kaunas LTU 412639 +Rabat MAR 623457 +Tijuana MEX 1212232 +Lagos NGA 1518000 +Paris FRA 2125246 +Dresden DEU 476668 +Dakar SEN 785071 +Basel CHE 166700 +Praha CZE 1181126 +Ankara TUR 3038159 +Lugansk UKR 469000 +Caracas VEN 1975294 +Samara RUS 1156100 +Seattle USA 563374 +set optimizer_switch='index_merge=off'; +EXPLAIN SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE City range Country,CountryPopulation,CountryName,CityName CountryName 38 NULL 29 Using index condition; Using where +SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +Name Country Population +Toronto CAN 688275 +Vancouver CAN 514008 +Basel CHE 166700 +Peking CHN 7472000 +Praha CZE 1181126 +Dresden DEU 476668 +Addis Abeba ETH 2495000 +Paris FRA 2125246 +Jakarta IDN 9604900 +Bangalore IND 2660088 +Delhi IND 7206704 +Teheran IRN 6758845 +Roma ITA 2643581 +Venezia ITA 277305 +Tokyo JPN 7980230 +Seoul KOR 9981619 +Kaunas LTU 412639 +Rabat MAR 623457 +Tijuana MEX 1212232 +Lagos NGA 1518000 +Manila PHL 1581082 +Samara RUS 1156100 +Dakar SEN 785071 +Ankara TUR 3038159 +Lugansk UKR 469000 +Seattle USA 563374 +Caracas VEN 1975294 +set optimizer_switch=@save_optimizer_switch; DROP DATABASE world; use test; CREATE TABLE t1 ( diff --git a/mysql-test/r/range_vs_index_merge_innodb.result b/mysql-test/r/range_vs_index_merge_innodb.result index c42f80f0e85..df3a2af0753 100644 --- a/mysql-test/r/range_vs_index_merge_innodb.result +++ b/mysql-test/r/range_vs_index_merge_innodb.result @@ -1046,6 +1046,182 @@ ID Name Country Population 4030 Sandy USA 101853 4031 Athens-Clarke County USA 101489 4032 Cambridge USA 101355 +set @save_optimizer_switch=@@optimizer_switch; +CREATE INDEX CityName on City(Name); +EXPLAIN SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE City range Country,CountryPopulation,CountryName,CityName CountryName 38 NULL 27 Using index condition; Using where +SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +Name Country Population +Toronto CAN 688275 +Vancouver CAN 514008 +Basel CHE 166700 +Peking CHN 7472000 +Praha CZE 1181126 +Dresden DEU 476668 +Addis Abeba ETH 2495000 +Paris FRA 2125246 +Jakarta IDN 9604900 +Bangalore IND 2660088 +Delhi IND 7206704 +Teheran IRN 6758845 +Roma ITA 2643581 +Venezia ITA 277305 +Tokyo JPN 7980230 +Seoul KOR 9981619 +Kaunas LTU 412639 +Rabat MAR 623457 +Tijuana MEX 1212232 +Lagos NGA 1518000 +Manila PHL 1581082 +Samara RUS 1156100 +Dakar SEN 785071 +Ankara TUR 3038159 +Lugansk UKR 469000 +Seattle USA 563374 +Caracas VEN 1975294 +set optimizer_switch='index_merge=off'; +EXPLAIN SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE City range Country,CountryPopulation,CountryName,CityName CountryName 38 NULL 27 Using index condition; Using where +SELECT Name, Country, Population FROM City WHERE +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); +Name Country Population +Toronto CAN 688275 +Vancouver CAN 514008 +Basel CHE 166700 +Peking CHN 7472000 +Praha CZE 1181126 +Dresden DEU 476668 +Addis Abeba ETH 2495000 +Paris FRA 2125246 +Jakarta IDN 9604900 +Bangalore IND 2660088 +Delhi IND 7206704 +Teheran IRN 6758845 +Roma ITA 2643581 +Venezia ITA 277305 +Tokyo JPN 7980230 +Seoul KOR 9981619 +Kaunas LTU 412639 +Rabat MAR 623457 +Tijuana MEX 1212232 +Lagos NGA 1518000 +Manila PHL 1581082 +Samara RUS 1156100 +Dakar SEN 785071 +Ankara TUR 3038159 +Lugansk UKR 469000 +Seattle USA 563374 +Caracas VEN 1975294 +set optimizer_switch=@save_optimizer_switch; DROP DATABASE world; use test; CREATE TABLE t1 ( diff --git a/mysql-test/t/range_vs_index_merge.test b/mysql-test/t/range_vs_index_merge.test index de7e6a0838c..613a7cf5760 100755 --- a/mysql-test/t/range_vs_index_merge.test +++ b/mysql-test/t/range_vs_index_merge.test @@ -621,6 +621,62 @@ SELECT * FROM City (Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%'); +# +# LP bug #954262: index merge oover long disjunction in WHERE +# + +set @save_optimizer_switch=@@optimizer_switch; + +CREATE INDEX CityName on City(Name); + +let $cond = +(Name='Manila' AND Country='PHL') OR +(Name='Addis Abeba' AND Country='ETH') OR +(Name='Jakarta' AND Country='IDN') OR +(Name='Bangalore' AND Country='IND') OR +(Name='Teheran' AND Country='IRN') OR +(Name='Roma' AND Country='ITA') OR +(Name='Delhi' AND Country='IND') OR +(Name='Venezia' AND Country='ITA') OR +(Name='Tokyo' AND Country='JPN') OR +(Name='Toronto' AND Country='CAN') OR +(Name='Peking' AND Country='CHN') OR +(Name='Lagos' AND Country='NGA') OR +(Name='Tijuana' AND Country='MEX') OR +(Name='Rabat' AND Country='MAR') OR +(Name='Seoul' AND Country='KOR') OR +(Name='Vancouver' AND Country='CAN') OR +(Name='Kaunas' AND Country='LTU') OR +(Name='Paris' AND Country='FRA') OR +(Name='Dakar' AND Country='SEN') OR +(Name='Basel' AND Country='CHE') OR +(Name='Praha' AND Country='CZE') OR +(Name='Ankara' AND Country='TUR') OR +(Name='Dresden' AND Country='DEU') OR +(Name='Lugansk' AND Country='UKR') OR +(Name='Caracas' AND Country='VEN') OR +(Name='Samara' AND Country='RUS') OR +(Name='Seattle' AND Country='USA'); + +eval +EXPLAIN SELECT Name, Country, Population FROM City WHERE +$cond; +eval +SELECT Name, Country, Population FROM City WHERE +$cond; + +set optimizer_switch='index_merge=off'; + +eval +EXPLAIN SELECT Name, Country, Population FROM City WHERE +$cond; +eval +SELECT Name, Country, Population FROM City WHERE +$cond; + +set optimizer_switch=@save_optimizer_switch; + + DROP DATABASE world; use test; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e0841d3a696..4b973b8500e 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1369,7 +1369,7 @@ SEL_IMERGE::SEL_IMERGE(SEL_IMERGE *arg, uint cnt, for (SEL_TREE **tree = trees, **arg_tree= arg->trees; tree < trees_next; tree++, arg_tree++) { - if (!(*tree= new SEL_TREE(*arg_tree, FALSE, param))) + if (!(*tree= new SEL_TREE(*arg_tree, TRUE, param))) goto mem_err; } From 3920d978192ddf531d323168460cc7565236bd3c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 21 Mar 2012 10:59:20 +0100 Subject: [PATCH 06/12] mdev-191 SHOW TABLES was unnecessary opening .frm files mark the corresponding I_S table as OPTIMIZE_I_S_TABLE, to let the I_S optimizer figure out whether files need to be opened, and don't open the tables unless I_S optimizer says so. --- sql/sql_show.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 0e8b919db53..bb352e33506 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3652,16 +3652,17 @@ end: @retval 1 error */ -static int fill_schema_table_names(THD *thd, TABLE *table, +static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, LEX_STRING *db_name, LEX_STRING *table_name, bool with_i_schema) { + TABLE *table= tables->table; if (with_i_schema) { table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), system_charset_info); } - else + else if (tables->table_open_method != SKIP_OPEN_TABLE) { enum legacy_db_type not_used; char path[FN_REFLEN + 1]; @@ -4006,7 +4007,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) /* SHOW TABLE NAMES command */ if (schema_table_idx == SCH_TABLE_NAMES) { - if (fill_schema_table_names(thd, tables->table, db_name, + if (fill_schema_table_names(thd, tables, db_name, table_name, with_i_schema)) continue; } @@ -7622,7 +7623,7 @@ ST_SCHEMA_TABLE schema_tables[]= {"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table, get_all_tables, 0, get_schema_constraints_record, 3, 4, 0, OPEN_TABLE_ONLY}, {"TABLE_NAMES", table_names_fields_info, create_schema_table, - get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0}, + get_all_tables, make_table_names_old_format, 0, 1, 2, 1, OPTIMIZE_I_S_TABLE}, {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table, fill_schema_table_privileges, 0, 0, -1, -1, 0, 0}, {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table, From d1f311799988266159310afce3cf19eaf58a1fa5 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 21 Mar 2012 18:22:02 +0100 Subject: [PATCH 07/12] lp:933959 Assertion `0' failed in net_end_statement(THD*) on concurrent SELECT FROM I_S.INNODB_SYS_INDEXES and ALTER TABLE Workaround: report a generic error if an I_S plugin failed silently. --- sql/sql_show.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f800a173672..91649d934fe 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6275,6 +6275,8 @@ bool get_schema_tables_result(JOIN *join, join->error= 1; tab->read_record.file= table_list->table->file; table_list->schema_table_state= executed_place; + if (!thd->is_error()) + my_error(ER_UNKNOWN_ERROR, MYF(0)); break; } tab->read_record.file= table_list->table->file; From da9aabbbb653274de997763b7f833ac6822f81bf Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 22 Mar 2012 19:56:17 -0700 Subject: [PATCH 08/12] Fixed LP bug #954900. If the first component of a ref key happened to be a constant appeared after constant row substitution then no store_key element should be created for such a component. Yet create_ref_for_key() erroneously could create such an element that caused construction of invalid ref keys and wrong results for some joins. --- mysql-test/r/join.result | 70 ++++++++++++++++++++++++++++++++++++++++ mysql-test/t/join.test | 68 ++++++++++++++++++++++++++++++++++++++ sql/sql_select.cc | 2 +- 3 files changed, 139 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index e196cb8170b..5185c9dcb2b 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -1400,4 +1400,74 @@ select t2.i from t1 left join t2 on t2.i = t1.i where t1.i = '1:1:1'; i 01:01:01 drop table t1,t2; +# +# BUG#954900: unexpected empty set due to an invalid build of key ref +# +CREATE TABLE t1 (dog_id int(10), birthday date, PRIMARY KEY (dog_id,birthday)); +INSERT INTO t1 VALUES (5918,'2004-07-22'); +CREATE TABLE t2 (dog_id int(10) unsigned, t_id char(1), birthday date, a_id int(10), +PRIMARY KEY (dog_id,t_id,birthday,a_id)); +INSERT INTO t2 VALUES +(5918,'N','2004-07-22',5216551), (5918,'N','2004-07-22',5223640), +(5918,'N','2004-07-22',5389491), (5918,'N','2004-07-22',5749434), +(5918,'N','2004-07-22',5992424), (5922,'N','2005-06-30',5076957), +(5924,'N','2000-08-11',20264), (5924,'N','2000-08-11',64251), +(5924,'N','2000-08-11',74748), (5924,'N','2000-08-11',87590), +(5924,'N','2000-08-11',104695), (5924,'N','2000-08-11',133136), +(5924,'N','2000-08-11',5027806), (5924,'N','2000-08-11',5076957), +(5924,'N','2000-08-11',5166821), (5924,'N','2000-08-11',5181896), +(5924,'N','2000-08-11',5217908), (5924,'N','2000-08-11',5220812), +(5924,'N','2000-08-11',5226473), (5924,'N','2000-08-11',5339111), +(5925,'N','2005-02-10',19227), (5925,'N','2005-02-10',74529), +(5925,'N','2005-02-10',74748), (5927,'N','2005-08-18',20264), +(5927,'N','2005-08-18',58364), (5929,'N','2005-01-19',58364), +(5935,'N','2006-03-10',19227), (5935,'N','2006-03-10',64251), +(5935,'N','2006-03-10',5222400), (5935,'N','2006-03-10',5226473), +(5936,'N','2004-10-29',5015032), (5937,'N','2002-04-05',11237), +(5937,'N','2002-04-05',23911), (5937,'N','2002-04-05',112133), +(5937,'N','2002-04-05',169721), (5937,'N','2002-04-05',170650), +(5937,'N','2002-04-05',5014494), (5937,'N','2002-04-05',5166009), +(5937,'N','2002-04-05',5181871), (5937,'N','2002-04-05',5213380), +(5937,'N','2002-04-05',5214875), (5937,'N','2002-04-05',5895062), +(5938,'N','2006-03-24',11237), (5938,'N','2006-03-24',19227), +(5938,'N','2006-03-24',23911), (5938,'N','2006-03-24',58364), +(5938,'N','2006-03-24',64251), (5938,'N','2006-03-24',111716), +(5938,'N','2006-03-24',112702), (5938,'N','2006-03-24',133136), +(5938,'N','2006-03-24',168718), (5938,'N','2006-03-24',5137136), +(5938,'N','2006-03-24',5161519), (5938,'N','2006-03-24',5168120), +(5938,'N','2006-03-24',5219034), (6234,'N','2006-06-02',103058), +(6234,'N','2006-06-02',5146844), (6235,'N','2006-06-01',12900), +(6235,'N','2006-06-01',20264), (6235,'N','2006-06-01',64251), +(6235,'N','2006-06-01',75160), (6235,'N','2006-06-01',5014494), +(6235,'N','2006-06-01',5181638), (6236,'N','2006-06-06',112595), +(6236,'N','2006-06-06',5219601), (6236,'N','2006-06-06',5808374); +CREATE TABLE t3 (dog_id int(10) unsigned); +INSERT INTO t3 VALUES (5918); +CREATE TABLE t4 (dog_id int(10), t_id char(1), birthday date, KEY (t_id)); +INSERT INTO t4 VALUES (5918,'N','2004-07-22'), (5919,'N','2004-07-20'); +CREATE TABLE t5 (dog_id int(10) unsigned, UNIQUE KEY (dog_id)); +INSERT INTO t5 VALUES (5918); +SET @tmp_optimizer_switch=@@optimizer_switch; +SET optimizer_switch='index_condition_pushdown=off'; +EXPLAIN +SELECT * FROM t5 DU, t1 D, t4 DT, t2 DSA, t3 DSAR +WHERE DU.dog_id=D.dog_id AND D.dog_id=DT.dog_id AND D.birthday=DT.birthday AND +DT.t_id=DSA.t_id AND DT.birthday=DSA.birthday AND DSA.dog_id=DSAR.dog_id; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE DU system dog_id NULL NULL NULL 1 +1 SIMPLE D system PRIMARY NULL NULL NULL 1 +1 SIMPLE DSAR system NULL NULL NULL NULL 1 +1 SIMPLE DT range t_id t_id 2 NULL 2 Using where +1 SIMPLE DSA ref PRIMARY PRIMARY 8 const,test.DT.t_id,test.D.birthday 1 Using index +SELECT * FROM t5 DU, t1 D, t4 DT, t2 DSA, t3 DSAR +WHERE DU.dog_id=D.dog_id AND D.dog_id=DT.dog_id AND D.birthday=DT.birthday AND +DT.t_id=DSA.t_id AND DT.birthday=DSA.birthday AND DSA.dog_id=DSAR.dog_id; +dog_id dog_id birthday dog_id t_id birthday dog_id t_id birthday a_id dog_id +5918 5918 2004-07-22 5918 N 2004-07-22 5918 N 2004-07-22 5216551 5918 +5918 5918 2004-07-22 5918 N 2004-07-22 5918 N 2004-07-22 5223640 5918 +5918 5918 2004-07-22 5918 N 2004-07-22 5918 N 2004-07-22 5389491 5918 +5918 5918 2004-07-22 5918 N 2004-07-22 5918 N 2004-07-22 5749434 5918 +5918 5918 2004-07-22 5918 N 2004-07-22 5918 N 2004-07-22 5992424 5918 +SET optimizer_switch=@tmp_optimizer_switch; +DROP TABLE t1,t2,t3,t4,t5; SET optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index 51bb72e1239..907d39e95fe 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -1067,4 +1067,72 @@ insert into t2 values ('1:1:1'); select t2.i from t1 left join t2 on t2.i = t1.i where t1.i = '1:1:1'; drop table t1,t2; +--echo # +--echo # BUG#954900: unexpected empty set due to an invalid build of key ref +--echo # + +CREATE TABLE t1 (dog_id int(10), birthday date, PRIMARY KEY (dog_id,birthday)); +INSERT INTO t1 VALUES (5918,'2004-07-22'); + +CREATE TABLE t2 (dog_id int(10) unsigned, t_id char(1), birthday date, a_id int(10), +PRIMARY KEY (dog_id,t_id,birthday,a_id)); +INSERT INTO t2 VALUES +(5918,'N','2004-07-22',5216551), (5918,'N','2004-07-22',5223640), +(5918,'N','2004-07-22',5389491), (5918,'N','2004-07-22',5749434), +(5918,'N','2004-07-22',5992424), (5922,'N','2005-06-30',5076957), +(5924,'N','2000-08-11',20264), (5924,'N','2000-08-11',64251), +(5924,'N','2000-08-11',74748), (5924,'N','2000-08-11',87590), +(5924,'N','2000-08-11',104695), (5924,'N','2000-08-11',133136), +(5924,'N','2000-08-11',5027806), (5924,'N','2000-08-11',5076957), +(5924,'N','2000-08-11',5166821), (5924,'N','2000-08-11',5181896), +(5924,'N','2000-08-11',5217908), (5924,'N','2000-08-11',5220812), +(5924,'N','2000-08-11',5226473), (5924,'N','2000-08-11',5339111), +(5925,'N','2005-02-10',19227), (5925,'N','2005-02-10',74529), +(5925,'N','2005-02-10',74748), (5927,'N','2005-08-18',20264), +(5927,'N','2005-08-18',58364), (5929,'N','2005-01-19',58364), +(5935,'N','2006-03-10',19227), (5935,'N','2006-03-10',64251), +(5935,'N','2006-03-10',5222400), (5935,'N','2006-03-10',5226473), +(5936,'N','2004-10-29',5015032), (5937,'N','2002-04-05',11237), +(5937,'N','2002-04-05',23911), (5937,'N','2002-04-05',112133), +(5937,'N','2002-04-05',169721), (5937,'N','2002-04-05',170650), +(5937,'N','2002-04-05',5014494), (5937,'N','2002-04-05',5166009), +(5937,'N','2002-04-05',5181871), (5937,'N','2002-04-05',5213380), +(5937,'N','2002-04-05',5214875), (5937,'N','2002-04-05',5895062), +(5938,'N','2006-03-24',11237), (5938,'N','2006-03-24',19227), +(5938,'N','2006-03-24',23911), (5938,'N','2006-03-24',58364), +(5938,'N','2006-03-24',64251), (5938,'N','2006-03-24',111716), +(5938,'N','2006-03-24',112702), (5938,'N','2006-03-24',133136), +(5938,'N','2006-03-24',168718), (5938,'N','2006-03-24',5137136), +(5938,'N','2006-03-24',5161519), (5938,'N','2006-03-24',5168120), +(5938,'N','2006-03-24',5219034), (6234,'N','2006-06-02',103058), +(6234,'N','2006-06-02',5146844), (6235,'N','2006-06-01',12900), +(6235,'N','2006-06-01',20264), (6235,'N','2006-06-01',64251), +(6235,'N','2006-06-01',75160), (6235,'N','2006-06-01',5014494), +(6235,'N','2006-06-01',5181638), (6236,'N','2006-06-06',112595), +(6236,'N','2006-06-06',5219601), (6236,'N','2006-06-06',5808374); + +CREATE TABLE t3 (dog_id int(10) unsigned); +INSERT INTO t3 VALUES (5918); + +CREATE TABLE t4 (dog_id int(10), t_id char(1), birthday date, KEY (t_id)); +INSERT INTO t4 VALUES (5918,'N','2004-07-22'), (5919,'N','2004-07-20'); + +CREATE TABLE t5 (dog_id int(10) unsigned, UNIQUE KEY (dog_id)); +INSERT INTO t5 VALUES (5918); + +SET @tmp_optimizer_switch=@@optimizer_switch; +SET optimizer_switch='index_condition_pushdown=off'; + +EXPLAIN +SELECT * FROM t5 DU, t1 D, t4 DT, t2 DSA, t3 DSAR +WHERE DU.dog_id=D.dog_id AND D.dog_id=DT.dog_id AND D.birthday=DT.birthday AND + DT.t_id=DSA.t_id AND DT.birthday=DSA.birthday AND DSA.dog_id=DSAR.dog_id; +SELECT * FROM t5 DU, t1 D, t4 DT, t2 DSA, t3 DSAR +WHERE DU.dog_id=D.dog_id AND D.dog_id=DT.dog_id AND D.birthday=DT.birthday AND + DT.t_id=DSA.t_id AND DT.birthday=DSA.birthday AND DSA.dog_id=DSAR.dog_id; + +SET optimizer_switch=@tmp_optimizer_switch; + +DROP TABLE t1,t2,t3,t4,t5; + SET optimizer_switch=@save_optimizer_switch; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b7c0156f65b..a526de5a256 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7498,7 +7498,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, if (keyuse->null_rejecting) j->ref.null_rejecting |= 1 << i; keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables; - if (!keyuse->used_tables && !thd->lex->describe) + if (!keyuse->val->used_tables() && !thd->lex->describe) { // Compare against constant store_key_item tmp(thd, keyinfo->key_part[i].field, From de1765fb64dda7c610217c27a6012955f01d88e4 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 23 Mar 2012 18:11:29 +0200 Subject: [PATCH 09/12] Speedups: - Optimize away calls to hp_rec_hashnr() by cashing hash - Try to get more rows / block (to minimize overhead of HP_PTRS) in HEAP tables. storage/heap/_check.c: Optimize away calls to hp_rec_hashnr() by cashing hash. Print cleanups storage/heap/heapdef.h: Added place to hold calculated hash value for row storage/heap/hp_create.c: Try to get more rows / block (to minimize overhead of HP_PTRS) storage/heap/hp_delete.c: Optimize away calls to hp_rec_hashnr() by cashing hash. storage/heap/hp_hash.c: Optimize away calls to hp_rec_hashnr() by cashing hash. Remove some not needed DBUG_PRINT storage/heap/hp_test2.c: Increased max table size as now heap tables takes a bit more space (a few %) storage/heap/hp_write.c: Optimize away calls to hp_rec_hashnr() by cashing hash. Remove duplicated code More DBUG_PRINT storage/maria/ma_create.c: More DBUG_PRINT --- storage/heap/_check.c | 23 ++++++++------- storage/heap/heapdef.h | 1 + storage/heap/hp_create.c | 14 +++++++-- storage/heap/hp_delete.c | 9 +++--- storage/heap/hp_hash.c | 12 ++++++-- storage/heap/hp_test2.c | 4 +-- storage/heap/hp_write.c | 60 +++++++++++++++++++++++---------------- storage/maria/ma_create.c | 3 +- 8 files changed, 80 insertions(+), 46 deletions(-) diff --git a/storage/heap/_check.c b/storage/heap/_check.c index 08b6da62ae1..6cd01012d84 100644 --- a/storage/heap/_check.c +++ b/storage/heap/_check.c @@ -110,8 +110,13 @@ static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records, for (i=found=max_links=seek=0 ; i < records ; i++) { hash_info=hp_find_hash(&keydef->block,i); - if (hp_mask(hp_rec_hashnr(keydef, hash_info->ptr_to_rec), - blength,records) == i) + if (hash_info->hash_of_key != hp_rec_hashnr(keydef, hash_info->ptr_to_rec)) + { + DBUG_PRINT("error", + ("Found row with wrong hash_of_key at position %lu", i)); + error= 1; + } + if (hp_mask(hash_info->hash_of_key, blength, records) == i) { found++; seek++; @@ -119,9 +124,7 @@ static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records, while ((hash_info=hash_info->next_key) && found < records + 1) { seek+= ++links; - if ((rec_link = hp_mask(hp_rec_hashnr(keydef, hash_info->ptr_to_rec), - blength, records)) - != i) + if ((rec_link= hp_mask(hash_info->hash_of_key, blength, records)) != i) { DBUG_PRINT("error", ("Record in wrong link: Link %lu Record: 0x%lx Record-link %lu", @@ -147,14 +150,14 @@ static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records, error=1; } DBUG_PRINT("info", - ("records: %ld seeks: %lu max links: %lu hitrate: %.2f " - "buckets: %lu", - records,seek,max_links, + ("key: %u records: %ld seeks: %lu max links: %lu " + "hitrate: %.2f buckets: %lu", + keynr, records,seek,max_links, (float) seek / (float) (records ? records : 1), hash_buckets_found)); if (print_status) - printf("Key: %d records: %ld seeks: %lu max links: %lu " - "hitrate: %.2f buckets: %lu\n", + printf("Key: %u records: %ld seeks: %lu max links: %lu " + "hitrate: %.2f buckets: %lu\n", keynr, records, seek, max_links, (float) seek / (float) (records ? records : 1), hash_buckets_found); diff --git a/storage/heap/heapdef.h b/storage/heap/heapdef.h index 3fc94062303..b9a3eab3432 100644 --- a/storage/heap/heapdef.h +++ b/storage/heap/heapdef.h @@ -50,6 +50,7 @@ typedef struct st_hp_hash_info { struct st_hp_hash_info *next_key; uchar *ptr_to_rec; + ulong hash_of_key; } HASH_INFO; typedef struct { diff --git a/storage/heap/hp_create.c b/storage/heap/hp_create.c index e87f09feb18..baa2f06cf4b 100644 --- a/storage/heap/hp_create.c +++ b/storage/heap/hp_create.c @@ -241,12 +241,20 @@ static void init_block(HP_BLOCK *block, uint reclength, ulong min_records, max_records= 1000; /* As good as quess as anything */ recbuffer= (uint) (reclength + sizeof(uchar**) - 1) & ~(sizeof(uchar**) - 1); records_in_block= max_records / 10; - if (records_in_block < 10 && max_records) + + /* + We don't want too few records_in_block as otherwise the overhead of + of the HP_PTRS block will be too notable + */ + records_in_block= min(1000, max_records); + if (records_in_block < 10) records_in_block= 10; - if (!records_in_block || records_in_block*recbuffer > + + /* The + 1 is there to ensure that we get at least 1 row per level */ + if (records_in_block*recbuffer > (my_default_record_cache_size-sizeof(HP_PTRS)*HP_MAX_LEVELS)) records_in_block= (my_default_record_cache_size - sizeof(HP_PTRS) * - HP_MAX_LEVELS) / recbuffer + 1; + HP_MAX_LEVELS) / recbuffer + 1; block->records_in_block= records_in_block; block->recbuffer= recbuffer; block->last_allocated= 0L; diff --git a/storage/heap/hp_delete.c b/storage/heap/hp_delete.c index db2c0df6128..d00ac94a918 100644 --- a/storage/heap/hp_delete.c +++ b/storage/heap/hp_delete.c @@ -149,8 +149,9 @@ int hp_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo, else if (pos->next_key) { empty=pos->next_key; - pos->ptr_to_rec=empty->ptr_to_rec; - pos->next_key=empty->next_key; + pos->ptr_to_rec= empty->ptr_to_rec; + pos->next_key= empty->next_key; + pos->hash_of_key= empty->hash_of_key; } else keyinfo->hash_buckets--; @@ -159,7 +160,7 @@ int hp_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo, DBUG_RETURN (0); /* Move the last key (lastpos) */ - lastpos_hashnr = hp_rec_hashnr(keyinfo, lastpos->ptr_to_rec); + lastpos_hashnr= lastpos->hash_of_key; /* pos is where lastpos should be */ pos=hp_find_hash(&keyinfo->block, hp_mask(lastpos_hashnr, share->blength, share->records)); @@ -168,7 +169,7 @@ int hp_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo, empty[0]=lastpos[0]; DBUG_RETURN(0); } - pos_hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec); + pos_hashnr= pos->hash_of_key; /* pos3 is where the pos should be */ pos3= hp_find_hash(&keyinfo->block, hp_mask(pos_hashnr, share->blength, share->records)); diff --git a/storage/heap/hp_hash.c b/storage/heap/hp_hash.c index 208453309aa..5c96bb771e8 100644 --- a/storage/heap/hp_hash.c +++ b/storage/heap/hp_hash.c @@ -149,8 +149,8 @@ uchar *hp_search(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *key, { flag=0; /* Reset flag */ if (hp_find_hash(&keyinfo->block, - hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec), - share->blength, share->records)) != pos) + hp_mask(pos->hash_of_key, + share->blength, share->records)) != pos) break; /* Wrong link */ } } @@ -300,7 +300,9 @@ ulong hp_hashnr(register HP_KEYDEF *keydef, register const uchar *key) } } } +#ifdef ONLY_FOR_HASH_DEBUGGING DBUG_PRINT("exit", ("hash: 0x%lx", nr)); +#endif return((ulong) nr); } @@ -367,7 +369,9 @@ ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec) } } } +#ifdef ONLY_FOR_HASH_DEBUGGING DBUG_PRINT("exit", ("hash: 0x%lx", nr)); +#endif return(nr); } @@ -438,7 +442,9 @@ ulong hp_hashnr(register HP_KEYDEF *keydef, register const uchar *key) } } } +#ifdef ONLY_FOR_HASH_DEBUGGING DBUG_PRINT("exit", ("hash: 0x%lx", nr)); +#endif return(nr); } @@ -491,7 +497,9 @@ ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec) } } } +#ifdef ONLY_FOR_HASH_DEBUGGING DBUG_PRINT("exit", ("hash: 0x%lx", nr)); +#endif return(nr); } diff --git a/storage/heap/hp_test2.c b/storage/heap/hp_test2.c index fe6ef254218..a534a5cf904 100644 --- a/storage/heap/hp_test2.c +++ b/storage/heap/hp_test2.c @@ -72,7 +72,7 @@ int main(int argc, char *argv[]) get_options(argc,argv); bzero(&hp_create_info, sizeof(hp_create_info)); - hp_create_info.max_table_size= 1024L*1024L; + hp_create_info.max_table_size= 2*1024L*1024L; write_count=update=opt_delete=0; key_check=0; @@ -642,7 +642,7 @@ static int get_options(int argc,char *argv[]) case 'V': case 'I': case '?': - printf("%s Ver 1.1 for %s at %s\n",progname,SYSTEM_TYPE,MACHINE_TYPE); + printf("%s Ver 1.2 for %s at %s\n",progname,SYSTEM_TYPE,MACHINE_TYPE); puts("TCX Datakonsult AB, by Monty, for your professional use\n"); printf("Usage: %s [-?ABIKLsWv] [-m#] [-t#]\n",progname); exit(0); diff --git a/storage/heap/hp_write.c b/storage/heap/hp_write.c index 300813218ae..42d924d8f2d 100644 --- a/storage/heap/hp_write.c +++ b/storage/heap/hp_write.c @@ -154,6 +154,13 @@ static uchar *next_free_record_pos(HP_SHARE *info) if ((info->records > info->max_records && info->max_records) || (info->data_length + info->index_length >= info->max_table_size)) { + DBUG_PRINT("error", + ("record file full. records: %u max_records: %lu " + "data_length: %llu index_length: %llu " + "max_table_size: %llu", + info->records, info->max_records, + info->data_length, info->index_length, + info->max_table_size)); my_errno=HA_ERR_RECORD_FILE_FULL; DBUG_RETURN(NULL); } @@ -200,6 +207,7 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, HP_SHARE *share = info->s; int flag; ulong halfbuff,hashnr,first_index; + ulong UNINIT_VAR(hash_of_key), UNINIT_VAR(hash_of_key2); uchar *UNINIT_VAR(ptr_to_rec),*UNINIT_VAR(ptr_to_rec2); HASH_INFO *empty,*UNINIT_VAR(gpos),*UNINIT_VAR(gpos2),*pos; DBUG_ENTER("hp_write_key"); @@ -230,7 +238,7 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, { do { - hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec); + hashnr = pos->hash_of_key; if (flag == 0) { /* @@ -262,7 +270,6 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, flag=LOWFIND | HIGHFIND; /* key shall be moved to the current empty position */ gpos=empty; - ptr_to_rec=pos->ptr_to_rec; empty=pos; /* This place is now free */ } else @@ -273,7 +280,6 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, */ flag=LOWFIND | LOWUSED; gpos=pos; - ptr_to_rec=pos->ptr_to_rec; } } else @@ -282,13 +288,15 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, if (!(flag & LOWUSED)) { /* Change link of previous lower-list key */ - gpos->ptr_to_rec=ptr_to_rec; - gpos->next_key=pos; + gpos->ptr_to_rec= ptr_to_rec; + gpos->next_key= pos; + gpos->hash_of_key= hash_of_key; flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED); } gpos=pos; - ptr_to_rec=pos->ptr_to_rec; } + ptr_to_rec= pos->ptr_to_rec; + hash_of_key= pos->hash_of_key; } else { @@ -299,20 +307,21 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, /* key shall be moved to the last (empty) position */ gpos2= empty; empty= pos; - ptr_to_rec2=pos->ptr_to_rec; } else { if (!(flag & HIGHUSED)) { /* Change link of previous upper-list key and save */ - gpos2->ptr_to_rec=ptr_to_rec2; - gpos2->next_key=pos; + gpos2->ptr_to_rec= ptr_to_rec2; + gpos2->next_key= pos; + gpos2->hash_of_key= hash_of_key2; flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED); } gpos2=pos; - ptr_to_rec2=pos->ptr_to_rec; } + ptr_to_rec2= pos->ptr_to_rec; + hash_of_key2= pos->hash_of_key; } } while ((pos=pos->next_key)); @@ -328,23 +337,27 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, if ((flag & (LOWFIND | LOWUSED)) == LOWFIND) { - gpos->ptr_to_rec=ptr_to_rec; - gpos->next_key=0; + gpos->ptr_to_rec= ptr_to_rec; + gpos->hash_of_key= hash_of_key; + gpos->next_key= 0; } if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND) { - gpos2->ptr_to_rec=ptr_to_rec2; - gpos2->next_key=0; + gpos2->ptr_to_rec= ptr_to_rec2; + gpos2->hash_of_key= hash_of_key2; + gpos2->next_key= 0; } } /* Check if we are at the empty position */ - pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record), - share->blength, share->records + 1)); + hash_of_key= hp_rec_hashnr(keyinfo, record); + pos=hp_find_hash(&keyinfo->block, + hp_mask(hash_of_key, share->blength, share->records + 1)); if (pos == empty) { - pos->ptr_to_rec=recpos; - pos->next_key=0; + pos->ptr_to_rec= recpos; + pos->hash_of_key= hash_of_key; + pos->next_key= 0; keyinfo->hash_buckets++; } else @@ -352,18 +365,17 @@ int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, /* Check if more records in same hash-nr family */ empty[0]=pos[0]; gpos=hp_find_hash(&keyinfo->block, - hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec), + hp_mask(pos->hash_of_key, share->blength, share->records + 1)); + + pos->ptr_to_rec= recpos; + pos->hash_of_key= hash_of_key; if (pos == gpos) - { - pos->ptr_to_rec=recpos; pos->next_key=empty; - } else { keyinfo->hash_buckets++; - pos->ptr_to_rec=recpos; - pos->next_key=0; + pos->next_key= 0; hp_movelink(pos, gpos, empty); } diff --git a/storage/maria/ma_create.c b/storage/maria/ma_create.c index b773e930e82..86709c09200 100644 --- a/storage/maria/ma_create.c +++ b/storage/maria/ma_create.c @@ -1337,11 +1337,12 @@ int _ma_update_state_lsns(MARIA_SHARE *share, LSN lsn, TrID create_trid, my_bool do_sync, my_bool update_create_rename_lsn) { int res; + DBUG_ENTER("_ma_update_state_lsns"); pthread_mutex_lock(&share->intern_lock); res= _ma_update_state_lsns_sub(share, lsn, create_trid, do_sync, update_create_rename_lsn); pthread_mutex_unlock(&share->intern_lock); - return res; + DBUG_RETURN(res); } From 8e825a2249440e7df0fce0123f6d543b09b29194 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 23 Mar 2012 18:18:16 +0200 Subject: [PATCH 10/12] Fixes lp:941889 "JOIN constructors takes a long time in 5.3" - Remove all references of MAX_TABLES from JOIN struct and make these dynamic - Updated Join_plan_state to allocate just as many elements as it's needed sql/opt_subselect.cc: Optimized version of Join_plan_state sql/sql_select.cc: Set join->positions and join->best_positions dynamicly Don't call update_virtual_fields() if table->vfield is not set. sql/sql_select.h: Remove all references of MAX_TABLES from JOIN struct and Join_plan_state and make these dynamic --- sql/opt_subselect.cc | 3 ++- sql/sql_select.cc | 23 +++++++++++++++++------ sql/sql_select.h | 32 +++++++++++++++++++++++--------- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a65f53a86c2..6dcfff4d609 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4961,7 +4961,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables) DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed); DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed); - Join_plan_state save_qep; /* The original QEP of the subquery. */ + /* The original QEP of the subquery. */ + Join_plan_state save_qep(table_count); /* Compute and compare the costs of materialization and in-exists if both diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a526de5a256..9d04204848d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3046,12 +3046,11 @@ make_join_statistics(JOIN *join, List &tables_list, key_map const_ref, eq_part; bool has_expensive_keyparts; TABLE **table_vector; - JOIN_TAB *stat,*stat_end,*s,**stat_ref; + JOIN_TAB *stat,*stat_end,*s,**stat_ref, **stat_vector; KEYUSE *keyuse,*start_keyuse; table_map outer_join=0; table_map no_rows_const_tables= 0; SARGABLE_PARAM *sargables= 0; - JOIN_TAB *stat_vector[MAX_TABLES+1]; List_iterator ti(tables_list); TABLE_LIST *tables; DBUG_ENTER("make_join_statistics"); @@ -3060,9 +3059,19 @@ make_join_statistics(JOIN *join, List &tables_list, table_count=join->table_count; stat=(JOIN_TAB*) join->thd->calloc(sizeof(JOIN_TAB)*(table_count)); - stat_ref=(JOIN_TAB**) join->thd->alloc(sizeof(JOIN_TAB*)*MAX_TABLES); + stat_ref=(JOIN_TAB**) join->thd->alloc(sizeof(JOIN_TAB*)* + (MAX_TABLES + table_count + 1)); + stat_vector= stat_ref + MAX_TABLES; table_vector=(TABLE**) join->thd->alloc(sizeof(TABLE*)*(table_count*2)); - if (!stat || !stat_ref || !table_vector) + join->positions= new (join->thd->mem_root) POSITION[(table_count+1)]; + /* + best_positions is ok to allocate with alloc() as we copy things to it with + memcpy() + */ + join->best_positions= (POSITION*) join->thd->alloc(sizeof(POSITION)* + (table_count +1)); + + if (join->thd->is_fatal_error) DBUG_RETURN(1); // Eom /* purecov: inspected */ join->best_ref=stat_vector; @@ -15927,7 +15936,8 @@ join_read_system(JOIN_TAB *tab) empty_record(table); // Make empty record return -1; } - update_virtual_fields(tab->join->thd, table); + if (table->vfield) + update_virtual_fields(tab->join->thd, table); store_record(table,record[1]); } else if (!table->status) // Only happens with left join @@ -15976,7 +15986,8 @@ join_read_const(JOIN_TAB *tab) return report_error(table, error); return -1; } - update_virtual_fields(tab->join->thd, table); + if (table->vfield) + update_virtual_fields(tab->join->thd, table); store_record(table,record[1]); } else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join diff --git a/sql/sql_select.h b/sql/sql_select.h index a5fa59a070a..a5c0a797ed6 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -742,7 +742,7 @@ public: Information about a position of table within a join order. Used in join optimization. */ -typedef struct st_position +typedef struct st_position :public Sql_alloc { /* The table that's put into join order */ JOIN_TAB *table; @@ -844,23 +844,36 @@ protected: */ class Join_plan_state { public: - DYNAMIC_ARRAY keyuse; /* Copy of the JOIN::keyuse array. */ - POSITION best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */ + DYNAMIC_ARRAY keyuse; /* Copy of the JOIN::keyuse array. */ + POSITION *best_positions; /* Copy of JOIN::best_positions */ /* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */ - KEYUSE *join_tab_keyuse[MAX_TABLES]; + KEYUSE **join_tab_keyuse; /* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */ - key_map join_tab_checked_keys[MAX_TABLES]; - SJ_MATERIALIZATION_INFO *sj_mat_info[MAX_TABLES]; + key_map *join_tab_checked_keys; + SJ_MATERIALIZATION_INFO **sj_mat_info; + my_bool error; public: - Join_plan_state() + Join_plan_state(uint tables) : error(0) { keyuse.elements= 0; keyuse.buffer= NULL; + best_positions= 0; /* To detect errors */ + error= my_multi_malloc(MYF(MY_WME), + &best_positions, + sizeof(*best_positions) * (tables + 1), + &join_tab_keyuse, + sizeof(*join_tab_keyuse) * tables, + &join_tab_checked_keys, + sizeof(*join_tab_checked_keys) * tables, + &sj_mat_info, + sizeof(sj_mat_info) * tables, + NullS) == 0; } Join_plan_state(JOIN *join); ~Join_plan_state() { delete_dynamic(&keyuse); + my_free(best_positions, MYF(0)); } }; @@ -961,7 +974,7 @@ public: */ ha_rows fetch_limit; /* Finally picked QEP. This is result of join optimization */ - POSITION best_positions[MAX_TABLES+1]; + POSITION *best_positions; /******* Join optimization state members start *******/ /* @@ -971,7 +984,7 @@ public: TABLE_LIST *emb_sjm_nest; /* Current join optimization state */ - POSITION positions[MAX_TABLES+1]; + POSITION *positions; /* Bitmap of nested joins embedding the position at the end of the current @@ -1241,6 +1254,7 @@ public: exec_const_cond= 0; group_optimized_away= 0; no_rows_in_result_called= 0; + positions= best_positions= 0; all_fields= fields_arg; if (&fields_list != &fields_arg) /* Avoid valgrind-warning */ From c36bdf1c889539413d5369ae628aff97d044b276 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 23 Mar 2012 18:22:39 +0200 Subject: [PATCH 11/12] Speedup: - Don't call update_virtual_fields() if table->vfield is not set - Don't prealloc memory for in open_tables() as this is very seldom used. sql/records.cc: Don't call update_virtual_fields() if table->vfield is not set sql/sql_base.cc: Don't prealloc memory for in open_tables() as this is very seldom used. Don't call update_virtual_fields() if table->vfield is not set sql/sql_delete.cc: Don't call update_virtual_fields() if table->vfield is not set sql/sql_handler.cc: Don't call update_virtual_fields() if table->vfield is not set sql/sql_join_cache.cc: Don't call update_virtual_fields() if table->vfield is not set Move some frequent values to local variables sql/sql_table.cc: Don't call update_virtual_fields() if table->vfield is not set sql/sql_update.cc: Don't call update_virtual_fields() if table->vfield is not set sql/table.cc: Assert if update_virtual_fields is called with wrong parameters --- sql/records.cc | 2 +- sql/sql_base.cc | 13 ++++--------- sql/sql_delete.cc | 3 ++- sql/sql_handler.cc | 3 ++- sql/sql_join_cache.cc | 18 +++++++++++------- sql/sql_table.cc | 6 ++++-- sql/sql_update.cc | 6 ++++-- sql/table.cc | 3 +-- 8 files changed, 29 insertions(+), 25 deletions(-) diff --git a/sql/records.cc b/sql/records.cc index 648f939cf73..5c495a16562 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -401,7 +401,7 @@ int rr_sequential(READ_RECORD *info) break; } } - if (!tmp) + if (!tmp && info->table->vfield) update_virtual_fields(info->thd, info->table); return tmp; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 19fabd27d72..03d8a925fc2 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4706,7 +4706,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) temporary mem_root for new .frm parsing. TODO: variables for size */ - init_sql_alloc(&new_frm_mem, 8024, 8024); + init_sql_alloc(&new_frm_mem, 8024, 0); thd->current_tablenr= 0; restart: @@ -8648,14 +8648,9 @@ fill_record(THD * thd, List &fields, List &values, } /* Update virtual fields*/ thd->abort_on_warning= FALSE; - if (vcol_table) - { - if (vcol_table->vfield) - { - if (update_virtual_fields(thd, vcol_table, TRUE)) - goto err; - } - } + if (vcol_table && vcol_table->vfield && + update_virtual_fields(thd, vcol_table, TRUE)) + goto err; thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; DBUG_RETURN(thd->is_error()); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 84e88196f20..24b1828b177 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -332,7 +332,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, while (!(error=info.read_record(&info)) && !thd->killed && ! thd->is_error()) { - update_virtual_fields(thd, table); + if (table->vfield) + update_virtual_fields(thd, table); thd->examined_row_count++; // thd->is_error() is tested to disallow delete row on error if (!select || select->skip_record(thd) > 0) diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 4f1c63930d6..47d78abdb68 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -827,7 +827,8 @@ retry: goto ok; } /* Generate values for virtual fields */ - update_virtual_fields(thd, table); + if (table->vfield) + update_virtual_fields(thd, table); if (cond && !cond->val_int()) continue; if (num_rows >= offset_limit_cnt) diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index a960c63e782..d49be2e446c 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -3344,23 +3344,26 @@ int JOIN_TAB_SCAN::next() int skip_rc; READ_RECORD *info= &join_tab->read_record; SQL_SELECT *select= join_tab->cache_select; + TABLE *table= join_tab->table; + THD *thd= join->thd; + if (is_first_record) is_first_record= FALSE; else err= info->read_record(info); - if (!err) - update_virtual_fields(join->thd, join_tab->table); - while (!err && select && (skip_rc= select->skip_record(join->thd)) <= 0) + if (!err && table->vfield) + update_virtual_fields(thd, table); + while (!err && select && (skip_rc= select->skip_record(thd)) <= 0) { - if (join->thd->killed || skip_rc < 0) + if (thd->killed || skip_rc < 0) return 1; /* Move to the next record if the last retrieved record does not meet the condition pushed to the table join_tab. */ err= info->read_record(info); - if (!err) - update_virtual_fields(join->thd, join_tab->table); + if (!err && table->vfield) + update_virtual_fields(thd, table); } return err; } @@ -3874,7 +3877,8 @@ int JOIN_TAB_SCAN_MRR::next() */ DBUG_ASSERT(cache->buff <= (uchar *) (*ptr) && (uchar *) (*ptr) <= cache->end_pos); - update_virtual_fields(join->thd, join_tab->table); + if (join_tab->table->vfield) + update_virtual_fields(join->thd, join_tab->table); } return rc; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 222f6e07bc2..1b9ec320f7e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -8078,7 +8078,8 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, error= 1; break; } - update_virtual_fields(thd, from); + if (from->vfield) + update_virtual_fields(thd, from); thd->row_count++; if (++thd->progress.counter >= time_to_report_progress) { @@ -8106,7 +8107,8 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, copy_ptr->do_copy(copy_ptr); } prev_insert_id= to->file->next_insert_id; - update_virtual_fields(thd, to, TRUE); + if (to->vfield) + update_virtual_fields(thd, to, TRUE); if (thd->is_error()) { error= 1; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index a921a87884e..cf03cc597c8 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -506,7 +506,8 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { - update_virtual_fields(thd, table); + if (table->vfield) + update_virtual_fields(thd, table); thd->examined_row_count++; if (!select || (error= select->skip_record(thd)) > 0) { @@ -621,7 +622,8 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { - update_virtual_fields(thd, table); + if (table->vfield) + update_virtual_fields(thd, table); thd->examined_row_count++; if (!select || select->skip_record(thd) > 0) { diff --git a/sql/table.cc b/sql/table.cc index 2d435b671e5..7a337521040 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5759,8 +5759,7 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write) DBUG_ENTER("update_virtual_fields"); Field **vfield_ptr, *vfield; int error __attribute__ ((unused))= 0; - if (!table || !table->vfield) - DBUG_RETURN(0); + DBUG_ASSERT(table && table->vfield); thd->reset_arena_for_cached_items(table->expr_arena); /* Iterate over virtual fields in the table */ From f72e0e686b2f3688fe98685107a293de5012be03 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 24 Mar 2012 17:08:59 +0100 Subject: [PATCH 12/12] Improve filesort performance for small sorts: Don't write pointers to records that we will never use. --- sql/filesort.cc | 65 ++++++++++++++++++++----------------------------- sql/table.h | 1 + 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/sql/filesort.cc b/sql/filesort.cc index 0fe5f899351..00cfb75cb58 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -41,12 +41,11 @@ if (my_b_write((file),(uchar*) (from),param->ref_length)) \ /* functions defined in this file */ -static char **make_char_array(char **old_pos, register uint fields, - uint length, myf my_flag); static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count, uchar *buf); static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select, - uchar * *sort_keys, IO_CACHE *buffer_file, + uchar * *sort_keys, uchar *sort_keys_buf, + IO_CACHE *buffer_file, IO_CACHE *tempfile,IO_CACHE *indexfile); static int write_keys(SORTPARAM *param,uchar * *sort_keys, uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile); @@ -211,20 +210,26 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, memavl= thd->variables.sortbuff_size; min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2); - while (memavl >= min_sort_memory) + if (!table_sort.sort_keys) { - ulong old_memavl; - ulong keys= memavl/(param.rec_length+sizeof(char*)); - param.keys=(uint) min(records+1, keys); - if ((table_sort.sort_keys= - (uchar **) make_char_array((char **) table_sort.sort_keys, - param.keys, param.rec_length, MYF(0)))) - break; - old_memavl=memavl; - if ((memavl=memavl/4*3) < min_sort_memory && old_memavl > min_sort_memory) - memavl= min_sort_memory; + while (memavl >= min_sort_memory) + { + ulong old_memavl; + ulong keys= memavl/(param.rec_length+sizeof(char*)); + table_sort.keys= (uint) min(records+1, keys); + if ((table_sort.sort_keys= + (uchar**) my_malloc(table_sort.keys*(param.rec_length+sizeof(char*)), + MYF(0)))) + break; + old_memavl=memavl; + if ((memavl=memavl/4*3) < min_sort_memory && + old_memavl > min_sort_memory) + memavl= min_sort_memory; + } } + sort_keys= table_sort.sort_keys; + param.keys= table_sort.keys - 1; /* TODO: check why we do this " - 1" */ if (memavl < min_sort_memory) { my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG)); @@ -234,10 +239,10 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, DISK_BUFFER_SIZE, MYF(ME_ERROR | MY_WME))) goto err; - param.keys--; /* TODO: check why we do this */ param.sort_form= table; param.end=(param.local_sortorder=sortorder)+s_length; - if ((records=find_all_keys(¶m,select,sort_keys, &buffpek_pointers, + if ((records=find_all_keys(¶m,select,sort_keys, + (uchar *)(sort_keys+param.keys), &buffpek_pointers, &tempfile, selected_records_file)) == HA_POS_ERROR) goto err; @@ -364,26 +369,6 @@ void filesort_free_buffers(TABLE *table, bool full) } } -/** Make a array of string pointers. */ - -static char **make_char_array(char **old_pos, register uint fields, - uint length, myf my_flag) -{ - register char **pos; - char *char_pos; - DBUG_ENTER("make_char_array"); - - if (old_pos || - (old_pos= (char**) my_malloc((uint) fields*(length+sizeof(char*)), - my_flag))) - { - pos=old_pos; char_pos=((char*) (pos+fields)) -length; - while (fields--) *(pos++) = (char_pos+= length); - } - - DBUG_RETURN(old_pos); -} /* make_char_array */ - /** Read 'count' number of buffer pointers into memory. */ @@ -498,7 +483,7 @@ static void dbug_print_record(TABLE *table, bool print_rowid) */ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, - uchar **sort_keys, + uchar **sort_keys, uchar *sort_keys_buf, IO_CACHE *buffpek_pointers, IO_CACHE *tempfile, IO_CACHE *indexfile) { @@ -511,6 +496,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, volatile killed_state *killed= &thd->killed; handler *file; MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set; + uchar *next_sort_key= sort_keys_buf; DBUG_ENTER("find_all_keys"); DBUG_PRINT("info",("using: %s", (select ? select->quick ? "ranges" : "where": @@ -652,9 +638,12 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) DBUG_RETURN(HA_POS_ERROR); idx=0; + next_sort_key= sort_keys_buf; indexpos++; } - make_sortkey(param,sort_keys[idx++],ref_pos); + sort_keys[idx++]= next_sort_key; + make_sortkey(param, next_sort_key, ref_pos); + next_sort_key+= param->rec_length; } else file->unlock_row(); diff --git a/sql/table.h b/sql/table.h index 920d2fa7818..710359b04c2 100644 --- a/sql/table.h +++ b/sql/table.h @@ -161,6 +161,7 @@ typedef struct st_filesort_info { IO_CACHE *io_cache; /* If sorted through filesort */ uchar **sort_keys; /* Buffer for sorting keys */ + uint keys; /* Number of key pointers in buffer */ uchar *buffpek; /* Buffer for buffpek structures */ uint buffpek_len; /* Max number of buffpeks in the buffer */ uchar *addon_buf; /* Pointer to a buffer if sorted with fields */