diff options
-rw-r--r-- | BitKeeper/etc/logging_ok | 2 | ||||
-rw-r--r-- | configure.in | 3 | ||||
-rw-r--r-- | include/config-win.h | 17 | ||||
-rw-r--r-- | include/my_global.h | 2 | ||||
-rw-r--r-- | mysql-test/r/cast.result | 71 | ||||
-rw-r--r-- | mysql-test/r/innodb-replace.result | 1 | ||||
-rw-r--r-- | mysql-test/t/cast.test | 26 | ||||
-rw-r--r-- | mysql-test/t/innodb-replace.test | 4 | ||||
-rw-r--r-- | mysys/default.c | 135 | ||||
-rw-r--r-- | mysys/mf_keycache.c | 11 | ||||
-rw-r--r-- | sql/field.h | 2 | ||||
-rw-r--r-- | sql/hostname.cc | 9 | ||||
-rw-r--r-- | sql/item.h | 7 | ||||
-rw-r--r-- | sql/item_func.cc | 73 | ||||
-rw-r--r-- | sql/item_func.h | 9 | ||||
-rw-r--r-- | sql/sql_insert.cc | 4 | ||||
-rw-r--r-- | sql/sql_select.cc | 12 | ||||
-rwxr-xr-x | tests/index_corrupt.pl | 212 |
18 files changed, 518 insertions, 82 deletions
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index e706a3358d5..346dcae3cfd 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -50,6 +50,8 @@ dlenev@build.mysql.com dlenev@jabberwock.localdomain dlenev@mysql.com ejonore@mc03.ndb.mysql.com +evgen@moonbone.(none) +evgen@moonbone.local gbichot@production.mysql.com gbichot@quadita2.mysql.com gbichot@quadxeon.mysql.com diff --git a/configure.in b/configure.in index da707080006..e3dba45ca19 100644 --- a/configure.in +++ b/configure.in @@ -1798,6 +1798,9 @@ If you are using gcc 2.8.# you should upgrade to egcs 1.0.3 or newer and try again]); fi fi +AC_CHECK_TYPES([sigset_t, off_t], [], [], [#include <sys/types.h>]) +AC_CHECK_TYPES([size_t], [], [], [#include <stdio.h>]) + MYSQL_PTHREAD_YIELD ###################################################################### diff --git a/include/config-win.h b/include/config-win.h index bc392ce73d8..a4f81a0ec6a 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -106,20 +106,33 @@ functions */ /* Type information */ +#if defined(__EMX__) || !defined(HAVE_UINT) +#undef HAVE_UINT +#define HAVE_UINT typedef unsigned short ushort; typedef unsigned int uint; +#endif /* defined(__EMX__) || !defined(HAVE_UINT) */ + typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */ typedef __int64 longlong; +#ifndef HAVE_SIGSET_T typedef int sigset_t; +#endif #define longlong_defined -/* off_t should not be __int64 because of conflicts in header files; - Use my_off_t or os_off_t instead */ +/* + off_t should not be __int64 because of conflicts in header files; + Use my_off_t or os_off_t instead +*/ +#ifndef HAVE_OFF_T typedef long off_t; +#endif typedef __int64 os_off_t; #ifdef _WIN64 typedef UINT_PTR rf_SetTimer; #else +#ifndef HAVE_SIZE_T typedef unsigned int size_t; +#endif typedef uint rf_SetTimer; #endif diff --git a/include/my_global.h b/include/my_global.h index 74846fe1762..e816d52cc71 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -425,6 +425,8 @@ int __void__; #endif #if defined(__EMX__) || !defined(HAVE_UINT) +#undef HAVE_UINT +#define HAVE_UINT typedef unsigned int uint; typedef unsigned short ushort; #endif diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index feefd47c611..c5fa5d076cc 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -4,9 +4,6 @@ CAST(1-2 AS UNSIGNED) select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER); CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER) -1 -select CONVERT('-1',UNSIGNED); -CONVERT('-1',UNSIGNED) -18446744073709551615 select CAST('10 ' as unsigned integer); CAST('10 ' as unsigned integer) 10 @@ -100,6 +97,41 @@ select 10E+0+'a'; 10 Warnings: Warning 1292 Truncated incorrect DOUBLE value: 'a' +select cast('18446744073709551616' as unsigned); +cast('18446744073709551616' as unsigned) +18446744073709551615 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: '18446744073709551616' +select cast('18446744073709551616' as signed); +cast('18446744073709551616' as signed) +-1 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: '18446744073709551616' +select cast('9223372036854775809' as signed); +cast('9223372036854775809' as signed) +-9223372036854775807 +Warnings: +Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement +select cast('-1' as unsigned); +cast('-1' as unsigned) +18446744073709551615 +Warnings: +Warning 1105 Cast to unsigned converted negative integer to it's positive complement +select cast('abc' as signed); +cast('abc' as signed) +0 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'abc' +select cast('1a' as signed); +cast('1a' as signed) +1 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: '1a' +select cast('' as signed); +cast('' as signed) +0 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: '' set names binary; select cast(_latin1'test' as char character set latin2); cast(_latin1'test' as char character set latin2) @@ -255,6 +287,39 @@ timediff(cast('2004-12-30 12:00:00' as time), '12:00:00') select timediff(cast('1 12:00:00' as time), '12:00:00'); timediff(cast('1 12:00:00' as time), '12:00:00') 24:00:00 +select cast(18446744073709551615 as unsigned); +cast(18446744073709551615 as unsigned) +18446744073709551615 +select cast(18446744073709551615 as signed); +cast(18446744073709551615 as signed) +-1 +select cast('18446744073709551615' as unsigned); +cast('18446744073709551615' as unsigned) +18446744073709551615 +select cast('18446744073709551615' as signed); +cast('18446744073709551615' as signed) +-1 +Warnings: +Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement +select cast('9223372036854775807' as signed); +cast('9223372036854775807' as signed) +9223372036854775807 +select cast(concat('184467440','73709551615') as unsigned); +cast(concat('184467440','73709551615') as unsigned) +18446744073709551615 +select cast(concat('184467440','73709551615') as signed); +cast(concat('184467440','73709551615') as signed) +-1 +Warnings: +Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement +select cast(repeat('1',20) as unsigned); +cast(repeat('1',20) as unsigned) +11111111111111111111 +select cast(repeat('1',20) as signed); +cast(repeat('1',20) as signed) +-7335632962598440505 +Warnings: +Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement select cast('1.2' as decimal(3,2)); cast('1.2' as decimal(3,2)) 1.20 diff --git a/mysql-test/r/innodb-replace.result b/mysql-test/r/innodb-replace.result index a27806640ad..b7edcc49e56 100644 --- a/mysql-test/r/innodb-replace.result +++ b/mysql-test/r/innodb-replace.result @@ -1,3 +1,4 @@ +drop table if exists t1; create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb; select * from t1; c1 c2 stamp diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test index e7dd49394ee..cafecd6000d 100644 --- a/mysql-test/t/cast.test +++ b/mysql-test/t/cast.test @@ -4,7 +4,6 @@ select CAST(1-2 AS UNSIGNED); select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER); -select CONVERT('-1',UNSIGNED); select CAST('10 ' as unsigned integer); select cast(-5 as unsigned) | 1, cast(-5 as unsigned) & -1; select cast(-5 as unsigned) -1, cast(-5 as unsigned) + 1; @@ -34,6 +33,15 @@ select 10+'a'; select 10.0+cast('a' as decimal); select 10E+0+'a'; +# out-of-range cases +select cast('18446744073709551616' as unsigned); +select cast('18446744073709551616' as signed); +select cast('9223372036854775809' as signed); +select cast('-1' as unsigned); +select cast('abc' as signed); +select cast('1a' as signed); +select cast('' as signed); + # # Character set convertion # @@ -132,6 +140,22 @@ select timediff(cast('2004-12-30 12:00:00' as time), '12:00:00'); # Still we should not throw away "days" part of time value select timediff(cast('1 12:00:00' as time), '12:00:00'); +# +# Bug #7036: Casting from string to unsigned would cap value of result at +# maximum signed value instead of maximum unsigned value +# +select cast(18446744073709551615 as unsigned); +select cast(18446744073709551615 as signed); +select cast('18446744073709551615' as unsigned); +select cast('18446744073709551615' as signed); +select cast('9223372036854775807' as signed); + +select cast(concat('184467440','73709551615') as unsigned); +select cast(concat('184467440','73709551615') as signed); + +select cast(repeat('1',20) as unsigned); +select cast(repeat('1',20) as signed); + #decimal-related additions select cast('1.2' as decimal(3,2)); select 1e18 * cast('1.2' as decimal(3,2)); diff --git a/mysql-test/t/innodb-replace.test b/mysql-test/t/innodb-replace.test index e7e96da1443..516f058a68e 100644 --- a/mysql-test/t/innodb-replace.test +++ b/mysql-test/t/innodb-replace.test @@ -2,6 +2,10 @@ # embedded server ignores 'delayed', so skip this -- source include/not_embedded.inc +--disable_warnings +drop table if exists t1; +--enable_warnings + # # Bug #1078 # diff --git a/mysys/default.c b/mysys/default.c index 0f33c94d17e..aa2293ac0af 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -410,6 +410,56 @@ static int search_default_file(Process_option_func opt_handler, /* + Skip over keyword and get argument after keyword + + SYNOPSIS + get_argument() + keyword Include directive keyword + kwlen Length of keyword + ptr Pointer to the keword in the line under process + line line number + + RETURN + 0 error + # Returns pointer to the argument after the keyword. +*/ + +static char *get_argument(const char *keyword, uint kwlen, + char *ptr, char *name, uint line) +{ + char *end; + + /* Skip over "include / includedir keyword" and following whitespace */ + + for (ptr+= kwlen - 1; + my_isspace(&my_charset_latin1, ptr[0]); + ptr++) + {} + + /* + Trim trailing whitespace from directory name + The -1 below is for the newline added by fgets() + Note that my_isspace() is true for \r and \n + */ + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; /* Cut off end space */ + + /* Print error msg if there is nothing after !include* directive */ + if (end <= ptr) + { + fprintf(stderr, + "error: Wrong '!%s' directive in config file: %s at line %d\n", + keyword, name, line); + return 0; + } + return ptr; +} + + +/* Open a configuration file (if exists) and read given options from it SYNOPSIS @@ -497,40 +547,34 @@ static int search_default_file_with_ext(Process_option_func opt_handler, continue; /* Configuration File Directives */ - if ((*ptr == '!') && (recursion_level < max_recursion_level)) + if ((*ptr == '!')) { + if (recursion_level >= max_recursion_level) + { + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; + fprintf(stderr, + "Warning: skipping '%s' directive as maximum include" + "recursion level was reached in file %s at line %d\n", + ptr, name, line); + continue; + } + /* skip over `!' and following whitespace */ for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++) {} - if ((!strncmp(ptr, includedir_keyword, sizeof(includedir_keyword) - 1)) - && my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) + if ((!strncmp(ptr, includedir_keyword, + sizeof(includedir_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) { - /* skip over "includedir" and following whitespace */ - for (ptr+= sizeof(includedir_keyword) - 1; - my_isspace(&my_charset_latin1, ptr[0]); ptr++) - {} - - /* trim trailing whitespace from directory name */ - end= ptr + strlen(ptr) - 1; - /* fgets() stores the newline character in the buffer */ - if ((end[0] == '\n') || (end[0] == '\r') || - my_isspace(&my_charset_latin1, end[0])) - { - for (; my_isspace(&my_charset_latin1, *(end - 1)); end--) - {} - end[0]= 0; - } - - /* print error msg if there is nothing after !includedir directive */ - if (end == ptr) - { - fprintf(stderr, - "error: Wrong !includedir directive in config " - "file: %s at line %d\n", - name,line); - goto err; - } + if (!(ptr= get_argument(includedir_keyword, + sizeof(includedir_keyword), + ptr, name, line))) + goto err; if (!(search_dir= my_dir(ptr, MYF(MY_WME)))) goto err; @@ -559,28 +603,13 @@ static int search_default_file_with_ext(Process_option_func opt_handler, my_dirend(search_dir); } - else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) - && my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword) - 1])) + else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1])) { - /* skip over `include' and following whitespace */ - for (ptr+= sizeof(include_keyword) - 1; - my_isspace(&my_charset_latin1, ptr[0]); ptr++) - {} - - /* trim trailing whitespace from filename */ - end= ptr + strlen(ptr) - 1; - for (; my_isspace(&my_charset_latin1, *(end - 1)) ; end--) - {} - end[0]= 0; - - if (end == ptr) - { - fprintf(stderr, - "error: Wrong !include directive in config " - "file: %s at line %d\n", - name,line); - goto err; - } + if (!(ptr= get_argument(include_keyword, + sizeof(include_keyword), ptr, + name, line))) + goto err; search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr, recursion_level + 1); @@ -588,14 +617,6 @@ static int search_default_file_with_ext(Process_option_func opt_handler, continue; } - else - if (recursion_level >= max_recursion_level) - { - fprintf(stderr, - "warning: skipping !include directive as maximum include" - "recursion level was reached in file %s at line %d\n", - name, line); - } if (*ptr == '[') /* Group name */ { diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index 4967b60cd68..2308536cd37 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -1025,8 +1025,8 @@ static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count) for a too long time (this time is determined by parameter age_threshold). */ -static inline void unreg_request(KEY_CACHE *keycache, - BLOCK_LINK *block, int at_end) +static void unreg_request(KEY_CACHE *keycache, + BLOCK_LINK *block, int at_end) { if (! --block->requests) { @@ -1045,10 +1045,13 @@ static inline void unreg_request(KEY_CACHE *keycache, } link_block(keycache, block, hot, (my_bool)at_end); block->last_hit_time= keycache->keycache_time; - if (++keycache->keycache_time - keycache->used_ins->last_hit_time > + keycache->keycache_time++; + + block= keycache->used_ins; + /* Check if we should link a hot block to the warm block */ + if (block && keycache->keycache_time - block->last_hit_time > keycache->age_threshold) { - block= keycache->used_ins; unlink_block(keycache, block); link_block(keycache, block, 0, 0); if (block->temperature != BLOCK_WARM) diff --git a/sql/field.h b/sql/field.h index 0f1cc0b95aa..ac9c2f351b3 100644 --- a/sql/field.h +++ b/sql/field.h @@ -118,6 +118,7 @@ public: String *val_int_as_str(String *val_buffer, my_bool unsigned_flag); virtual Item_result result_type () const=0; virtual Item_result cmp_type () const { return result_type(); } + virtual Item_result cast_to_int_type () const { return result_type(); } static enum_field_types field_type_merge(enum_field_types, enum_field_types); static Item_result result_merge_type(enum_field_types); bool eq(Field *field) @@ -1216,6 +1217,7 @@ public: } enum_field_types type() const { return FIELD_TYPE_STRING; } enum Item_result cmp_type () const { return INT_RESULT; } + enum Item_result cast_to_int_type () const { return INT_RESULT; } enum ha_base_keytype key_type() const; int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); diff --git a/sql/hostname.cc b/sql/hostname.cc index c74d230bbcb..fe2fad6f3b2 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -177,7 +177,14 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors) &tmp_errno))) { DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno)); - add_wrong_ip(in); + /* + Don't cache responses when the DSN server is down, as otherwise + transient DNS failure may leave any number of clients (those + that attempted to connect during the outage) unable to connect + indefinitely. + */ + if (tmp_errno == HOST_NOT_FOUND || tmp_error == NO_DATA) + add_wrong_ip(in); my_gethostbyname_r_free(); DBUG_RETURN(0); } diff --git a/sql/item.h b/sql/item.h index e86c66ca6f3..850a2d6636d 100644 --- a/sql/item.h +++ b/sql/item.h @@ -302,7 +302,8 @@ public: { return save_in_field(field, 1); } virtual bool send(Protocol *protocol, String *str); virtual bool eq(const Item *, bool binary_cmp) const; - virtual Item_result result_type () const { return REAL_RESULT; } + virtual Item_result result_type() const { return REAL_RESULT; } + virtual Item_result cast_to_int_type() const { return result_type(); } virtual enum_field_types field_type() const; virtual enum Type type() const =0; /* valXXX methods must return NULL or 0 or 0.0 if null_value is set. */ @@ -738,6 +739,10 @@ public: { return field->result_type(); } + Item_result cast_to_int_type() const + { + return field->cast_to_int_type(); + } enum_field_types field_type() const { return field->type(); diff --git a/sql/item_func.cc b/sql/item_func.cc index c4b0602a186..c2afc2fd685 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -904,6 +904,58 @@ void Item_func_signed::print(String *str) } +longlong Item_func_signed::val_int_from_str(int *error) +{ + char buff[MAX_FIELD_WIDTH], *end; + String tmp(buff,sizeof(buff), &my_charset_bin), *res; + longlong value; + + /* + For a string result, we must first get the string and then convert it + to a longlong + */ + + if (!(res= args[0]->val_str(&tmp))) + { + null_value= 1; + *error= 0; + return 0; + } + null_value= 0; + end= (char*) res->ptr()+ res->length(); + value= my_strtoll10(res->ptr(), &end, error); + if (*error > 0 || end != res->ptr()+ res->length()) + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", + res->c_ptr()); + return value; +} + + +longlong Item_func_signed::val_int() +{ + longlong value; + int error; + + if (args[0]->cast_to_int_type() != STRING_RESULT) + { + value= args[0]->val_int(); + null_value= args[0]->null_value; + return value; + } + + value= val_int_from_str(&error); + if (value < 0 && error == 0) + { + push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + "Cast to signed converted positive out-of-range integer to " + "it's negative complement"); + } + return value; +} + + void Item_func_unsigned::print(String *str) { str->append("cast(", 5); @@ -913,6 +965,27 @@ void Item_func_unsigned::print(String *str) } +longlong Item_func_unsigned::val_int() +{ + longlong value; + int error; + + if (args[0]->cast_to_int_type() != STRING_RESULT) + { + value= args[0]->val_int(); + null_value= args[0]->null_value; + return value; + } + + value= val_int_from_str(&error); + if (error < 0) + push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + "Cast to unsigned converted negative integer to it's " + "positive complement"); + return value; +} + + String *Item_decimal_typecast::val_str(String *str) { my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); diff --git a/sql/item_func.h b/sql/item_func.h index a27f4382577..76d1151f3bf 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -262,12 +262,8 @@ public: null_value= args[0]->null_value; return tmp; } - longlong val_int() - { - longlong tmp= args[0]->val_int(); - null_value= args[0]->null_value; - return tmp; - } + longlong val_int(); + longlong val_int_from_str(int *error); void fix_length_and_dec() { max_length=args[0]->max_length; unsigned_flag=0; } void print(String *str); @@ -281,6 +277,7 @@ public: const char *func_name() const { return "cast_as_unsigned"; } void fix_length_and_dec() { max_length=args[0]->max_length; unsigned_flag=1; } + longlong val_int(); void print(String *str); }; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index ac8f5a06745..729f9751bba 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -89,7 +89,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, } if (values.elements != table->s->fields) { - my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1); + my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); return -1; } #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -112,7 +112,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, int res; if (fields.elements != values.elements) { - my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1); + my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); return -1; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 49e77e1c2dd..fa774c6d89e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2492,8 +2492,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, KEY_OPTIMIZE_EXISTS) | ((old->optimize | new_fields->optimize) & KEY_OPTIMIZE_REF_OR_NULL)); - old->null_rejecting= old->null_rejecting && - new_fields->null_rejecting; + old->null_rejecting= (old->null_rejecting && + new_fields->null_rejecting); } } else if (old->eq_func && new_fields->eq_func && @@ -2505,8 +2505,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, KEY_OPTIMIZE_EXISTS) | ((old->optimize | new_fields->optimize) & KEY_OPTIMIZE_REF_OR_NULL)); - old->null_rejecting= old->null_rejecting && - new_fields->null_rejecting; + old->null_rejecting= (old->null_rejecting && + new_fields->null_rejecting); } else if (old->eq_func && new_fields->eq_func && (old->val->is_null() || new_fields->val->is_null())) @@ -2518,7 +2518,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, if (old->val->is_null()) old->val= new_fields->val; /* The referred expression can be NULL: */ - old->null_rejecting= false; + old->null_rejecting= 0; } else { @@ -2680,6 +2680,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, If the condition has form "tbl.keypart = othertbl.field" and othertbl.field can be NULL, there will be no matches if othertbl.field has NULL value. + We use null_rejecting in add_not_null_conds() to add + 'othertbl.field IS NOT NULL' to tab->select_cond. */ (*key_fields)->null_rejecting= (cond->functype() == Item_func::EQ_FUNC) && ((*value)->type() == Item::FIELD_ITEM) && diff --git a/tests/index_corrupt.pl b/tests/index_corrupt.pl new file mode 100755 index 00000000000..19bf54f5d11 --- /dev/null +++ b/tests/index_corrupt.pl @@ -0,0 +1,212 @@ +#!/usr/bin/perl -w +# +# This is a test for a key cache bug (bug #10167) +# To expose the bug mysqld should be started with --key-buffer-size=64K +# + +$opt_loop_count=100000; # Change this to make test harder/easier + +##################### Standard benchmark inits ############################## + +use DBI; +use Getopt::Long; +use Benchmark; + +package main; + +$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert= + $opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0; +$opt_host=$opt_user=$opt_password=""; $opt_db="test"; + +GetOptions("host=s","db=s","loop-count=i","skip-create","skip-in", + "skip-delete","verbose","fast-insert","lock-tables","debug","fast", + "force","user=s","password=s") || die "Aborted"; +$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these + +$firsttable = "bench_f1"; +$secondtable = "bench_f2"; +$kill_file= "/tmp/mysqltest_index_corrupt.$$"; + +#### +#### Start timeing and start test +#### + +$start_time=new Benchmark; +if (!$opt_skip_create) +{ + $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host", + $opt_user, $opt_password, + { PrintError => 0}) || die $DBI::errstr; + $dbh->do("drop table if exists $firsttable, $secondtable"); + + print "Creating tables in $opt_db\n"; + $dbh->do("create table $firsttable ( +c_pollid INTEGER NOT NULL, +c_time BIGINT NOT NULL, +c_data DOUBLE NOT NULL, +c_error INTEGER NOT NULL, +c_warning INTEGER NOT NULL, +c_okay INTEGER NOT NULL, +c_unknown INTEGER NOT NULL, +c_rolled_up BIT NOT NULL, +INDEX t_mgmt_hist_r_i1 (c_pollid), +INDEX t_mgmt_hist_r_i2 (c_time), +INDEX t_mgmt_hist_r_i3 (c_rolled_up))") or die $DBI::errstr; + + $dbh->do("create table $secondtable ( +c_pollid INTEGER NOT NULL, +c_min_time BIGINT NOT NULL, +c_max_time BIGINT NOT NULL, +c_min_data DOUBLE NOT NULL, +c_max_data DOUBLE NOT NULL, +c_avg_data DOUBLE NOT NULL, +c_error INTEGER NOT NULL, +c_warning INTEGER NOT NULL, +c_okay INTEGER NOT NULL, +c_unknown INTEGER NOT NULL, +c_rolled_up BIT NOT NULL, +INDEX t_mgmt_hist_d_i1 (c_pollid), +INDEX t_mgmt_hist_d_i2 (c_min_time), +INDEX t_mgmt_hist_d_i3 (c_max_time), +INDEX t_mgmt_hist_d_i4 (c_rolled_up))") or die $DBI::errstr; + + + $dbh->disconnect; $dbh=0; # Close handler +} +$|= 1; # Autoflush + +#### +#### Start the tests +#### + +print "Running tests\n"; +insert_in_bench() if (($pid=fork()) == 0); $work{$pid}="insert"; +select_from_bench() if (($pid=fork()) == 0); $work{$pid}="insert-select; +delete_from_bench() if (($pid=fork()) == 0); $work{$pid}="delete"; + +$errors=0; +while (($pid=wait()) != -1) +{ + $ret=$?/256; + print "thread '" . $work{$pid} . "' finished with exit code $ret\n"; + $errors++ if ($ret != 0); +} + +if (!$opt_skip_delete && !$errors) +{ + $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host", + $opt_user, $opt_password, + { PrintError => 0}) || die $DBI::errstr; + $dbh->do("drop table $firsttable, $secondtable"); +} +print ($errors ? "Test failed\n" :"Test ok\n"); + +$end_time=new Benchmark; +print "Total time: " . + timestr(timediff($end_time, $start_time),"noc") . "\n"; + +unlink $kill_file; + +exit(0); + +# +# Insert records in the two tables +# + +sub insert_in_bench +{ + my ($dbh,$rows,$found,$i); + + $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host", + $opt_user, $opt_password, + { PrintError => 0}) || die $DBI::errstr; + for ($rows= 1; $rows <= $opt_loop_count ; $rows++) + { + $c_pollid = sprintf("%d",rand 1000); + $c_time = sprintf("%d",rand 100000); + $c_data = rand 1000000; + $test = rand 1; + $c_error=0; + $c_warning=0; + $c_okay=0; + $c_unknown=0; + if ($test < .8) { + $c_okay=1; + } elsif ($test <.9) { + $c_error=1; + } elsif ($test <.95) { + $c_warning=1; + } else { + $c_unknown=1; + } + $statement = "INSERT INTO $firsttable (c_pollid, c_time, c_data, c_error +, c_warning, c_okay, c_unknown, c_rolled_up) ". + "VALUES ($c_pollid,$c_time,$c_data,$c_error,$c_warning,$c_okay,$c_unknown,0)"; + $cursor = $dbh->prepare($statement); + $cursor->execute(); + $cursor->finish(); + } + + $dbh->disconnect; $dbh=0; + print "insert_in_bench: Inserted $rows rows\n"; + + # Kill other threads + open(KILLFILE, "> $kill_file"); + close(KILLFILE); + + exit(0); +} + + +sub select_from_bench +{ + my ($dbh,$rows,$cursor); + + $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host", + $opt_user, $opt_password, + { PrintError => 0}) || die $DBI::errstr; + for ($rows= 1; $rows < $opt_loop_count ; $rows++) + { + $t_value = rand 100000; + $t_value2 = $t_value+10000; + $statement = "INSERT INTO $secondtable (c_pollid, c_min_time, c_max_time +, c_min_data, c_max_data, c_avg_data, c_error, c_warning, c_okay, c_unknown, c_rolled_up) SELECT c_pollid, MIN(c_time), MAX(c_time), MIN(c_data), MAX(c_data), AVG(c_data), SUM(c_error), SUM(c_warning), SUM(c_okay), SUM(c_unknown), 0 FROM $firsttable WHERE (c_time>=$t_value) AND (c_time<$t_value2) AND (c_rolled_up=0) GROUP BY c_pollid"; + $cursor = $dbh->prepare($statement); + $cursor->execute(); + $cursor->finish(); + sleep 1; + if (-e $kill_file) + { + last; + } + } + print "select_from_bench: insert-select executed $rows times\n"; + exit(0); +} + + +sub delete_from_bench +{ + my ($dbh,$row, $t_value, $t2_value, $statement, $cursor); + + $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host", + $opt_user, $opt_password, + { PrintError => 0}) || die $DBI::errstr; + + for ($rows= 1; $rows < $opt_loop_count ; $rows++) + { + $t_value = rand 50000; + $t2_value = $t_value + 50001; + $statement = "DELETE FROM $firsttable WHERE (c_time>$t_value) AND (c_time<$t2_value)"; + $cursor = $dbh->prepare($statement); + $cursor->execute(); + $cursor->finish(); + sleep 10; + if (-e $kill_file) + { + last; + } + } + print "delete: delete executed $rows times\n"; + exit(0); +} |