summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_time.h7
-rw-r--r--include/mysql.h86
-rw-r--r--libmysql/libmysql.c652
-rw-r--r--sql-common/my_time.c164
-rw-r--r--sql/field.cc28
-rw-r--r--sql/field.h7
-rw-r--r--sql/mysql_priv.h6
-rw-r--r--sql/time.cc162
-rw-r--r--tests/client_test.c366
9 files changed, 1050 insertions, 428 deletions
diff --git a/include/my_time.h b/include/my_time.h
index 1635c55fdc9..8058df8fe4e 100644
--- a/include/my_time.h
+++ b/include/my_time.h
@@ -52,6 +52,13 @@ typedef long my_time_t;
enum enum_mysql_timestamp_type
str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
uint flags, int *was_cut);
+longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
+ my_bool fuzzy_date, int *was_cut);
+ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *time);
+ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *time);
+ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *time);
+ulonglong TIME_to_ulonglong(const MYSQL_TIME *time);
+
bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time,
int *was_cut);
diff --git a/include/mysql.h b/include/mysql.h
index 0edd3873192..cb7b4629ec0 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -537,26 +537,91 @@ enum enum_mysql_stmt_state
};
-/* bind structure */
+/*
+ This structure is used to define bind information, and
+ internally by the client library.
+ Public members with their descriptions are listed below
+ (conventionally `On input' refers to the binds given to
+ mysql_stmt_bind_param, `On output' refers to the binds given
+ to mysql_stmt_bind_result):
+
+ buffer_type - One of the MYSQL_* types, used to describe
+ the host language type of buffer.
+ On output: if column type is different from
+ buffer_type, column value is automatically converted
+ to buffer_type before it is stored in the buffer.
+ buffer - On input: points to the buffer with input data.
+ On output: points to the buffer capable to store
+ output data.
+ The type of memory pointed by buffer must correspond
+ to buffer_type. See the correspondence table in
+ the comment to mysql_stmt_bind_param.
+
+ The two above members are mandatory for any kind of bind.
+
+ buffer_length - the length of the buffer. You don't have to set
+ it for any fixed length buffer: float, double,
+ int, etc. It must be set however for variable-length
+ types, such as BLOBs or STRINGs.
+
+ length - On input: in case when lengths of input values
+ are different for each execute, you can set this to
+ point at a variable containining value length. This
+ way the value length can be different in each execute.
+ If length is not NULL, buffer_length is not used.
+ Note, length can even point at buffer_length if
+ you keep bind structures around while fetching:
+ this way you can change buffer_length before
+ each execution, everything will work ok.
+ On output: if length is set, mysql_stmt_fetch will
+ write column length into it.
+
+ is_null - On input: points to a boolean variable that should
+ be set to TRUE for NULL values.
+ This member is useful only if your data may be
+ NULL in some but not all cases.
+ If your data is never NULL, is_null should be set to 0.
+ If your data is always NULL, set buffer_type
+ to MYSQL_TYPE_NULL, and is_null will not be used.
+
+ is_unsigned - On input: used to signify that values provided for one
+ of numeric types are unsigned.
+ On output describes signedness of the output buffer.
+ If, taking into account is_unsigned flag, column data
+ is out of range of the output buffer, data for this column
+ is regarded truncated. Note that this has no correspondence
+ to the sign of result set column, if you need to find it out
+ use mysql_stmt_result_metadata.
+ error - where to write a truncation error if it is present.
+ possible error value is:
+ 0 no truncation
+ 1 value is out of range or buffer is too small
+
+ Please note that MYSQL_BIND also has internals members.
+*/
+
typedef struct st_mysql_bind
{
unsigned long *length; /* output length pointer */
my_bool *is_null; /* Pointer to null indicator */
void *buffer; /* buffer to get/put data */
+ /* set this if you want to track data truncations happened during fetch */
+ my_bool *error;
enum enum_field_types buffer_type; /* buffer type */
- unsigned long buffer_length; /* buffer length, must be set for str/binary */
-
- /* Following are for internal use. Set by mysql_stmt_bind_param */
- unsigned char *inter_buffer; /* for the current data position */
+ /* output buffer length, must be set when fetching str/binary */
+ unsigned long buffer_length;
+ unsigned char *row_ptr; /* for the current data position */
unsigned long offset; /* offset position for char/binary fetch */
- unsigned long internal_length; /* Used if length is 0 */
+ unsigned long length_value; /* Used if length is 0 */
unsigned int param_number; /* For null count and error messages */
unsigned int pack_length; /* Internal length for packed data */
+ my_bool error_value; /* used if error is 0 */
my_bool is_unsigned; /* set if integer type is unsigned */
my_bool long_data_used; /* If used with mysql_send_long_data */
- my_bool internal_is_null; /* Used if is_null is 0 */
+ my_bool is_null_value; /* Used if is_null is 0 */
void (*store_param_func)(NET *net, struct st_mysql_bind *param);
- void (*fetch_result)(struct st_mysql_bind *, unsigned char **row);
+ void (*fetch_result)(struct st_mysql_bind *, MYSQL_FIELD *,
+ unsigned char **row);
void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *,
unsigned char **row);
} MYSQL_BIND;
@@ -598,7 +663,7 @@ typedef struct st_mysql_stmt
/* Types of input parameters should be sent to server */
my_bool send_types_to_server;
my_bool bind_param_done; /* input buffers were supplied */
- my_bool bind_result_done; /* output buffers were supplied */
+ unsigned char bind_result_done; /* output buffers were supplied */
/* mysql_stmt_close() had to cancel this result */
my_bool unbuffered_fetch_cancelled;
/*
@@ -704,7 +769,8 @@ void STDCALL mysql_close(MYSQL *sock);
/* status return codes */
-#define MYSQL_NO_DATA 100
+#define MYSQL_NO_DATA 100
+#define MYSQL_DATA_TRUNCATED 101
#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT)
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index fbaa22cff14..68568f97dea 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -1737,6 +1737,7 @@ static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row);
STMT_ATTR_UPDATE_MAX_LENGTH attribute is set.
*/
static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data);
+static bool setup_one_fetch_function(MYSQL_BIND *bind, MYSQL_FIELD *field);
/*
Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME
@@ -1760,6 +1761,20 @@ static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data);
#define MAX_DOUBLE_STRING_REP_LENGTH 331
+/* A macro to check truncation errors */
+
+#define IS_TRUNCATED(value, is_unsigned, min, max, umax) \
+ ((is_unsigned) ? (((value) > (umax) || (value) < 0) ? 1 : 0) : \
+ (((value) > (max) || (value) < (min)) ? 1 : 0))
+
+#define BIND_RESULT_DONE 1
+/*
+ We report truncations only if at least one of MYSQL_BIND::error
+ pointers is set. In this case stmt->bind_result_done |-ed with
+ this flag.
+*/
+#define REPORT_DATA_TRUNCATION 2
+
/**************** Misc utility functions ****************************/
/*
@@ -2121,6 +2136,7 @@ static void update_stmt_fields(MYSQL_STMT *stmt)
MYSQL_FIELD *field= stmt->mysql->fields;
MYSQL_FIELD *field_end= field + stmt->field_count;
MYSQL_FIELD *stmt_field= stmt->fields;
+ MYSQL_BIND *bind= stmt->bind_result_done ? stmt->bind : 0;
DBUG_ASSERT(stmt->field_count == stmt->mysql->field_count);
@@ -2131,6 +2147,11 @@ static void update_stmt_fields(MYSQL_STMT *stmt)
stmt_field->type = field->type;
stmt_field->flags = field->flags;
stmt_field->decimals = field->decimals;
+ if (bind)
+ {
+ /* Ignore return value: it should be 0 if bind_result succeeded. */
+ (void) setup_one_fetch_function(bind++, stmt_field);
+ }
}
}
@@ -3407,6 +3428,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
{
char *buffer= (char *)param->buffer;
int err= 0;
+ char *endptr;
/*
This function should support all target buffer types: the rest
@@ -3417,42 +3439,54 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
break;
case MYSQL_TYPE_TINY:
{
- uchar data= (uchar) my_strntol(&my_charset_latin1, value, length, 10,
- NULL, &err);
- *buffer= data;
+ longlong data= my_strntoll(&my_charset_latin1, value, length, 10,
+ &endptr, &err);
+ *param->error= (IS_TRUNCATED(data, param->is_unsigned,
+ INT8_MIN, INT8_MAX, UINT8_MAX) |
+ test(err));
+ *buffer= (uchar) data;
break;
}
case MYSQL_TYPE_SHORT:
{
- short data= (short) my_strntol(&my_charset_latin1, value, length, 10,
- NULL, &err);
- shortstore(buffer, data);
+ longlong data= my_strntoll(&my_charset_latin1, value, length, 10,
+ &endptr, &err);
+ *param->error= (IS_TRUNCATED(data, param->is_unsigned,
+ INT16_MIN, INT16_MAX, UINT16_MAX) |
+ test(err));
+ shortstore(buffer, (short) data);
break;
}
case MYSQL_TYPE_LONG:
{
- int32 data= (int32)my_strntol(&my_charset_latin1, value, length, 10,
- NULL, &err);
- longstore(buffer, data);
+ longlong data= my_strntoll(&my_charset_latin1, value, length, 10,
+ &endptr, &err);
+ *param->error= (IS_TRUNCATED(data, param->is_unsigned,
+ INT32_MIN, INT32_MAX, UINT32_MAX) |
+ test(err));
+ longstore(buffer, (int32) data);
break;
}
case MYSQL_TYPE_LONGLONG:
{
longlong data= my_strntoll(&my_charset_latin1, value, length, 10,
- NULL, &err);
+ &endptr, &err);
+ *param->error= test(err);
longlongstore(buffer, data);
break;
}
case MYSQL_TYPE_FLOAT:
{
- float data = (float) my_strntod(&my_charset_latin1, value, length,
- NULL, &err);
- floatstore(buffer, data);
+ double data= my_strntod(&my_charset_latin1, value, length, &endptr, &err);
+ float fdata= (float) data;
+ *param->error= (fdata != data) | test(err);
+ floatstore(buffer, fdata);
break;
}
case MYSQL_TYPE_DOUBLE:
{
- double data= my_strntod(&my_charset_latin1, value, length, NULL, &err);
+ double data= my_strntod(&my_charset_latin1, value, length, &endptr, &err);
+ *param->error= test(err);
doublestore(buffer, data);
break;
}
@@ -3460,6 +3494,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
{
MYSQL_TIME *tm= (MYSQL_TIME *)buffer;
str_to_time(value, length, tm, &err);
+ *param->error= test(err);
break;
}
case MYSQL_TYPE_DATE:
@@ -3467,7 +3502,9 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
case MYSQL_TYPE_TIMESTAMP:
{
MYSQL_TIME *tm= (MYSQL_TIME *)buffer;
- str_to_datetime(value, length, tm, 0, &err);
+ (void) str_to_datetime(value, length, tm, 0, &err);
+ *param->error= test(err) && (param->buffer_type == MYSQL_TYPE_DATE &&
+ tm->time_type != MYSQL_TIMESTAMP_DATE);
break;
}
case MYSQL_TYPE_TINY_BLOB:
@@ -3494,6 +3531,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value,
copy_length= 0;
if (copy_length < param->buffer_length)
buffer[copy_length]= '\0';
+ *param->error= copy_length > param->buffer_length;
/*
param->length will always contain length of entire column;
number of copied bytes may be way different:
@@ -3525,31 +3563,66 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
case MYSQL_TYPE_NULL: /* do nothing */
break;
case MYSQL_TYPE_TINY:
+ *param->error= IS_TRUNCATED(value, param->is_unsigned,
+ INT8_MIN, INT8_MAX, UINT8_MAX);
*(uchar *)param->buffer= (uchar) value;
break;
case MYSQL_TYPE_SHORT:
- shortstore(buffer, value);
+ *param->error= IS_TRUNCATED(value, param->is_unsigned,
+ INT16_MIN, INT16_MAX, UINT16_MAX);
+ shortstore(buffer, (short) value);
break;
case MYSQL_TYPE_LONG:
- longstore(buffer, value);
+ *param->error= IS_TRUNCATED(value, param->is_unsigned,
+ INT32_MIN, INT32_MAX, UINT32_MAX);
+ longstore(buffer, (int32) value);
break;
case MYSQL_TYPE_LONGLONG:
longlongstore(buffer, value);
break;
case MYSQL_TYPE_FLOAT:
{
- float data= field_is_unsigned ? (float) ulonglong2double(value) :
- (float) value;
+ float data;
+ if (field_is_unsigned)
+ {
+ data= (float) ulonglong2double(value);
+ *param->error= (ulonglong) data != (ulonglong) value;
+ }
+ else
+ {
+ data= (float) value;
+ /* printf("%lld, %f\n", value, data); */
+ *param->error= value != ((longlong) data);
+ }
floatstore(buffer, data);
break;
}
case MYSQL_TYPE_DOUBLE:
{
- double data= field_is_unsigned ? ulonglong2double(value) :
- (double) value;
+ double data;
+ if (field_is_unsigned)
+ {
+ data= ulonglong2double(value);
+ *param->error= (ulonglong) data != (ulonglong) value;
+ }
+ else
+ {
+ data= value;
+ *param->error= (longlong) data != value;
+ }
doublestore(buffer, data);
break;
}
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATETIME:
+ {
+ int error;
+ value= number_to_datetime(value, (MYSQL_TIME *) buffer, 1, &error);
+ *param->error= test(error);
+ break;
+ }
default:
{
char buff[22]; /* Enough for longlong */
@@ -3592,23 +3665,73 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
case MYSQL_TYPE_NULL: /* do nothing */
break;
case MYSQL_TYPE_TINY:
- *buffer= (uchar)value;
+ {
+ if (param->is_unsigned)
+ {
+ int8 data= (int8) value;
+ *param->error= (double) data != value;
+ *buffer= (uchar) data;
+ }
+ else
+ {
+ uchar data= (uchar) value;
+ *param->error= (double) data != value;
+ *buffer= data;
+ }
break;
+ }
case MYSQL_TYPE_SHORT:
- shortstore(buffer, (short)value);
+ {
+ if (param->is_unsigned)
+ {
+ ushort data= (ushort) value;
+ *param->error= (double) data != value;
+ shortstore(buffer, data);
+ }
+ else
+ {
+ short data= (short) value;
+ *param->error= (double) data != value;
+ shortstore(buffer, data);
+ }
break;
+ }
case MYSQL_TYPE_LONG:
- longstore(buffer, (long)value);
+ {
+ if (param->is_unsigned)
+ {
+ uint32 data= (uint32) value;
+ *param->error= (double) data != value;
+ longstore(buffer, data);
+ }
+ else
+ {
+ int32 data= (int32) value;
+ *param->error= (double) data != value;
+ longstore(buffer, data);
+ }
break;
+ }
case MYSQL_TYPE_LONGLONG:
{
- longlong val= (longlong) value;
- longlongstore(buffer, val);
+ if (param->is_unsigned)
+ {
+ ulonglong data= (ulonglong) value;
+ *param->error= (double) data != value;
+ longlongstore(buffer, data);
+ }
+ else
+ {
+ longlong data= (longlong) value;
+ *param->error= (double) data != value;
+ longlongstore(buffer, data);
+ }
break;
}
case MYSQL_TYPE_FLOAT:
{
float data= (float) value;
+ *param->error= data != value;
floatstore(buffer, data);
break;
}
@@ -3663,18 +3786,45 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
*/
static void fetch_datetime_with_conversion(MYSQL_BIND *param,
+ MYSQL_FIELD *field,
MYSQL_TIME *time)
{
switch (param->buffer_type) {
case MYSQL_TYPE_NULL: /* do nothing */
break;
case MYSQL_TYPE_DATE:
+ *(MYSQL_TIME *)(param->buffer)= *time;
+ *param->error= time->time_type != MYSQL_TIMESTAMP_DATE;
+ break;
case MYSQL_TYPE_TIME:
+ *(MYSQL_TIME *)(param->buffer)= *time;
+ *param->error= time->time_type != MYSQL_TIMESTAMP_TIME;
+ break;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
- /* XXX: should we copy only relevant members here? */
*(MYSQL_TIME *)(param->buffer)= *time;
+ /* No error: time and date are compatible with datetime */
break;
+ case MYSQL_TYPE_YEAR:
+ shortstore(param->buffer, time->year);
+ *param->error= 1;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ {
+ ulonglong value= TIME_to_ulonglong(time);
+ return fetch_float_with_conversion(param, field,
+ ulonglong2double(value), DBL_DIG);
+ }
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ {
+ longlong value= (longlong) TIME_to_ulonglong(time);
+ return fetch_long_with_conversion(param, field, value);
+ }
default:
{
/*
@@ -3772,7 +3922,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
MYSQL_TIME tm;
read_binary_date(&tm, row);
- fetch_datetime_with_conversion(param, &tm);
+ fetch_datetime_with_conversion(param, field, &tm);
break;
}
case MYSQL_TYPE_TIME:
@@ -3780,7 +3930,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
MYSQL_TIME tm;
read_binary_time(&tm, row);
- fetch_datetime_with_conversion(param, &tm);
+ fetch_datetime_with_conversion(param, field, &tm);
break;
}
case MYSQL_TYPE_DATETIME:
@@ -3789,7 +3939,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
MYSQL_TIME tm;
read_binary_datetime(&tm, row);
- fetch_datetime_with_conversion(param, &tm);
+ fetch_datetime_with_conversion(param, field, &tm);
break;
}
default:
@@ -3822,34 +3972,51 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field,
none
*/
-static void fetch_result_tinyint(MYSQL_BIND *param, uchar **row)
+static void fetch_result_tinyint(MYSQL_BIND *param, MYSQL_FIELD *field,
+ uchar **row)
{
- *(uchar *)param->buffer= **row;
+ my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG);
+ uchar data= **row;
+ *(uchar *)param->buffer= data;
+ *param->error= param->is_unsigned != field_is_unsigned && data > INT8_MAX;
(*row)++;
}
-static void fetch_result_short(MYSQL_BIND *param, uchar **row)
+static void fetch_result_short(MYSQL_BIND *param, MYSQL_FIELD *field,
+ uchar **row)
{
- short value = (short)sint2korr(*row);
- shortstore(param->buffer, value);
+ my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG);
+ ushort data= (ushort) sint2korr(*row);
+ shortstore(param->buffer, data);
+ *param->error= param->is_unsigned != field_is_unsigned && data > INT16_MAX;
*row+= 2;
}
-static void fetch_result_int32(MYSQL_BIND *param, uchar **row)
+static void fetch_result_int32(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
- int32 value= (int32)sint4korr(*row);
- longstore(param->buffer, value);
+ my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG);
+ uint32 data= (uint32) sint4korr(*row);
+ longstore(param->buffer, data);
+ *param->error= param->is_unsigned != field_is_unsigned && data > INT32_MAX;
*row+= 4;
}
-static void fetch_result_int64(MYSQL_BIND *param, uchar **row)
+static void fetch_result_int64(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
- longlong value= (longlong)sint8korr(*row);
- longlongstore(param->buffer, value);
+ my_bool field_is_unsigned= test(field->flags & UNSIGNED_FLAG);
+ ulonglong data= (ulonglong) sint8korr(*row);
+ *param->error= param->is_unsigned != field_is_unsigned && data > INT64_MAX;
+ longlongstore(param->buffer, data);
*row+= 8;
}
-static void fetch_result_float(MYSQL_BIND *param, uchar **row)
+static void fetch_result_float(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
float value;
float4get(value,*row);
@@ -3857,7 +4024,9 @@ static void fetch_result_float(MYSQL_BIND *param, uchar **row)
*row+= 4;
}
-static void fetch_result_double(MYSQL_BIND *param, uchar **row)
+static void fetch_result_double(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
double value;
float8get(value,*row);
@@ -3865,34 +4034,45 @@ static void fetch_result_double(MYSQL_BIND *param, uchar **row)
*row+= 8;
}
-static void fetch_result_time(MYSQL_BIND *param, uchar **row)
+static void fetch_result_time(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
read_binary_time(tm, row);
}
-static void fetch_result_date(MYSQL_BIND *param, uchar **row)
+static void fetch_result_date(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
read_binary_date(tm, row);
}
-static void fetch_result_datetime(MYSQL_BIND *param, uchar **row)
+static void fetch_result_datetime(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
read_binary_datetime(tm, row);
}
-static void fetch_result_bin(MYSQL_BIND *param, uchar **row)
+static void fetch_result_bin(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
ulong length= net_field_length(row);
ulong copy_length= min(length, param->buffer_length);
memcpy(param->buffer, (char *)*row, copy_length);
*param->length= length;
+ *param->error= copy_length < length;
*row+= length;
}
-static void fetch_result_str(MYSQL_BIND *param, uchar **row)
+static void fetch_result_str(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
{
ulong length= net_field_length(row);
ulong copy_length= min(length, param->buffer_length);
@@ -3901,6 +4081,7 @@ static void fetch_result_str(MYSQL_BIND *param, uchar **row)
if (copy_length != param->buffer_length)
((uchar *)param->buffer)[copy_length]= '\0';
*param->length= length; /* return total length */
+ *param->error= copy_length < length;
*row+= length;
}
@@ -3942,6 +4123,214 @@ static void skip_result_string(MYSQL_BIND *param __attribute__((unused)),
/*
+ Check that two field types are binary compatible i. e.
+ have equal representation in the binary protocol and
+ require client-side buffers of the same type.
+
+ SYNOPSIS
+ is_binary_compatible()
+ type1 parameter type supplied by user
+ type2 field type, obtained from result set metadata
+
+ RETURN
+ TRUE or FALSE
+*/
+
+static my_bool is_binary_compatible(enum enum_field_types type1,
+ enum enum_field_types type2)
+{
+ static const enum enum_field_types
+ range1[]= { MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR, 0 },
+ range2[]= { MYSQL_TYPE_INT24, MYSQL_TYPE_LONG, 0 },
+ range3[]= { MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP, 0 },
+ range4[]= { MYSQL_TYPE_ENUM, MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB,
+ MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB,
+ MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY,
+ MYSQL_TYPE_DECIMAL, 0 },
+ *range_list[]= { range1, range2, range3, range4 },
+ **range_list_end= range_list + sizeof(range_list)/sizeof(*range_list);
+ enum enum_field_types **range, *type;
+
+ if (type1 == type2)
+ return TRUE;
+ for (range= range_list; range != range_list_end; ++range)
+ {
+ /* check that both type1 and type2 are in the same range */
+ bool type1_found= FALSE, type2_found= FALSE;
+ for (type= *range; *type; type++)
+ {
+ type1_found|= type1 == *type;
+ type2_found|= type2 == *type;
+ }
+ if (type1_found || type2_found)
+ return type1_found && type2_found;
+ }
+ return FALSE;
+}
+
+
+/*
+ Setup a fetch function for one column of a result set.
+
+ SYNOPSIS
+ setup_one_fetch_function()
+ param output buffer descriptor
+ field column descriptor
+
+ DESCRIPTION
+ When user binds result set buffers or when result set
+ metadata is changed, we need to setup fetch (and possibly
+ conversion) functions for all columns of the result set.
+ In addition to that here we set up skip_result function, used
+ to update result set metadata in case when
+ STMT_ATTR_UPDATE_MAX_LENGTH attribute is set.
+ Notice that while fetch_result is chosen depending on both
+ field->type and param->type, skip_result depends on field->type
+ only.
+
+ RETURN
+ TRUE fetch function for this typecode was not found (typecode
+ is not supported by the client library)
+ FALSE success
+*/
+
+static my_bool setup_one_fetch_function(MYSQL_BIND *param, MYSQL_FIELD *field)
+{
+ /* Setup data copy functions for the different supported types */
+ switch (param->buffer_type) {
+ case MYSQL_TYPE_NULL: /* for dummy binds */
+ /*
+ It's not binary compatible with anything the server can return:
+ no need to setup fetch_result, as it'll be reset anyway
+ */
+ *param->length= 0;
+ break;
+ case MYSQL_TYPE_TINY:
+ param->fetch_result= fetch_result_tinyint;
+ *param->length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ param->fetch_result= fetch_result_short;
+ *param->length= 2;
+ break;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ param->fetch_result= fetch_result_int32;
+ *param->length= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ param->fetch_result= fetch_result_int64;
+ *param->length= 8;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ param->fetch_result= fetch_result_float;
+ *param->length= 4;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ param->fetch_result= fetch_result_double;
+ *param->length= 8;
+ break;
+ case MYSQL_TYPE_TIME:
+ param->fetch_result= fetch_result_time;
+ *param->length= sizeof(MYSQL_TIME);
+ break;
+ case MYSQL_TYPE_DATE:
+ param->fetch_result= fetch_result_date;
+ *param->length= sizeof(MYSQL_TIME);
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ param->fetch_result= fetch_result_datetime;
+ *param->length= sizeof(MYSQL_TIME);
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ DBUG_ASSERT(param->buffer_length != 0);
+ param->fetch_result= fetch_result_bin;
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ DBUG_ASSERT(param->buffer_length != 0);
+ param->fetch_result= fetch_result_str;
+ break;
+ default:
+ return TRUE;
+ }
+ if (! is_binary_compatible(param->buffer_type, field->type))
+ param->fetch_result= fetch_result_with_conversion;
+
+ /* Setup skip_result functions (to calculate max_length) */
+ param->skip_result= skip_result_fixed;
+ switch (field->type) {
+ case MYSQL_TYPE_NULL: /* for dummy binds */
+ param->pack_length= 0;
+ field->max_length= 0;
+ break;
+ case MYSQL_TYPE_TINY:
+ param->pack_length= 1;
+ field->max_length= 4; /* as in '-127' */
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ param->pack_length= 2;
+ field->max_length= 6; /* as in '-32767' */
+ break;
+ case MYSQL_TYPE_INT24:
+ field->max_length= 9; /* as in '16777216' or in '-8388607' */
+ param->pack_length= 4;
+ break;
+ case MYSQL_TYPE_LONG:
+ field->max_length= 11; /* '-2147483647' */
+ param->pack_length= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ field->max_length= 21; /* '18446744073709551616' */
+ param->pack_length= 8;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ param->pack_length= 4;
+ field->max_length= MAX_DOUBLE_STRING_REP_LENGTH;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ param->pack_length= 8;
+ field->max_length= MAX_DOUBLE_STRING_REP_LENGTH;
+ break;
+ case MYSQL_TYPE_TIME:
+ field->max_length= 15; /* 19:23:48.123456 */
+ param->skip_result= skip_result_with_length;
+ case MYSQL_TYPE_DATE:
+ field->max_length= 10; /* 2003-11-11 */
+ param->skip_result= skip_result_with_length;
+ break;
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ param->skip_result= skip_result_with_length;
+ field->max_length= MAX_DATE_STRING_REP_LENGTH;
+ break;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ param->skip_result= skip_result_string;
+ break;
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
Setup the bind buffers for resultset processing
*/
@@ -3951,6 +4340,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
MYSQL_FIELD *field;
ulong bind_count= stmt->field_count;
uint param_count= 0;
+ uchar report_data_truncation= 0;
DBUG_ENTER("mysql_stmt_bind_result");
DBUG_PRINT("enter",("field_count: %d", bind_count));
@@ -3981,144 +4371,29 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
This is to make the execute code easier
*/
if (!param->is_null)
- param->is_null= &param->internal_is_null;
+ param->is_null= &param->is_null_value;
if (!param->length)
- param->length= &param->internal_length;
+ param->length= &param->length_value;
+
+ if (!param->error)
+ param->error= &param->error_value;
+ else
+ report_data_truncation= REPORT_DATA_TRUNCATION;
param->param_number= param_count++;
param->offset= 0;
- /* Setup data copy functions for the different supported types */
- switch (param->buffer_type) {
- case MYSQL_TYPE_NULL: /* for dummy binds */
- *param->length= 0;
- break;
- case MYSQL_TYPE_TINY:
- param->fetch_result= fetch_result_tinyint;
- *param->length= 1;
- break;
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_YEAR:
- param->fetch_result= fetch_result_short;
- *param->length= 2;
- break;
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_LONG:
- param->fetch_result= fetch_result_int32;
- *param->length= 4;
- break;
- case MYSQL_TYPE_LONGLONG:
- param->fetch_result= fetch_result_int64;
- *param->length= 8;
- break;
- case MYSQL_TYPE_FLOAT:
- param->fetch_result= fetch_result_float;
- *param->length= 4;
- break;
- case MYSQL_TYPE_DOUBLE:
- param->fetch_result= fetch_result_double;
- *param->length= 8;
- break;
- case MYSQL_TYPE_TIME:
- param->fetch_result= fetch_result_time;
- *param->length= sizeof(MYSQL_TIME);
- break;
- case MYSQL_TYPE_DATE:
- param->fetch_result= fetch_result_date;
- *param->length= sizeof(MYSQL_TIME);
- break;
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- param->fetch_result= fetch_result_datetime;
- *param->length= sizeof(MYSQL_TIME);
- break;
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- DBUG_ASSERT(param->buffer_length != 0);
- param->fetch_result= fetch_result_bin;
- break;
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_STRING:
- DBUG_ASSERT(param->buffer_length != 0);
- param->fetch_result= fetch_result_str;
- break;
- default:
- strmov(stmt->sqlstate, unknown_sqlstate);
- sprintf(stmt->last_error,
- ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
- param->buffer_type, param_count);
- DBUG_RETURN(1);
- }
-
- /* Setup skip_result functions (to calculate max_length) */
- param->skip_result= skip_result_fixed;
- switch (field->type) {
- case MYSQL_TYPE_NULL: /* for dummy binds */
- param->pack_length= 0;
- field->max_length= 0;
- break;
- case MYSQL_TYPE_TINY:
- param->pack_length= 1;
- field->max_length= 4; /* as in '-127' */
- break;
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_SHORT:
- param->pack_length= 2;
- field->max_length= 6; /* as in '-32767' */
- break;
- case MYSQL_TYPE_INT24:
- field->max_length= 9; /* as in '16777216' or in '-8388607' */
- param->pack_length= 4;
- break;
- case MYSQL_TYPE_LONG:
- field->max_length= 11; /* '-2147483647' */
- param->pack_length= 4;
- break;
- case MYSQL_TYPE_LONGLONG:
- field->max_length= 21; /* '18446744073709551616' */
- param->pack_length= 8;
- break;
- case MYSQL_TYPE_FLOAT:
- param->pack_length= 4;
- field->max_length= MAX_DOUBLE_STRING_REP_LENGTH;
- break;
- case MYSQL_TYPE_DOUBLE:
- param->pack_length= 8;
- field->max_length= MAX_DOUBLE_STRING_REP_LENGTH;
- break;
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- param->skip_result= skip_result_with_length;
- field->max_length= MAX_DATE_STRING_REP_LENGTH;
- break;
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_GEOMETRY:
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_STRING:
- param->skip_result= skip_result_string;
- break;
- default:
+ if (setup_one_fetch_function(param, field))
+ {
strmov(stmt->sqlstate, unknown_sqlstate);
sprintf(stmt->last_error,
- ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
- field->type, param_count);
+ ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
+ field->type, param_count);
DBUG_RETURN(1);
}
}
- stmt->bind_result_done= TRUE;
+ stmt->bind_result_done= BIND_RESULT_DONE | report_data_truncation;
DBUG_RETURN(0);
}
@@ -4132,6 +4407,7 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
MYSQL_BIND *bind, *end;
MYSQL_FIELD *field;
uchar *null_ptr, bit;
+ int truncation_count= 0;
/*
Precondition: if stmt->field_count is zero or row is NULL, read_row_*
function must return no data.
@@ -4154,26 +4430,25 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
bind < end ;
bind++, field++)
{
+ *bind->error= 0;
if (*null_ptr & bit)
{
/*
- We should set both inter_buffer and is_null to be able to see
+ We should set both row_ptr and is_null to be able to see
nulls in mysql_stmt_fetch_column. This is because is_null may point
to user data which can be overwritten between mysql_stmt_fetch and
mysql_stmt_fetch_column, and in this case nullness of column will be
lost. See mysql_stmt_fetch_column for details.
*/
- bind->inter_buffer= NULL;
+ bind->row_ptr= NULL;
*bind->is_null= 1;
}
else
{
*bind->is_null= 0;
- bind->inter_buffer= row;
- if (field->type == bind->buffer_type)
- (*bind->fetch_result)(bind, &row);
- else
- fetch_result_with_conversion(bind, field, &row);
+ bind->row_ptr= row;
+ (*bind->fetch_result)(bind, field, &row);
+ truncation_count+= *bind->error;
}
if (!((bit<<=1) & 255))
{
@@ -4181,6 +4456,8 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
null_ptr++;
}
}
+ if (truncation_count && (stmt->bind_result_done & REPORT_DATA_TRUNCATION))
+ return MYSQL_DATA_TRUNCATED;
return 0;
}
@@ -4207,7 +4484,7 @@ int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt)
DBUG_ENTER("mysql_stmt_fetch");
if ((rc= (*stmt->read_row_func)(stmt, &row)) ||
- (rc= stmt_fetch_row(stmt, row)))
+ ((rc= stmt_fetch_row(stmt, row)) && rc != MYSQL_DATA_TRUNCATED))
{
stmt->state= MYSQL_STMT_PREPARE_DONE; /* XXX: this is buggy */
stmt->read_row_func= stmt_read_row_no_data;
@@ -4254,17 +4531,20 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
DBUG_RETURN(1);
}
- if (param->inter_buffer)
+ if (!bind->error)
+ bind->error= &bind->error_value;
+ *bind->error= 0;
+ if (param->row_ptr)
{
MYSQL_FIELD *field= stmt->fields+column;
- uchar *row= param->inter_buffer;
+ uchar *row= param->row_ptr;
bind->offset= offset;
if (bind->is_null)
*bind->is_null= 0;
if (bind->length) /* Set the length if non char/binary types */
*bind->length= *param->length;
else
- bind->length= &param->internal_length; /* Needed for fetch_result() */
+ bind->length= &param->length_value; /* Needed for fetch_result() */
fetch_result_with_conversion(bind, field, &row);
}
else
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index 4c5dd361061..45adb657f73 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -872,3 +872,167 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to)
return 0;
}
}
+
+
+/*
+ Convert datetime value specified as number to broken-down TIME
+ representation and form value of DATETIME type as side-effect.
+
+ SYNOPSIS
+ number_to_datetime()
+ nr - datetime value as number
+ time_res - pointer for structure for broken-down representation
+ fuzzy_date - indicates whenever we allow fuzzy dates
+ was_cut - set ot 1 if there was some kind of error during
+ conversion or to 0 if everything was OK.
+
+ DESCRIPTION
+ Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
+ YYYYMMDDHHMMSS to broken-down TIME representation. Return value in
+ YYYYMMDDHHMMSS format as side-effect.
+
+ This function also checks if datetime value fits in DATETIME range.
+
+ RETURN VALUE
+ Datetime value in YYYYMMDDHHMMSS format.
+ If input value is not valid datetime value then 0 is returned.
+*/
+
+longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
+ my_bool fuzzy_date, int *was_cut)
+{
+ long part1,part2;
+
+ *was_cut= 0;
+
+ if (nr == LL(0) || nr >= LL(10000101000000))
+ goto ok;
+ if (nr < 101)
+ goto err;
+ if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
+ {
+ nr= (nr+20000000L)*1000000L; // YYMMDD, year: 2000-2069
+ goto ok;
+ }
+ if (nr < (YY_PART_YEAR)*10000L+101L)
+ goto err;
+ if (nr <= 991231L)
+ {
+ nr= (nr+19000000L)*1000000L; // YYMMDD, year: 1970-1999
+ goto ok;
+ }
+ if (nr < 10000101L)
+ goto err;
+ if (nr <= 99991231L)
+ {
+ nr= nr*1000000L;
+ goto ok;
+ }
+ if (nr < 101000000L)
+ goto err;
+ if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
+ {
+ nr= nr+LL(20000000000000); // YYMMDDHHMMSS, 2000-2069
+ goto ok;
+ }
+ if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
+ goto err;
+ if (nr <= LL(991231235959))
+ nr= nr+LL(19000000000000); // YYMMDDHHMMSS, 1970-1999
+
+ ok:
+ part1=(long) (nr/LL(1000000));
+ part2=(long) (nr - (longlong) part1*LL(1000000));
+ time_res->year= (int) (part1/10000L); part1%=10000L;
+ time_res->month= (int) part1 / 100;
+ time_res->day= (int) part1 % 100;
+ time_res->hour= (int) (part2/10000L); part2%=10000L;
+ time_res->minute=(int) part2 / 100;
+ time_res->second=(int) part2 % 100;
+
+ if (time_res->year <= 9999 && time_res->month <= 12 &&
+ time_res->day <= 31 && time_res->hour <= 23 &&
+ time_res->minute <= 59 && time_res->second <= 59 &&
+ (fuzzy_date || (time_res->month != 0 && time_res->day != 0) || nr==0))
+ return nr;
+
+ err:
+
+ *was_cut= 1;
+ return LL(0);
+}
+
+
+/* Convert time value to integer in YYYYMMDDHHMMSS format */
+
+ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *time)
+{
+ return ((ulonglong) (time->year * 10000UL +
+ time->month * 100UL +
+ time->day) * ULL(1000000) +
+ (ulonglong) (time->hour * 10000UL +
+ time->minute * 100UL +
+ time->second));
+}
+
+
+/* Convert TIME value to integer in YYYYMMDD format */
+
+ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *time)
+{
+ return (ulonglong) (time->year * 10000UL + time->month * 100UL + time->day);
+}
+
+
+/*
+ Convert TIME value to integer in HHMMSS format.
+ This function doesn't take into account time->day member:
+ it's assumed that days have been converted to hours already.
+*/
+
+ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *time)
+{
+ return (ulonglong) (time->hour * 10000UL +
+ time->minute * 100UL +
+ time->second);
+}
+
+
+/*
+ Convert struct TIME (date and time split into year/month/day/hour/...
+ to a number in format YYYYMMDDHHMMSS (DATETIME),
+ YYYYMMDD (DATE) or HHMMSS (TIME).
+
+ SYNOPSIS
+ TIME_to_ulonglong()
+
+ DESCRIPTION
+ The function is used when we need to convert value of time item
+ to a number if it's used in numeric context, i. e.:
+ SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
+ SELECT ?+1;
+
+ NOTE
+ This function doesn't check that given TIME structure members are
+ in valid range. If they are not, return value won't reflect any
+ valid date either.
+*/
+
+ulonglong TIME_to_ulonglong(const MYSQL_TIME *time)
+{
+ switch (time->time_type) {
+ case MYSQL_TIMESTAMP_DATETIME:
+ return TIME_to_ulonglong_datetime(time);
+ case MYSQL_TIMESTAMP_DATE:
+ return TIME_to_ulonglong_date(time);
+ case MYSQL_TIMESTAMP_TIME:
+ return TIME_to_ulonglong_time(time);
+ case MYSQL_TIMESTAMP_NONE:
+ case MYSQL_TIMESTAMP_ERROR:
+ return ULL(0);
+ default:
+ DBUG_ASSERT(0);
+ }
+ return 0;
+}
+
diff --git a/sql/field.cc b/sql/field.cc
index dafb3dc25da..6f38bd3c85a 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -467,11 +467,11 @@ bool Field::get_time(TIME *ltime)
Needs to be changed if/when we want to support different time formats
*/
-void Field::store_time(TIME *ltime,timestamp_type type)
+int Field::store_time(TIME *ltime, timestamp_type type)
{
char buff[MAX_DATE_STRING_REP_LENGTH];
uint length= (uint) my_TIME_to_str(ltime, buff);
- store(buff, length, &my_charset_bin);
+ return store(buff, length, &my_charset_bin);
}
@@ -3089,7 +3089,7 @@ int Field_timestamp::store(longlong nr)
bool in_dst_time_gap;
THD *thd= table->in_use;
- if (number_to_TIME(nr, &l_time, 0, &error))
+ if (number_to_datetime(nr, &l_time, 0, &error))
{
if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
{
@@ -3372,6 +3372,16 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
}
+int Field_time::store_time(TIME *ltime, timestamp_type type)
+{
+ long tmp= ((ltime->month ? 0 : ltime->day * 24L) + ltime->hour) * 10000L +
+ (ltime->minute * 100 + ltime->second);
+ if (ltime->neg)
+ tmp= -tmp;
+ return Field_time::store((longlong) tmp);
+}
+
+
int Field_time::store(double nr)
{
long tmp;
@@ -3953,17 +3963,20 @@ int Field_newdate::store(longlong nr)
return error;
}
-void Field_newdate::store_time(TIME *ltime,timestamp_type type)
+int Field_newdate::store_time(TIME *ltime,timestamp_type type)
{
long tmp;
+ int error= 0;
if (type == MYSQL_TIMESTAMP_DATE || type == MYSQL_TIMESTAMP_DATETIME)
tmp=ltime->year*16*32+ltime->month*32+ltime->day;
else
{
tmp=0;
+ error= 1;
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
int3store(ptr,tmp);
+ return error;
}
bool Field_newdate::send_binary(Protocol *protocol)
@@ -4112,7 +4125,7 @@ int Field_datetime::store(longlong nr)
int error;
longlong initial_nr= nr;
- nr= number_to_TIME(nr, &not_used, 1, &error);
+ nr= number_to_datetime(nr, &not_used, 1, &error);
if (error)
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -4131,9 +4144,10 @@ int Field_datetime::store(longlong nr)
}
-void Field_datetime::store_time(TIME *ltime,timestamp_type type)
+int Field_datetime::store_time(TIME *ltime,timestamp_type type)
{
longlong tmp;
+ int error= 0;
/*
We don't perform range checking here since values stored in TIME
structure always fit into DATETIME range.
@@ -4144,6 +4158,7 @@ void Field_datetime::store_time(TIME *ltime,timestamp_type type)
else
{
tmp=0;
+ error= 1;
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
#ifdef WORDS_BIGENDIAN
@@ -4154,6 +4169,7 @@ void Field_datetime::store_time(TIME *ltime,timestamp_type type)
else
#endif
longlongstore(ptr,tmp);
+ return error;
}
bool Field_datetime::send_binary(Protocol *protocol)
diff --git a/sql/field.h b/sql/field.h
index 4353780f9a4..e2411fb9400 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -96,7 +96,7 @@ public:
virtual int store(const char *to,uint length,CHARSET_INFO *cs)=0;
virtual int store(double nr)=0;
virtual int store(longlong nr)=0;
- virtual void store_time(TIME *ltime,timestamp_type t_type);
+ virtual int store_time(TIME *ltime, timestamp_type t_type);
virtual double val_real(void)=0;
virtual longlong val_int(void)=0;
inline String *val_str(String *str) { return val_str(str, str); }
@@ -782,7 +782,7 @@ public:
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr);
- void store_time(TIME *ltime,timestamp_type type);
+ int store_time(TIME *ltime, timestamp_type type);
void reset(void) { ptr[0]=ptr[1]=ptr[2]=0; }
double val_real(void);
longlong val_int(void);
@@ -815,6 +815,7 @@ public:
enum_field_types type() const { return FIELD_TYPE_TIME;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
enum Item_result cmp_type () const { return INT_RESULT; }
+ int store_time(TIME *ltime, timestamp_type type);
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr);
@@ -855,7 +856,7 @@ public:
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr);
- void store_time(TIME *ltime,timestamp_type type);
+ int store_time(TIME *ltime, timestamp_type type);
void reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; }
double val_real(void);
longlong val_int(void);
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 91d15dc1125..4985a244824 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1139,8 +1139,6 @@ my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *not_exist);
bool str_to_time_with_warn(const char *str,uint length,TIME *l_time);
timestamp_type str_to_datetime_with_warn(const char *str, uint length,
TIME *l_time, uint flags);
-longlong number_to_TIME(longlong nr, TIME *time_res, bool fuzzy_date,
- int *was_cut);
void localtime_to_TIME(TIME *to, struct tm *from);
void calc_time_from_sec(TIME *to, long seconds, long microseconds);
@@ -1162,10 +1160,6 @@ void make_date(const DATE_TIME_FORMAT *format, const TIME *l_time,
String *str);
void make_time(const DATE_TIME_FORMAT *format, const TIME *l_time,
String *str);
-ulonglong TIME_to_ulonglong_datetime(const TIME *time);
-ulonglong TIME_to_ulonglong_date(const TIME *time);
-ulonglong TIME_to_ulonglong_time(const TIME *time);
-ulonglong TIME_to_ulonglong(const TIME *time);
int test_if_number(char *str,int *res,bool allow_wildcards);
void change_byte(byte *,uint,char,char);
diff --git a/sql/time.cc b/sql/time.cc
index 562f9956ccc..f1d21915c23 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -263,95 +263,6 @@ str_to_time_with_warn(const char *str, uint length, TIME *l_time)
/*
- Convert datetime value specified as number to broken-down TIME
- representation and form value of DATETIME type as side-effect.
-
- SYNOPSIS
- number_to_TIME()
- nr - datetime value as number
- time_res - pointer for structure for broken-down representation
- fuzzy_date - indicates whenever we allow fuzzy dates
- was_cut - set ot 1 if there was some kind of error during
- conversion or to 0 if everything was OK.
-
- DESCRIPTION
- Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
- YYYYMMDDHHMMSS to broken-down TIME representation. Return value in
- YYYYMMDDHHMMSS format as side-effect.
-
- This function also checks if datetime value fits in DATETIME range.
-
- RETURN VALUE
- Datetime value in YYYYMMDDHHMMSS format.
- If input value is not valid datetime value then 0 is returned.
-*/
-
-longlong number_to_TIME(longlong nr, TIME *time_res, bool fuzzy_date,
- int *was_cut)
-{
- long part1,part2;
-
- *was_cut= 0;
-
- if (nr == LL(0) || nr >= LL(10000101000000))
- goto ok;
- if (nr < 101)
- goto err;
- if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
- {
- nr= (nr+20000000L)*1000000L; // YYMMDD, year: 2000-2069
- goto ok;
- }
- if (nr < (YY_PART_YEAR)*10000L+101L)
- goto err;
- if (nr <= 991231L)
- {
- nr= (nr+19000000L)*1000000L; // YYMMDD, year: 1970-1999
- goto ok;
- }
- if (nr < 10000101L)
- goto err;
- if (nr <= 99991231L)
- {
- nr= nr*1000000L;
- goto ok;
- }
- if (nr < 101000000L)
- goto err;
- if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
- {
- nr= nr+LL(20000000000000); // YYMMDDHHMMSS, 2000-2069
- goto ok;
- }
- if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
- goto err;
- if (nr <= LL(991231235959))
- nr= nr+LL(19000000000000); // YYMMDDHHMMSS, 1970-1999
-
- ok:
- part1=(long) (nr/LL(1000000));
- part2=(long) (nr - (longlong) part1*LL(1000000));
- time_res->year= (int) (part1/10000L); part1%=10000L;
- time_res->month= (int) part1 / 100;
- time_res->day= (int) part1 % 100;
- time_res->hour= (int) (part2/10000L); part2%=10000L;
- time_res->minute=(int) part2 / 100;
- time_res->second=(int) part2 % 100;
-
- if (time_res->year <= 9999 && time_res->month <= 12 &&
- time_res->day <= 31 && time_res->hour <= 23 &&
- time_res->minute <= 59 && time_res->second <= 59 &&
- (fuzzy_date || (time_res->month != 0 && time_res->day != 0) || nr==0))
- return nr;
-
- err:
-
- *was_cut= 1;
- return LL(0);
-}
-
-
-/*
Convert a system time structure to TIME
*/
@@ -807,77 +718,4 @@ void make_truncated_value_warning(THD *thd, const char *str_val,
}
-/* Convert time value to integer in YYYYMMDDHHMMSS format */
-
-ulonglong TIME_to_ulonglong_datetime(const TIME *time)
-{
- return ((ulonglong) (time->year * 10000UL +
- time->month * 100UL +
- time->day) * ULL(1000000) +
- (ulonglong) (time->hour * 10000UL +
- time->minute * 100UL +
- time->second));
-}
-
-
-/* Convert TIME value to integer in YYYYMMDD format */
-
-ulonglong TIME_to_ulonglong_date(const TIME *time)
-{
- return (ulonglong) (time->year * 10000UL + time->month * 100UL + time->day);
-}
-
-
-/*
- Convert TIME value to integer in HHMMSS format.
- This function doesn't take into account time->day member:
- it's assumed that days have been converted to hours already.
-*/
-
-ulonglong TIME_to_ulonglong_time(const TIME *time)
-{
- return (ulonglong) (time->hour * 10000UL +
- time->minute * 100UL +
- time->second);
-}
-
-
-/*
- Convert struct TIME (date and time split into year/month/day/hour/...
- to a number in format YYYYMMDDHHMMSS (DATETIME),
- YYYYMMDD (DATE) or HHMMSS (TIME).
-
- SYNOPSIS
- TIME_to_ulonglong()
-
- DESCRIPTION
- The function is used when we need to convert value of time item
- to a number if it's used in numeric context, i. e.:
- SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
- SELECT ?+1;
-
- NOTE
- This function doesn't check that given TIME structure members are
- in valid range. If they are not, return value won't reflect any
- valid date either.
-*/
-
-ulonglong TIME_to_ulonglong(const TIME *time)
-{
- switch (time->time_type) {
- case MYSQL_TIMESTAMP_DATETIME:
- return TIME_to_ulonglong_datetime(time);
- case MYSQL_TIMESTAMP_DATE:
- return TIME_to_ulonglong_date(time);
- case MYSQL_TIMESTAMP_TIME:
- return TIME_to_ulonglong_time(time);
- case MYSQL_TIMESTAMP_NONE:
- case MYSQL_TIMESTAMP_ERROR:
- return ULL(0);
- default:
- DBUG_ASSERT(0);
- }
- return 0;
-}
-
#endif
diff --git a/tests/client_test.c b/tests/client_test.c
index 979c1563c02..e63580ce031 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -517,16 +517,18 @@ int my_process_stmt_result(MYSQL_STMT *stmt)
buffer[i].buffer= (void *) data[i];
buffer[i].is_null= &is_null[i];
}
- my_print_result_metadata(result);
rc= mysql_stmt_bind_result(stmt, buffer);
check_execute(stmt, rc);
+ rc= 1;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*)&rc);
rc= mysql_stmt_store_result(stmt);
check_execute(stmt, rc);
+ my_print_result_metadata(result);
mysql_field_seek(result, 0);
- while (mysql_stmt_fetch(stmt) == 0)
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
{
if (!opt_silent)
{
@@ -559,6 +561,7 @@ int my_process_stmt_result(MYSQL_STMT *stmt)
}
row_count++;
}
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
if (!opt_silent)
{
if (row_count)
@@ -1876,6 +1879,7 @@ static void test_fetch_null()
myquery(rc);
/* fetch */
+ bzero(bind, sizeof(bind));
for (i= 0; i < (int) array_elements(bind); i++)
{
bind[i].buffer_type= MYSQL_TYPE_LONG;
@@ -2941,11 +2945,13 @@ static void test_long_data_str1()
bind[0].buffer= (void *) &data; /* this buffer won't be altered */
bind[0].buffer_length= 16;
bind[0].length= &blob_length;
+ bind[0].error= &bind[0].error_value;
rc= mysql_stmt_bind_result(stmt, bind);
data[16]= 0;
rc= mysql_stmt_fetch(stmt);
- DIE_UNLESS(rc == 0);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(bind[0].error_value);
DIE_UNLESS(strlen(data) == 16);
DIE_UNLESS(blob_length == max_blob_length);
@@ -3308,10 +3314,10 @@ static void test_bind_result()
/* fetch */
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= (void *) &nData; /* integer data */
bind[0].is_null= &is_null[0];
- bind[0].length= 0;
bind[1].buffer_type= MYSQL_TYPE_STRING;
bind[1].buffer= szData; /* string data */
@@ -3402,6 +3408,7 @@ static void test_bind_result_ext()
rc= mysql_commit(mysql);
myquery(rc);
+ bzero(bind, sizeof(bind));
for (i= 0; i < (int) array_elements(bind); i++)
{
bind[i].length= &length[i];
@@ -3520,37 +3527,46 @@ static void test_bind_result_ext1()
rc= mysql_commit(mysql);
myquery(rc);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= (void *) t_data;
bind[0].buffer_length= sizeof(t_data);
+ bind[0].error= &bind[0].error_value;
bind[1].buffer_type= MYSQL_TYPE_FLOAT;
bind[1].buffer= (void *)&s_data;
bind[1].buffer_length= 0;
+ bind[1].error= &bind[1].error_value;
bind[2].buffer_type= MYSQL_TYPE_SHORT;
bind[2].buffer= (void *)&i_data;
bind[2].buffer_length= 0;
+ bind[2].error= &bind[2].error_value;
bind[3].buffer_type= MYSQL_TYPE_TINY;
bind[3].buffer= (void *)&b_data;
bind[3].buffer_length= 0;
+ bind[3].error= &bind[3].error_value;
bind[4].buffer_type= MYSQL_TYPE_LONG;
bind[4].buffer= (void *)&f_data;
bind[4].buffer_length= 0;
+ bind[4].error= &bind[4].error_value;
bind[5].buffer_type= MYSQL_TYPE_STRING;
bind[5].buffer= (void *)d_data;
bind[5].buffer_length= sizeof(d_data);
+ bind[5].error= &bind[5].error_value;
bind[6].buffer_type= MYSQL_TYPE_LONG;
bind[6].buffer= (void *)&bData;
bind[6].buffer_length= 0;
+ bind[6].error= &bind[6].error_value;
bind[7].buffer_type= MYSQL_TYPE_DOUBLE;
bind[7].buffer= (void *)&szData;
bind[7].buffer_length= 0;
+ bind[7].error= &bind[7].error_value;
for (i= 0; i < array_elements(bind); i++)
{
@@ -3568,7 +3584,8 @@ static void test_bind_result_ext1()
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
- check_execute(stmt, rc);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(bind[4].error_value == 1);
if (!opt_silent)
{
@@ -3803,6 +3820,7 @@ static void test_fetch_date()
rc= mysql_commit(mysql);
myquery(rc);
+ bzero(bind, sizeof(bind));
for (i= 0; i < array_elements(bind); i++)
{
bind[i].is_null= &is_null[i];
@@ -4605,8 +4623,6 @@ static void test_set_variable()
get_bind[1].buffer_type= MYSQL_TYPE_LONG;
get_bind[1].buffer= (void *)&get_count;
- get_bind[1].is_null= 0;
- get_bind[1].length= 0;
rc= mysql_stmt_execute(stmt1);
check_execute(stmt1, rc);
@@ -5522,6 +5538,7 @@ static void test_store_result()
myquery(rc);
/* fetch */
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= (void *) &nData; /* integer data */
bind[0].length= &length;
@@ -5988,7 +6005,7 @@ static void test_bind_date_conv(uint row_count)
for (count= 0; count < row_count; count++)
{
rc= mysql_stmt_fetch(stmt);
- check_execute(stmt, rc);
+ DIE_UNLESS(rc == 0 || rc == MYSQL_DATA_TRUNCATED);
if (!opt_silent)
fprintf(stdout, "\n");
@@ -6004,14 +6021,8 @@ static void test_bind_date_conv(uint row_count)
DIE_UNLESS(tm[i].day == 0 || tm[i].day == day+count);
DIE_UNLESS(tm[i].hour == 0 || tm[i].hour == hour+count);
-#ifdef NOT_USED
- /*
- minute causes problems from date<->time, don't assert, instead
- validate separatly in another routine
- */
DIE_UNLESS(tm[i].minute == 0 || tm[i].minute == minute+count);
DIE_UNLESS(tm[i].second == 0 || tm[i].second == sec+count);
-#endif
DIE_UNLESS(tm[i].second_part == 0 ||
tm[i].second_part == second_part+count);
}
@@ -6242,13 +6253,15 @@ static void test_buffers()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
- bzero(buffer, 20); /* Avoid overruns in printf() */
+ bzero(buffer, sizeof(buffer)); /* Avoid overruns in printf() */
+ bzero(bind, sizeof(bind));
bind[0].length= &length;
bind[0].is_null= &is_null;
bind[0].buffer_length= 1;
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= (void *)buffer;
+ bind[0].error= &bind[0].error_value;
rc= mysql_stmt_bind_result(stmt, bind);
check_execute(stmt, rc);
@@ -6258,7 +6271,8 @@ static void test_buffers()
buffer[1]= 'X';
rc= mysql_stmt_fetch(stmt);
- check_execute(stmt, rc);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(bind[0].error_value);
if (!opt_silent)
fprintf(stdout, "\n data: %s (%lu)", buffer, length);
DIE_UNLESS(buffer[0] == 'M');
@@ -6292,7 +6306,8 @@ static void test_buffers()
check_execute(stmt, rc);
rc= mysql_stmt_fetch(stmt);
- check_execute(stmt, rc);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(bind[0].error_value);
if (!opt_silent)
fprintf(stdout, "\n data: %s (%lu)", buffer, length);
DIE_UNLESS(strncmp(buffer, "Popula", 6) == 0);
@@ -6429,10 +6444,9 @@ static void test_fetch_nobuffs()
fprintf(stdout, "\n total rows : %d", rc);
DIE_UNLESS(rc == 1);
+ bzero(bind, sizeof(MYSQL_BIND));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= (void *)str[0];
- bind[0].is_null= 0;
- bind[0].length= 0;
bind[0].buffer_length= sizeof(str[0]);
bind[1]= bind[2]= bind[3]= bind[0];
bind[1].buffer= (void *)str[1];
@@ -6489,7 +6503,8 @@ static void test_ushort_bug()
d smallint unsigned)");
myquery(rc);
- rc= mysql_query(mysql, "INSERT INTO test_ushort VALUES(35999, 35999, 35999, 200)");
+ rc= mysql_query(mysql,
+ "INSERT INTO test_ushort VALUES(35999, 35999, 35999, 200)");
myquery(rc);
@@ -6499,24 +6514,23 @@ static void test_ushort_bug()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_SHORT;
bind[0].buffer= (void *)&short_value;
- bind[0].is_null= 0;
+ bind[0].is_unsigned= TRUE;
bind[0].length= &s_length;
bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= (void *)&long_value;
- bind[1].is_null= 0;
bind[1].length= &l_length;
bind[2].buffer_type= MYSQL_TYPE_LONGLONG;
bind[2].buffer= (void *)&longlong_value;
- bind[2].is_null= 0;
bind[2].length= &ll_length;
bind[3].buffer_type= MYSQL_TYPE_TINY;
bind[3].buffer= (void *)&tiny_value;
- bind[3].is_null= 0;
+ bind[3].is_unsigned= TRUE;
bind[3].length= &t_length;
rc= mysql_stmt_bind_result(stmt, bind);
@@ -6586,24 +6600,22 @@ static void test_sshort_bug()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_SHORT;
bind[0].buffer= (void *)&short_value;
- bind[0].is_null= 0;
bind[0].length= &s_length;
bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= (void *)&long_value;
- bind[1].is_null= 0;
bind[1].length= &l_length;
bind[2].buffer_type= MYSQL_TYPE_LONGLONG;
bind[2].buffer= (void *)&longlong_value;
- bind[2].is_null= 0;
bind[2].length= &ll_length;
bind[3].buffer_type= MYSQL_TYPE_TINY;
bind[3].buffer= (void *)&tiny_value;
- bind[3].is_null= 0;
+ bind[3].is_unsigned= TRUE;
bind[3].length= &t_length;
rc= mysql_stmt_bind_result(stmt, bind);
@@ -6673,24 +6685,21 @@ static void test_stiny_bug()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_SHORT;
bind[0].buffer= (void *)&short_value;
- bind[0].is_null= 0;
bind[0].length= &s_length;
bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= (void *)&long_value;
- bind[1].is_null= 0;
bind[1].length= &l_length;
bind[2].buffer_type= MYSQL_TYPE_LONGLONG;
bind[2].buffer= (void *)&longlong_value;
- bind[2].is_null= 0;
bind[2].length= &ll_length;
bind[3].buffer_type= MYSQL_TYPE_TINY;
bind[3].buffer= (void *)&tiny_value;
- bind[3].is_null= 0;
bind[3].length= &t_length;
rc= mysql_stmt_bind_result(stmt, bind);
@@ -6783,10 +6792,10 @@ static void test_field_misc()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= table_type;
bind[0].length= &type_length;
- bind[0].is_null= 0;
bind[0].buffer_length= NAME_LEN;
rc= mysql_stmt_bind_result(stmt, bind);
@@ -6857,10 +6866,10 @@ static void test_field_misc()
DIE_UNLESS(1 == my_process_stmt_result(stmt));
verify_prepare_field(result, 0,
- "@@max_allowed_packet", "", /* field and its org name */
+ "@@max_allowed_packet", "", /* field and its org name */
MYSQL_TYPE_LONGLONG, /* field type */
"", "", /* table and its org name */
- "", 10, 0); /* db name, length */
+ "", 10, 0); /* db name, length */
mysql_free_result(result);
mysql_stmt_close(stmt);
@@ -7093,11 +7102,10 @@ static void test_frm_bug()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= data_dir;
bind[0].buffer_length= FN_REFLEN;
- bind[0].is_null= 0;
- bind[0].length= 0;
bind[1]= bind[0];
rc= mysql_stmt_bind_result(stmt, bind);
@@ -7828,17 +7836,13 @@ static void test_fetch_seek()
stmt= mysql_simple_prepare(mysql, "select * from t1");
check_stmt(stmt);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= (void *)&c1;
- bind[0].buffer_length= 0;
- bind[0].is_null= 0;
- bind[0].length= 0;
bind[1].buffer_type= MYSQL_TYPE_STRING;
bind[1].buffer= (void *)c2;
bind[1].buffer_length= sizeof(c2);
- bind[1].is_null= 0;
- bind[1].length= 0;
bind[2]= bind[1];
bind[2].buffer= (void *)c3;
@@ -7928,6 +7932,7 @@ static void test_fetch_offset()
stmt= mysql_simple_prepare(mysql, "select * from t1");
check_stmt(stmt);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= (void *)data;
bind[0].buffer_length= 11;
@@ -8014,6 +8019,7 @@ static void test_fetch_column()
stmt= mysql_simple_prepare(mysql, "select * from t1 order by c2 desc");
check_stmt(stmt);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= (void *)&bc1;
bind[0].buffer_length= 0;
@@ -8261,10 +8267,9 @@ static void test_free_result()
stmt= mysql_simple_prepare(mysql, "select * from test_free_result");
check_stmt(stmt);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= (void *)&bc1;
- bind[0].buffer_length= 0;
- bind[0].is_null= 0;
bind[0].length= &bl1;
rc= mysql_stmt_execute(stmt);
@@ -8342,6 +8347,7 @@ static void test_free_store_result()
stmt= mysql_simple_prepare(mysql, "select * from test_free_result");
check_stmt(stmt);
+ bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= (void *)&bc1;
bind[0].buffer_length= 0;
@@ -8732,10 +8738,6 @@ static void test_bug1500()
rc= my_process_stmt_result(stmt);
DIE_UNLESS(rc == 1);
- /*
- FIXME If we comment out next string server will crash too :(
- This is another manifestation of bug #1663
- */
mysql_stmt_close(stmt);
/* This should work too */
@@ -8906,7 +8908,7 @@ static void test_subqueries()
int rc, i;
const char *query= "SELECT (SELECT SUM(a+b) FROM t2 where t1.b=t2.b GROUP BY t1.a LIMIT 1) as scalar_s, exists (select 1 from t2 where t2.a/2=t1.a) as exists_s, a in (select a+3 from t2) as in_s, (a-1, b-1) in (select a, b from t2) as in_row_s FROM t1, (select a x, b y from t2) tt WHERE x=a";
- myheader("test_subquery");
+ myheader("test_subqueries");
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2");
myquery(rc);
@@ -8957,7 +8959,7 @@ static void test_distinct()
const char *query=
"SELECT 2+count(distinct b), group_concat(a) FROM t1 group by a";
- myheader("test_subquery");
+ myheader("test_distinct");
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
myquery(rc);
@@ -9755,7 +9757,7 @@ static void test_bug3035()
{
MYSQL_STMT *stmt;
int rc;
- MYSQL_BIND bind_array[12];
+ MYSQL_BIND bind_array[12], *bind= bind_array, *bind_end= bind + 12;
int8 int8_val;
uint8 uint8_val;
int16 int16_val;
@@ -9808,6 +9810,9 @@ static void test_bug3035()
bzero(bind_array, sizeof(bind_array));
+ for (bind= bind_array; bind < bind_end; bind++)
+ bind->error= &bind->error_value;
+
bind_array[0].buffer_type= MYSQL_TYPE_TINY;
bind_array[0].buffer= (void *) &int8_val;
@@ -9913,7 +9918,15 @@ static void test_bug3035()
DIE_UNLESS(!strcmp(ulonglong_as_string, "0"));
rc= mysql_stmt_fetch(stmt);
- check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ printf("Truncation mask: ");
+ for (bind= bind_array; bind < bind_end; bind++)
+ printf("%d", (int) bind->error_value);
+ printf("\n");
+ }
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
DIE_UNLESS(int8_val == int8_max);
DIE_UNLESS(uint8_val == uint8_max);
@@ -10180,12 +10193,12 @@ static void test_union_param()
/* bind parameters */
bind[0].buffer_type= MYSQL_TYPE_STRING;
- bind[0].buffer= my_val;
+ bind[0].buffer= (char*) &my_val;
bind[0].buffer_length= 4;
bind[0].length= &my_length;
bind[0].is_null= (char*)&my_null;
bind[1].buffer_type= MYSQL_TYPE_STRING;
- bind[1].buffer= my_val;
+ bind[1].buffer= (char*) &my_val;
bind[1].buffer_length= 4;
bind[1].length= &my_length;
bind[1].is_null= (char*)&my_null;
@@ -11898,7 +11911,7 @@ static void test_datetime_ranges()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
- DIE_UNLESS(mysql_warning_count(mysql) != 2);
+ DIE_UNLESS(mysql_warning_count(mysql) == 2);
verify_col_data("t1", "day_ovfl", "838:59:59");
verify_col_data("t1", "day", "828:30:30");
@@ -12047,6 +12060,248 @@ static void test_conversion()
}
+static void test_truncation()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ int rc;
+ MYSQL_BIND *bind_array, *bind;
+
+ myheader("test_truncation");
+
+ /* Prepare the test table */
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ stmt_text= "create table t1 ("
+ "i8 tinyint, ui8 tinyint unsigned, "
+ "i16 smallint, i16_1 smallint, "
+ "ui16 smallint unsigned, i32 int, i32_1 int, "
+ "d double, d_1 double, ch char(30), ch_1 char(30), "
+ "tx text, tx_1 text, ch_2 char(30) "
+ ")";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "insert into t1 VALUES ("
+ "-10, " /* i8 */
+ "200, " /* ui8 */
+ "32000, " /* i16 */
+ "-32767, " /* i16_1 */
+ "64000, " /* ui16 */
+ "1073741824, " /* i32 */
+ "1073741825, " /* i32_1 */
+ "123.456, " /* d */
+ "-12345678910, " /* d_1 */
+ "'111111111111111111111111111111',"/* ch */
+ "'abcdef', " /* ch_1 */
+ "'12345 ', " /* tx */
+ "'12345.67 ', " /* tx_1 */
+ "'12345.67abc'" /* ch_2 */
+ ")";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "select i8 c1, i8 c2, ui8 c3, i16_1 c4, ui16 c5, "
+ " i16 c6, ui16 c7, i32 c8, i32_1 c9, i32_1 c10, "
+ " d c11, d_1 c12, d_1 c13, ch c14, ch_1 c15, tx c16, "
+ " tx_1 c17, ch_2 c18 "
+ "from t1";
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ uint bind_count= (uint) mysql_stmt_field_count(stmt);
+
+ /*************** Fill in the bind structure and bind it **************/
+ bind_array= malloc(sizeof(MYSQL_BIND) * bind_count);
+ bzero(bind_array, sizeof(MYSQL_BIND) * bind_count);
+ for (bind= bind_array; bind < bind_array + bind_count; bind++)
+ bind->error= &bind->error_value;
+ bind= bind_array;
+
+ bind->buffer= malloc(sizeof(uint8));
+ bind->buffer_type= MYSQL_TYPE_TINY;
+ bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(uint32));
+ bind->buffer_type= MYSQL_TYPE_LONG;
+ bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(int8));
+ bind->buffer_type= MYSQL_TYPE_TINY;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(uint16));
+ bind->buffer_type= MYSQL_TYPE_SHORT;
+ bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(int16));
+ bind->buffer_type= MYSQL_TYPE_SHORT;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(uint16));
+ bind->buffer_type= MYSQL_TYPE_SHORT;
+ bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(int8));
+ bind->buffer_type= MYSQL_TYPE_TINY;
+ bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(float));
+ bind->buffer_type= MYSQL_TYPE_FLOAT;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(float));
+ bind->buffer_type= MYSQL_TYPE_FLOAT;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(double));
+ bind->buffer_type= MYSQL_TYPE_DOUBLE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(longlong));
+ bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(ulonglong));
+ bind->buffer_type= MYSQL_TYPE_LONGLONG;
+ bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(longlong));
+ bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(longlong));
+ bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(longlong));
+ bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(longlong));
+ bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(double));
+ bind->buffer_type= MYSQL_TYPE_DOUBLE;
+
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ bind->buffer= malloc(sizeof(double));
+ bind->buffer_type= MYSQL_TYPE_DOUBLE;
+
+ rc= mysql_stmt_bind_result(stmt, bind_array);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+
+ /*************** Verify truncation results ***************************/
+ bind= bind_array;
+
+ /* signed tiny -> tiny */
+ DIE_UNLESS(*bind->error && * (int8*) bind->buffer == -10);
+
+ /* signed tiny -> uint32 */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error && * (int32*) bind->buffer == -10);
+
+ /* unsigned tiny -> tiny */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error && * (uint8*) bind->buffer == 200);
+
+ /* short -> ushort */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error && * (int16*) bind->buffer == -32767);
+
+ /* ushort -> short */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error && * (uint16*) bind->buffer == 64000);
+
+ /* short -> ushort (no truncation, data is in the range of target type) */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(! *bind->error && * (uint16*) bind->buffer == 32000);
+
+ /* ushort -> utiny */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error && * (int8*) bind->buffer == 0);
+
+ /* int -> float: no truncation, the number is a power of two */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(! *bind->error && * (float*) bind->buffer == 1073741824);
+
+ /* int -> float: truncation, not enough bits in float */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ /* do nothing: due to a gcc bug result here is not predictable */
+
+ /* int -> double: no truncation */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(! *bind->error && * (double*) bind->buffer == 1073741825);
+
+ /* double -> longlong: fractional part is lost */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error && * (longlong*) bind->buffer == 123);
+
+ /* double -> ulonglong, negative fp number to unsigned integer */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ /* Value in the buffer is not defined: don't test it */
+ DIE_UNLESS(*bind->error);
+
+ /* double -> longlong, negative fp number to signed integer: no loss */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(! *bind->error && * (longlong*) bind->buffer == LL(-12345678910));
+
+ /* big numeric string -> number */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error);
+
+ /* junk string -> number */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(*bind->error && *(longlong*) bind->buffer == 0);
+
+ /* string with trailing spaces -> number */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(! *bind->error && *(longlong*) bind->buffer == 12345);
+
+ /* string with trailing spaces -> double */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ DIE_UNLESS(! *bind->error && *(double*) bind->buffer == 12345.67);
+
+ /* string with trailing junk -> double */
+ DIE_UNLESS(++bind < bind_array + bind_count);
+ /*
+ XXX: There must be a truncation error: but it's not the way the server
+ behaves, so let's leave it for now.
+ */
+ DIE_UNLESS(*(double*) bind->buffer == 12345.67);
+ /*
+ TODO: string -> double, double -> time, double -> string (truncation
+ errors are not supported here yet)
+ longlong -> time/date/datetime
+ date -> time, date -> timestamp, date -> number
+ time -> string, time -> date, time -> timestamp,
+ number -> date string -> date
+ */
+ /*************** Cleanup *********************************************/
+
+ mysql_stmt_close(stmt);
+
+ for (bind= bind_array; bind < bind_array + bind_count; bind++)
+ free(bind->buffer);
+ free(bind_array);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
/*
Read and parse arguments and MySQL options from my.cnf
*/
@@ -12260,6 +12515,7 @@ static struct my_tests_st my_tests[]= {
{ "test_view_insert_fields", test_view_insert_fields },
{ "test_basic_cursors", test_basic_cursors },
{ "test_cursors_with_union", test_cursors_with_union },
+ { "test_truncation", test_truncation },
{ 0, 0 }
};