summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <monty@mysql.com>2005-04-29 17:03:34 +0300
committerunknown <monty@mysql.com>2005-04-29 17:03:34 +0300
commit97b0821462cd21d5051b3264f072ea3d4a7a8d7f (patch)
tree0caae1ad50324015508cbda7e4ef72f92ca69c25
parent2a69f0049b3fd6e765e2f58a81ea7b5e3d5dcd1c (diff)
downloadmariadb-git-97b0821462cd21d5051b3264f072ea3d4a7a8d7f.tar.gz
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)
-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/t/cast.test26
-rw-r--r--mysys/default.c103
-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
11 files changed, 263 insertions, 59 deletions
diff --git a/configure.in b/configure.in
index e90bea90dad..6efdc56d450 100644
--- a/configure.in
+++ b/configure.in
@@ -1860,6 +1860,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 472190e53ca..b3865c1fda7 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 745179e8a06..bf6f3b52c4b 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -393,6 +393,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 636e2603f9b..7cd0934f7a3 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(-5 as unsigned) | 1, cast(-5 as unsigned) & -1;
cast(-5 as unsigned) | 1 cast(-5 as unsigned) & -1
18446744073709551611 18446744073709551611
@@ -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));
CAST(DATE "2004-01-22 21:45:33" AS BINARY(4))
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;
select 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')
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
diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test
index 23bba7d5aff..aeab81585f0 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(-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);
@@ -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 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
#
@@ -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');
# 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);
diff --git a/mysys/default.c b/mysys/default.c
index 4ee2041bc39..bf23502389d 100644
--- a/mysys/default.c
+++ b/mysys/default.c
@@ -319,6 +319,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
SYNOPSIS
@@ -426,31 +476,10 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
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
- 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 (!(ptr= get_argument(includedir_keyword,
+ sizeof(includedir_keyword),
+ ptr, name, line)))
+ goto err;
if (!(search_dir= my_dir(ptr, MYF(MY_WME))))
goto err;
@@ -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)) &&
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 */
- 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;
- }
+ if (!(ptr= get_argument(include_keyword,
+ sizeof(include_keyword), ptr,
+ name, line)))
+ goto err;
search_default_file_with_ext(args, alloc, "", "", ptr, group,
recursion_level + 1);
diff --git a/sql/field.h b/sql/field.h
index 5dc124aba35..f19771c3f9c 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -119,6 +119,7 @@ public:
virtual String *val_str(String*,String *)=0;
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) { return ptr == field->ptr && null_ptr == field->null_ptr; }
@@ -1115,6 +1116,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 d949095b455..d576fbbc60a 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -180,7 +180,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. */
@@ -422,6 +423,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 2b38584fe23..1b80ef06251 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -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)
{
str->append("cast(", 5);
@@ -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()
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_func.h b/sql/item_func.h
index 3a309f4ae99..288db3a148c 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -226,12 +226,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);
@@ -245,6 +241,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);
};