Data truncation reporting implementation (libmysql) + post review
fixes. Still to do: - deploy my_strtoll10 in limbysql.c - add mysql_options option to switch MYSQL_DATA_TRUNCATED on and off.
This commit is contained in:
parent
24aa7e9ce7
commit
7216594f4f
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -3941,6 +4122,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= ¶m->internal_is_null;
|
||||
param->is_null= ¶m->is_null_value;
|
||||
|
||||
if (!param->length)
|
||||
param->length= ¶m->internal_length;
|
||||
param->length= ¶m->length_value;
|
||||
|
||||
if (!param->error)
|
||||
param->error= ¶m->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:
|
||||
if (setup_one_fetch_function(param, field))
|
||||
{
|
||||
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:
|
||||
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= ¶m->internal_length; /* Needed for fetch_result() */
|
||||
bind->length= ¶m->length_value; /* Needed for fetch_result() */
|
||||
fetch_result_with_conversion(bind, field, &row);
|
||||
}
|
||||
else
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
28
sql/field.cc
28
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, ¬_used, 1, &error);
|
||||
nr= number_to_datetime(nr, ¬_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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
162
sql/time.cc
162
sql/time.cc
@ -262,95 +262,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
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user