Commit 7c369a24 authored by konstantin@mysql.com's avatar konstantin@mysql.com

More work on truncations in libmysql: after-review fixes.

parent a2c9c8ea
...@@ -3428,7 +3428,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, ...@@ -3428,7 +3428,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
{ {
char *buffer= (char *)param->buffer; char *buffer= (char *)param->buffer;
int err= 0; int err= 0;
char *endptr; char *endptr= value + length;
/* /*
This function should support all target buffer types: the rest This function should support all target buffer types: the rest
...@@ -3439,39 +3439,33 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, ...@@ -3439,39 +3439,33 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
break; break;
case MYSQL_TYPE_TINY: case MYSQL_TYPE_TINY:
{ {
longlong data= my_strntoll(&my_charset_latin1, value, length, 10, longlong data= my_strtoll10(value, &endptr, &err);
&endptr, &err);
*param->error= (IS_TRUNCATED(data, param->is_unsigned, *param->error= (IS_TRUNCATED(data, param->is_unsigned,
INT_MIN8, INT_MAX8, UINT_MAX8) | INT_MIN8, INT_MAX8, UINT_MAX8) || err > 0);
test(err));
*buffer= (uchar) data; *buffer= (uchar) data;
break; break;
} }
case MYSQL_TYPE_SHORT: case MYSQL_TYPE_SHORT:
{ {
longlong data= my_strntoll(&my_charset_latin1, value, length, 10, longlong data= my_strtoll10(value, &endptr, &err);
&endptr, &err);
*param->error= (IS_TRUNCATED(data, param->is_unsigned, *param->error= (IS_TRUNCATED(data, param->is_unsigned,
INT_MIN16, INT_MAX16, UINT_MAX16) | INT_MIN16, INT_MAX16, UINT_MAX16) || err > 0);
test(err));
shortstore(buffer, (short) data); shortstore(buffer, (short) data);
break; break;
} }
case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONG:
{ {
longlong data= my_strntoll(&my_charset_latin1, value, length, 10, longlong data= my_strtoll10(value, &endptr, &err);
&endptr, &err);
*param->error= (IS_TRUNCATED(data, param->is_unsigned, *param->error= (IS_TRUNCATED(data, param->is_unsigned,
INT_MIN32, INT_MAX32, UINT_MAX32) | INT_MIN32, INT_MAX32, UINT_MAX32) || err > 0);
test(err));
longstore(buffer, (int32) data); longstore(buffer, (int32) data);
break; break;
} }
case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_LONGLONG:
{ {
longlong data= my_strntoll(&my_charset_latin1, value, length, 10, longlong data= my_strtoll10(value, &endptr, &err);
&endptr, &err); *param->error= param->is_unsigned ? err != 0 :
*param->error= test(err); (err > 0 || (err == 0 && data < 0));
longlongstore(buffer, data); longlongstore(buffer, data);
break; break;
} }
...@@ -3554,10 +3548,9 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, ...@@ -3554,10 +3548,9 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
*/ */
static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
longlong value) longlong value, my_bool is_unsigned)
{ {
char *buffer= (char *)param->buffer; char *buffer= (char *)param->buffer;
uint field_is_unsigned= field->flags & UNSIGNED_FLAG;
switch (param->buffer_type) { switch (param->buffer_type) {
case MYSQL_TYPE_NULL: /* do nothing */ case MYSQL_TYPE_NULL: /* do nothing */
...@@ -3579,38 +3572,38 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, ...@@ -3579,38 +3572,38 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
break; break;
case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_LONGLONG:
longlongstore(buffer, value); longlongstore(buffer, value);
*param->error= param->is_unsigned != is_unsigned && value < 0;
break; break;
case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_FLOAT:
{ {
/*
We need to store data in the buffer before the truncation check to
workaround Intel FPU executive precision feature.
(See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 for details)
AFAIU it does not guarantee to work.
*/
float data; float data;
if (field_is_unsigned) if (is_unsigned)
{
data= (float) ulonglong2double(value); data= (float) ulonglong2double(value);
*param->error= (ulonglong) data != (ulonglong) value;
}
else else
{
data= (float) value; data= (float) value;
/* printf("%lld, %f\n", value, data); */
*param->error= value != ((longlong) data);
}
floatstore(buffer, data); floatstore(buffer, data);
*param->error= is_unsigned ?
((ulonglong) value) != ((ulonglong) (*(float*) buffer)) :
((longlong) value) != ((longlong) (*(float*) buffer));
break; break;
} }
case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_DOUBLE:
{ {
double data; double data;
if (field_is_unsigned) if (is_unsigned)
{
data= ulonglong2double(value); data= ulonglong2double(value);
*param->error= (ulonglong) data != (ulonglong) value;
}
else else
{
data= value; data= value;
*param->error= (longlong) data != value;
}
doublestore(buffer, data); doublestore(buffer, data);
*param->error= is_unsigned ?
((ulonglong) value) != ((ulonglong) (*(double*) buffer)) :
((longlong) value) != ((longlong) (*(double*) buffer));
break; break;
} }
case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME:
...@@ -3626,7 +3619,7 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, ...@@ -3626,7 +3619,7 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
default: default:
{ {
char buff[22]; /* Enough for longlong */ char buff[22]; /* Enough for longlong */
char *end= longlong10_to_str(value, buff, field_is_unsigned ? 10: -10); char *end= longlong10_to_str(value, buff, is_unsigned ? 10: -10);
/* Resort to string conversion which supports all typecodes */ /* Resort to string conversion which supports all typecodes */
uint length= (uint) (end-buff); uint length= (uint) (end-buff);
...@@ -3665,74 +3658,67 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, ...@@ -3665,74 +3658,67 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
case MYSQL_TYPE_NULL: /* do nothing */ case MYSQL_TYPE_NULL: /* do nothing */
break; break;
case MYSQL_TYPE_TINY: case MYSQL_TYPE_TINY:
{ /*
We need to _store_ data in the buffer before the truncation check to
workaround Intel FPU executive precision feature.
(See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 for details)
Sic: AFAIU it does not guarantee to work.
*/
if (param->is_unsigned) if (param->is_unsigned)
{ *buffer= (uint8) value;
int8 data= (int8) value;
*param->error= (double) data != value;
*buffer= (uchar) data;
}
else else
{ *buffer= (int8) value;
uchar data= (uchar) value; *param->error= value != (param->is_unsigned ? (double) ((uint8) *buffer) :
*param->error= (double) data != value; (double) ((int8) *buffer));
*buffer= data;
}
break; break;
}
case MYSQL_TYPE_SHORT: case MYSQL_TYPE_SHORT:
{
if (param->is_unsigned) if (param->is_unsigned)
{ {
ushort data= (ushort) value; ushort data= (ushort) value;
*param->error= (double) data != value;
shortstore(buffer, data); shortstore(buffer, data);
} }
else else
{ {
short data= (short) value; short data= (short) value;
*param->error= (double) data != value;
shortstore(buffer, data); shortstore(buffer, data);
} }
*param->error= value != (param->is_unsigned ? (double) (*(ushort*) buffer):
(double) (*(short*) buffer));
break; break;
}
case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONG:
{
if (param->is_unsigned) if (param->is_unsigned)
{ {
uint32 data= (uint32) value; uint32 data= (uint32) value;
*param->error= (double) data != value;
longstore(buffer, data); longstore(buffer, data);
} }
else else
{ {
int32 data= (int32) value; int32 data= (int32) value;
*param->error= (double) data != value;
longstore(buffer, data); longstore(buffer, data);
} }
break; *param->error= value != (param->is_unsigned ? (double) (*(uint32*) buffer):
} (double) (*(int32*) buffer));
break;
case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_LONGLONG:
{
if (param->is_unsigned) if (param->is_unsigned)
{ {
ulonglong data= (ulonglong) value; ulonglong data= (ulonglong) value;
*param->error= (double) data != value;
longlongstore(buffer, data); longlongstore(buffer, data);
} }
else else
{ {
longlong data= (longlong) value; longlong data= (longlong) value;
*param->error= (double) data != value;
longlongstore(buffer, data); longlongstore(buffer, data);
} }
*param->error= value != (param->is_unsigned ?
(double) (*(ulonglong*) buffer) :
(double) (*(longlong*) buffer));
break; break;
}
case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_FLOAT:
{ {
float data= (float) value; float data= (float) value;
*param->error= data != value;
floatstore(buffer, data); floatstore(buffer, data);
*param->error= (*(float*) buffer) != value;
break; break;
} }
case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_DOUBLE:
...@@ -3824,7 +3810,7 @@ static void fetch_datetime_with_conversion(MYSQL_BIND *param, ...@@ -3824,7 +3810,7 @@ static void fetch_datetime_with_conversion(MYSQL_BIND *param,
case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_LONGLONG:
{ {
longlong value= (longlong) TIME_to_ulonglong(time); longlong value= (longlong) TIME_to_ulonglong(time);
fetch_long_with_conversion(param, field, value); fetch_long_with_conversion(param, field, value, TRUE);
break; break;
} }
default: default:
...@@ -3872,7 +3858,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, ...@@ -3872,7 +3858,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
/* sic: we need to cast to 'signed char' as 'char' may be unsigned */ /* sic: we need to cast to 'signed char' as 'char' may be unsigned */
longlong data= field_is_unsigned ? (longlong) value : longlong data= field_is_unsigned ? (longlong) value :
(longlong) (signed char) value; (longlong) (signed char) value;
fetch_long_with_conversion(param, field, data); fetch_long_with_conversion(param, field, data, 0);
*row+= 1; *row+= 1;
break; break;
} }
...@@ -3882,7 +3868,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, ...@@ -3882,7 +3868,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
short value= sint2korr(*row); short value= sint2korr(*row);
longlong data= field_is_unsigned ? (longlong) (unsigned short) value : longlong data= field_is_unsigned ? (longlong) (unsigned short) value :
(longlong) value; (longlong) value;
fetch_long_with_conversion(param, field, data); fetch_long_with_conversion(param, field, data, 0);
*row+= 2; *row+= 2;
break; break;
} }
...@@ -3892,14 +3878,15 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, ...@@ -3892,14 +3878,15 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
long value= sint4korr(*row); long value= sint4korr(*row);
longlong data= field_is_unsigned ? (longlong) (unsigned long) value : longlong data= field_is_unsigned ? (longlong) (unsigned long) value :
(longlong) value; (longlong) value;
fetch_long_with_conversion(param, field, data); fetch_long_with_conversion(param, field, data, 0);
*row+= 4; *row+= 4;
break; break;
} }
case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_LONGLONG:
{ {
longlong value= (longlong)sint8korr(*row); longlong value= (longlong)sint8korr(*row);
fetch_long_with_conversion(param, field, value); fetch_long_with_conversion(param, field, value,
field->flags & UNSIGNED_FLAG);
*row+= 8; *row+= 8;
break; break;
} }
......
...@@ -46,7 +46,7 @@ static unsigned long lfactor[9]= ...@@ -46,7 +46,7 @@ static unsigned long lfactor[9]=
from string nptr and converts it to an signed or unsigned from string nptr and converts it to an signed or unsigned
long long integer value. long long integer value.
Space characters and tab are ignored. Space characters and tab are ignored.
A sign character might precede the the digit characters. The number A sign character might precede the digit characters. The number
may have any number of pre-zero digits. may have any number of pre-zero digits.
The function stops reading the string nptr at the first character The function stops reading the string nptr at the first character
......
...@@ -12240,7 +12240,7 @@ static void test_truncation() ...@@ -12240,7 +12240,7 @@ static void test_truncation()
/* int -> float: truncation, not enough bits in float */ /* int -> float: truncation, not enough bits in float */
DIE_UNLESS(++bind < bind_array + bind_count); DIE_UNLESS(++bind < bind_array + bind_count);
/* do nothing: due to a gcc bug result here is not predictable */ DIE_UNLESS(*bind->error);
/* int -> double: no truncation */ /* int -> double: no truncation */
DIE_UNLESS(++bind < bind_array + bind_count); DIE_UNLESS(++bind < bind_array + bind_count);
......
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