Commit f301f8f2 authored by unknown's avatar unknown

CAST(string_argument AS UNSIGNED) didn't work for big integers above the signed range. (Bug #7036)

Produce warnings of wrong cast of strings to signed/unsigned.
Don't block not resolved IP's if DNS server is down (Bug #8467)
Fix compiler problems with MinGW (Bug #8872)


configure.in:
  Fix compiler problems with MinGW (Bug #8872)
include/config-win.h:
  Fix compiler problems with MinGW (Bug #8872)
include/my_global.h:
  Fix compiler problems with MinGW (Bug #8872)
mysql-test/r/cast.result:
  Test for cast to signed/unsigned outside of range (Bug #7036)
mysql-test/t/cast.test:
  Test for cast to signed/unsigned outside of range (Bug #7036)
mysys/default.c:
  Cleanup (combine identical code). 
  Done mainly by Jani
sql/field.h:
  Added cast_to_int_type() to ensure that enums are casted as numbers
sql/hostname.cc:
  Don't block not resolved IP's if DNS server is down (Bug #8467)
sql/item.h:
  Added cast_to_int_type() to ensure that enums are casted as numbers
sql/item_func.cc:
  CAST(string_argument AS UNSIGNED) didn't work for big integers above the
  signed range. (Bug #7036)
  Produce warnings of wrong cast of strings to signed/unsigned
sql/item_func.h:
  CAST(string_argument AS UNSIGNED) didn't work for big integers above the
  signed range. (Bug #7036)
parent 3e090cba
...@@ -1860,6 +1860,9 @@ If you are using gcc 2.8.# you should upgrade to egcs 1.0.3 or newer and try ...@@ -1860,6 +1860,9 @@ If you are using gcc 2.8.# you should upgrade to egcs 1.0.3 or newer and try
again]); again]);
fi fi
fi fi
AC_CHECK_TYPES([sigset_t, off_t], [], [], [#include <sys/types.h>])
AC_CHECK_TYPES([size_t], [], [], [#include <stdio.h>])
MYSQL_PTHREAD_YIELD MYSQL_PTHREAD_YIELD
###################################################################### ######################################################################
......
...@@ -106,20 +106,33 @@ functions */ ...@@ -106,20 +106,33 @@ functions */
/* Type information */ /* Type information */
#if defined(__EMX__) || !defined(HAVE_UINT)
#undef HAVE_UINT
#define HAVE_UINT
typedef unsigned short ushort; typedef unsigned short ushort;
typedef unsigned int uint; typedef unsigned int uint;
#endif /* defined(__EMX__) || !defined(HAVE_UINT) */
typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */ typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */
typedef __int64 longlong; typedef __int64 longlong;
#ifndef HAVE_SIGSET_T
typedef int sigset_t; typedef int sigset_t;
#endif
#define longlong_defined #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; typedef long off_t;
#endif
typedef __int64 os_off_t; typedef __int64 os_off_t;
#ifdef _WIN64 #ifdef _WIN64
typedef UINT_PTR rf_SetTimer; typedef UINT_PTR rf_SetTimer;
#else #else
#ifndef HAVE_SIZE_T
typedef unsigned int size_t; typedef unsigned int size_t;
#endif
typedef uint rf_SetTimer; typedef uint rf_SetTimer;
#endif #endif
......
...@@ -393,6 +393,8 @@ int __void__; ...@@ -393,6 +393,8 @@ int __void__;
#endif #endif
#if defined(__EMX__) || !defined(HAVE_UINT) #if defined(__EMX__) || !defined(HAVE_UINT)
#undef HAVE_UINT
#define HAVE_UINT
typedef unsigned int uint; typedef unsigned int uint;
typedef unsigned short ushort; typedef unsigned short ushort;
#endif #endif
......
...@@ -4,9 +4,6 @@ CAST(1-2 AS UNSIGNED) ...@@ -4,9 +4,6 @@ CAST(1-2 AS UNSIGNED)
select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER); select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER);
CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER) CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER)
-1 -1
select CONVERT('-1',UNSIGNED);
CONVERT('-1',UNSIGNED)
18446744073709551615
select cast(-5 as unsigned) | 1, cast(-5 as unsigned) & -1; select cast(-5 as unsigned) | 1, cast(-5 as unsigned) & -1;
cast(-5 as unsigned) | 1 cast(-5 as unsigned) & -1 cast(-5 as unsigned) | 1 cast(-5 as unsigned) & -1
18446744073709551611 18446744073709551611 18446744073709551611 18446744073709551611
...@@ -57,6 +54,41 @@ CONVERT(DATE "2004-01-22 21:45:33",BINARY(4)) ...@@ -57,6 +54,41 @@ CONVERT(DATE "2004-01-22 21:45:33",BINARY(4))
select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4)); select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4));
CAST(DATE "2004-01-22 21:45:33" AS BINARY(4)) CAST(DATE "2004-01-22 21:45:33" AS BINARY(4))
2004 2004
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; set names binary;
select cast(_latin1'test' as char character set latin2); select cast(_latin1'test' as char character set latin2);
cast(_latin1'test' as char character set latin2) cast(_latin1'test' as char character set latin2)
...@@ -187,3 +219,36 @@ timediff(cast('2004-12-30 12:00:00' as time), '12:00:00') ...@@ -187,3 +219,36 @@ 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'); select timediff(cast('1 12:00:00' as time), '12:00:00');
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 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
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
select CAST(1-2 AS UNSIGNED); select CAST(1-2 AS UNSIGNED);
select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER); select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER);
select CONVERT('-1',UNSIGNED);
select cast(-5 as unsigned) | 1, cast(-5 as unsigned) & -1; select cast(-5 as unsigned) | 1, cast(-5 as unsigned) & -1;
select cast(-5 as unsigned) -1, cast(-5 as unsigned) + 1; select cast(-5 as unsigned) -1, cast(-5 as unsigned) + 1;
select ~5, cast(~5 as signed); select ~5, cast(~5 as signed);
...@@ -22,6 +21,15 @@ select CONVERT(DATE "2004-01-22 21:45:33",CHAR(4)); ...@@ -22,6 +21,15 @@ select CONVERT(DATE "2004-01-22 21:45:33",CHAR(4));
select CONVERT(DATE "2004-01-22 21:45:33",BINARY(4)); select CONVERT(DATE "2004-01-22 21:45:33",BINARY(4));
select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4)); select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4));
# 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 # Character set convertion
# #
...@@ -118,3 +126,19 @@ select date_add(cast('2004-12-30 12:00:00' as date), interval 0 hour); ...@@ -118,3 +126,19 @@ select date_add(cast('2004-12-30 12:00:00' as date), interval 0 hour);
select timediff(cast('2004-12-30 12:00:00' as time), '12:00:00'); 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 # Still we should not throw away "days" part of time value
select timediff(cast('1 12:00:00' as time), '12:00:00'); 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);
...@@ -318,6 +318,56 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, ...@@ -318,6 +318,56 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
} }
/*
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 Open a configuration file (if exists) and read given options from it
...@@ -426,31 +476,10 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, ...@@ -426,31 +476,10 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
sizeof(includedir_keyword) - 1)) && sizeof(includedir_keyword) - 1)) &&
my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
{ {
/* skip over "includedir" and following whitespace */ if (!(ptr= get_argument(includedir_keyword,
for (ptr+= sizeof(includedir_keyword) - 1; sizeof(includedir_keyword),
my_isspace(&my_charset_latin1, ptr[0]); ptr++) ptr, name, line)))
{} goto err;
/*
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;
/* 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 (!(search_dir= my_dir(ptr, MYF(MY_WME)))) if (!(search_dir= my_dir(ptr, MYF(MY_WME))))
goto err; goto err;
...@@ -486,26 +515,10 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, ...@@ -486,26 +515,10 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) && else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1])) my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
{ {
/* skip over `include' and following whitespace */ if (!(ptr= get_argument(include_keyword,
for (ptr+= sizeof(include_keyword) - 1; sizeof(include_keyword), ptr,
my_isspace(&my_charset_latin1, ptr[0]); ptr++) name, line)))
{} goto err;
/* trim trailing whitespace from filename */
for (end= ptr + strlen(ptr) - 1;
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;
}
search_default_file_with_ext(args, alloc, "", "", ptr, group, search_default_file_with_ext(args, alloc, "", "", ptr, group,
recursion_level + 1); recursion_level + 1);
......
...@@ -119,6 +119,7 @@ class Field ...@@ -119,6 +119,7 @@ class Field
virtual String *val_str(String*,String *)=0; virtual String *val_str(String*,String *)=0;
virtual Item_result result_type () const=0; virtual Item_result result_type () const=0;
virtual Item_result cmp_type () const { return result_type(); } 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 enum_field_types field_type_merge(enum_field_types, enum_field_types);
static Item_result result_merge_type(enum_field_types); static Item_result result_merge_type(enum_field_types);
bool eq(Field *field) { return ptr == field->ptr && null_ptr == field->null_ptr; } bool eq(Field *field) { return ptr == field->ptr && null_ptr == field->null_ptr; }
...@@ -1115,6 +1116,7 @@ class Field_enum :public Field_str { ...@@ -1115,6 +1116,7 @@ class Field_enum :public Field_str {
} }
enum_field_types type() const { return FIELD_TYPE_STRING; } enum_field_types type() const { return FIELD_TYPE_STRING; }
enum Item_result cmp_type () const { return INT_RESULT; } 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; enum ha_base_keytype key_type() const;
int store(const char *to,uint length,CHARSET_INFO *charset); int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr); int store(double nr);
......
...@@ -177,7 +177,14 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors) ...@@ -177,7 +177,14 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors)
&tmp_errno))) &tmp_errno)))
{ {
DBUG_PRINT("error",("gethostbyname_r returned %d",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(); my_gethostbyname_r_free();
DBUG_RETURN(0); DBUG_RETURN(0);
} }
......
...@@ -180,7 +180,8 @@ class Item { ...@@ -180,7 +180,8 @@ class Item {
{ return save_in_field(field, 1); } { return save_in_field(field, 1); }
virtual bool send(Protocol *protocol, String *str); virtual bool send(Protocol *protocol, String *str);
virtual bool eq(const Item *, bool binary_cmp) const; 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_field_types field_type() const;
virtual enum Type type() const =0; virtual enum Type type() const =0;
/* valXXX methods must return NULL or 0 or 0.0 if null_value is set. */ /* valXXX methods must return NULL or 0 or 0.0 if null_value is set. */
...@@ -422,6 +423,10 @@ class Item_field :public Item_ident ...@@ -422,6 +423,10 @@ class Item_field :public Item_ident
{ {
return field->result_type(); return field->result_type();
} }
Item_result cast_to_int_type() const
{
return field->cast_to_int_type();
}
enum_field_types field_type() const enum_field_types field_type() const
{ {
return field->type(); return field->type();
......
...@@ -582,6 +582,58 @@ void Item_func_signed::print(String *str) ...@@ -582,6 +582,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) void Item_func_unsigned::print(String *str)
{ {
str->append("cast(", 5); str->append("cast(", 5);
...@@ -591,6 +643,27 @@ void Item_func_unsigned::print(String *str) ...@@ -591,6 +643,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;
}
double Item_func_plus::val() double Item_func_plus::val()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
......
...@@ -226,12 +226,8 @@ class Item_func_signed :public Item_int_func ...@@ -226,12 +226,8 @@ class Item_func_signed :public Item_int_func
null_value= args[0]->null_value; null_value= args[0]->null_value;
return tmp; return tmp;
} }
longlong val_int() longlong val_int();
{ longlong val_int_from_str(int *error);
longlong tmp= args[0]->val_int();
null_value= args[0]->null_value;
return tmp;
}
void fix_length_and_dec() void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=0; } { max_length=args[0]->max_length; unsigned_flag=0; }
void print(String *str); void print(String *str);
...@@ -245,6 +241,7 @@ class Item_func_unsigned :public Item_func_signed ...@@ -245,6 +241,7 @@ class Item_func_unsigned :public Item_func_signed
const char *func_name() const { return "cast_as_unsigned"; } const char *func_name() const { return "cast_as_unsigned"; }
void fix_length_and_dec() void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=1; } { max_length=args[0]->max_length; unsigned_flag=1; }
longlong val_int();
void print(String *str); void print(String *str);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment