summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BitKeeper/etc/logging_ok2
-rw-r--r--configure.in3
-rw-r--r--include/config-win.h17
-rw-r--r--include/my_global.h2
-rw-r--r--mysql-test/r/cast.result71
-rw-r--r--mysql-test/r/innodb-replace.result1
-rw-r--r--mysql-test/t/cast.test26
-rw-r--r--mysql-test/t/innodb-replace.test4
-rw-r--r--mysys/default.c135
-rw-r--r--mysys/mf_keycache.c11
-rw-r--r--sql/field.h2
-rw-r--r--sql/hostname.cc9
-rw-r--r--sql/item.h7
-rw-r--r--sql/item_func.cc73
-rw-r--r--sql/item_func.h9
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_select.cc12
-rwxr-xr-xtests/index_corrupt.pl212
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);
+}