Commit 27eda712 authored by unknown's avatar unknown

Proposed fix for Bug#4026 "Microseconds part of TIME/DATETIME types

is broken (prepared statements)": fixed date handling in many places 
of prepared statements code.


libmysql/libmysql.c:
  Fix for Bug#4026:
  - now buffer_length is defined for any buffer type. Network buffer 
    preallocation cleaned up.
  - added constants for maximum buffer sizes necessary for MYSQL_TYPE_DATE,
    MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME types.
  - TIME/DATETIME packing/unpacking functions fixed 
  - now result set metadata is always updated from fields sent to COM_EXECUTE.
    This is necessary to make 'SELECT ?' queries work without conversions.
sql/item.cc:
  - added implementatoin of Item_param::get_date
sql/item.h:
  - added enum_field_types Item_param::param_type. First step for proper
    handling of placeholders.
  - added get_date() implementation to prevent date -> string -> date 
    conversions when MYSQL_TYPE_DATE/DATETIME parameter is used in temporal 
    context.
sql/protocol.cc:
  Fix for Bug#4026:
  - PACKET_BUFFET_EXTRA_ALLOC -> PACKET_BUFFER_EXTRA_ALLOC.
    The define itself was moved to .cc as it's used only in protocol.cc
  - fixed Protocol_prep::store_time() call.
sql/protocol.h:
  - PACKET_BUFFER_EXTRA_ALLOC moved to protocol.cc
sql/sql_prepare.cc:
  Fix for Bug#4026:
  - MYSQL_TYPE_TIME/DATETIME handling fixed.
  - added initialization for Item_param::param_type in 
    setup_one_conversion_function
tests/client_test.c:
  Test case for Bug#4026
parent 5cc410bb
...@@ -1667,6 +1667,27 @@ static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row); ...@@ -1667,6 +1667,27 @@ static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row);
static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row);
/*
Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME
values stored in network buffer.
*/
/* 1 (length) + 2 (year) + 1 (month) + 1 (day) */
static const unsigned MAX_DATE_REP_LENGTH= 5;
/*
1 (length) + 1 (is negative) + 4 (day count) + 1 (hour)
+ 1 (minute) + 1 (seconds) + 4 (microseconds)
*/
static const unsigned MAX_TIME_REP_LENGTH= 13;
/*
1 (length) + 2 (year) + 1 (month) + 1 (day) +
1 (hour) + 1 (minute) + 1 (second) + 4 (microseconds)
*/
static const unsigned MAX_DATETIME_REP_LENGTH= 12;
/**************** Misc utility functions ****************************/ /**************** Misc utility functions ****************************/
/* /*
...@@ -2030,6 +2051,30 @@ static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) ...@@ -2030,6 +2051,30 @@ static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt)
return stmt->field_count; return stmt->field_count;
} }
/*
Update result set columns metadata if it was sent again in
reply to COM_EXECUTE.
*/
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;
DBUG_ASSERT(stmt->field_count == stmt->mysql->field_count);
for (; field < field_end; ++field, ++stmt_field)
{
stmt_field->charsetnr= field->charsetnr;
stmt_field->length = field->length;
stmt_field->type = field->type;
stmt_field->flags = field->flags;
stmt_field->decimals = field->decimals;
}
}
/* /*
Returns prepared statement metadata in the form of a result set. Returns prepared statement metadata in the form of a result set.
...@@ -2166,7 +2211,7 @@ static void store_param_double(NET *net, MYSQL_BIND *param) ...@@ -2166,7 +2211,7 @@ static void store_param_double(NET *net, MYSQL_BIND *param)
static void store_param_time(NET *net, MYSQL_BIND *param) static void store_param_time(NET *net, MYSQL_BIND *param)
{ {
MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer; MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer;
char buff[15], *pos; char buff[MAX_TIME_REP_LENGTH], *pos;
uint length; uint length;
pos= buff+1; pos= buff+1;
...@@ -2177,7 +2222,7 @@ static void store_param_time(NET *net, MYSQL_BIND *param) ...@@ -2177,7 +2222,7 @@ static void store_param_time(NET *net, MYSQL_BIND *param)
pos[7]= (uchar) tm->second; pos[7]= (uchar) tm->second;
int4store(pos+8, tm->second_part); int4store(pos+8, tm->second_part);
if (tm->second_part) if (tm->second_part)
length= 11; length= 12;
else if (tm->hour || tm->minute || tm->second || tm->day) else if (tm->hour || tm->minute || tm->second || tm->day)
length= 8; length= 8;
else else
...@@ -2189,7 +2234,7 @@ static void store_param_time(NET *net, MYSQL_BIND *param) ...@@ -2189,7 +2234,7 @@ static void store_param_time(NET *net, MYSQL_BIND *param)
static void net_store_datetime(NET *net, MYSQL_TIME *tm) static void net_store_datetime(NET *net, MYSQL_TIME *tm)
{ {
char buff[12], *pos; char buff[MAX_DATETIME_REP_LENGTH], *pos;
uint length; uint length;
pos= buff+1; pos= buff+1;
...@@ -2280,7 +2325,7 @@ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) ...@@ -2280,7 +2325,7 @@ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param)
Param->length should ALWAYS point to the correct length for the type Param->length should ALWAYS point to the correct length for the type
Either to the length pointer given by the user or param->buffer_length Either to the length pointer given by the user or param->buffer_length
*/ */
if ((my_realloc_str(net, 9 + *param->length))) if ((my_realloc_str(net, *param->length)))
{ {
set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -2557,16 +2602,37 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) ...@@ -2557,16 +2602,37 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
*/ */
if (mysql->methods->stmt_execute(stmt)) if (mysql->methods->stmt_execute(stmt))
DBUG_RETURN(1); DBUG_RETURN(1);
if (!stmt->field_count && mysql->field_count) if (mysql->field_count)
{
/* Server has sent result set metadata */
if (stmt->field_count == 0)
{ {
/* /*
This is 'SHOW'/'EXPLAIN'-like query. Current implementation of This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
prepared statements can't send result set metadata for this queries prepared statements can't send result set metadata for these queries
on prepare stage. Read it now. on prepare stage. Read it now.
*/ */
alloc_stmt_fields(stmt); alloc_stmt_fields(stmt);
} }
else
{
/*
Update result set metadata if it for some reason changed between
prepare and execute, i.e.:
- in case of 'SELECT ?' we don't know column type unless data was
supplied to mysql_stmt_execute, so updated column type is sent
now.
- if data dictionary changed between prepare and execute, for
example a table used in the query was altered.
Note, that now (4.1.3) we always send metadata in reply to
COM_EXECUTE (even if it is not necessary), so either this or
previous always branch works.
TODO: send metadata only when it's really necessary and add a warning
'Metadata changed' when it's sent twice.
*/
update_stmt_fields(stmt);
}
}
stmt->state= MYSQL_STMT_EXECUTE_DONE; stmt->state= MYSQL_STMT_EXECUTE_DONE;
if (stmt->field_count) if (stmt->field_count)
{ {
...@@ -2693,15 +2759,17 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) ...@@ -2693,15 +2759,17 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
param->store_param_func= store_param_double; param->store_param_func= store_param_double;
break; break;
case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME:
/* Buffer length ignored for DATE, TIME and DATETIME */
param->store_param_func= store_param_time; param->store_param_func= store_param_time;
param->buffer_length= MAX_TIME_REP_LENGTH;
break; break;
case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATE:
param->store_param_func= store_param_date; param->store_param_func= store_param_date;
param->buffer_length= MAX_DATE_REP_LENGTH;
break; break;
case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIMESTAMP:
param->store_param_func= store_param_datetime; param->store_param_func= store_param_datetime;
param->buffer_length= MAX_DATETIME_REP_LENGTH;
break; break;
case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_MEDIUM_BLOB:
...@@ -2861,15 +2929,15 @@ static uint read_binary_time(MYSQL_TIME *tm, uchar **pos) ...@@ -2861,15 +2929,15 @@ static uint read_binary_time(MYSQL_TIME *tm, uchar **pos)
} }
to= *pos; to= *pos;
tm->second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; tm->neg= (bool) to[0];
tm->day= (ulong) sint4korr(to+1); tm->day= (ulong) sint4korr(to+1);
tm->hour= (uint) to[5]; tm->hour= (uint) to[5];
tm->minute= (uint) to[6]; tm->minute= (uint) to[6];
tm->second= (uint) to[7]; tm->second= (uint) to[7];
tm->second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0;
tm->year= tm->month= 0; tm->year= tm->month= 0;
tm->neg= (bool)to[0];
return length; return length;
} }
...@@ -2886,7 +2954,11 @@ static uint read_binary_datetime(MYSQL_TIME *tm, uchar **pos) ...@@ -2886,7 +2954,11 @@ static uint read_binary_datetime(MYSQL_TIME *tm, uchar **pos)
} }
to= *pos; to= *pos;
tm->second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
tm->neg= 0;
tm->year= (uint) sint2korr(to);
tm->month= (uint) to[2];
tm->day= (uint) to[3];
if (length > 4) if (length > 4)
{ {
...@@ -2896,11 +2968,7 @@ static uint read_binary_datetime(MYSQL_TIME *tm, uchar **pos) ...@@ -2896,11 +2968,7 @@ static uint read_binary_datetime(MYSQL_TIME *tm, uchar **pos)
} }
else else
tm->hour= tm->minute= tm->second= 0; tm->hour= tm->minute= tm->second= 0;
tm->second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0;
tm->year= (uint) sint2korr(to);
tm->month= (uint) to[2];
tm->day= (uint) to[3];
tm->neg= 0;
return length; return length;
} }
......
...@@ -629,6 +629,7 @@ Item_param::Item_param(unsigned pos_in_query_arg) : ...@@ -629,6 +629,7 @@ Item_param::Item_param(unsigned pos_in_query_arg) :
state(NO_VALUE), state(NO_VALUE),
item_result_type(STRING_RESULT), item_result_type(STRING_RESULT),
item_type(STRING_ITEM), item_type(STRING_ITEM),
param_type(MYSQL_TYPE_STRING),
pos_in_query(pos_in_query_arg), pos_in_query(pos_in_query_arg),
set_param_func(default_set_param_func) set_param_func(default_set_param_func)
{ {
...@@ -808,6 +809,17 @@ bool Item_param::get_time(TIME *res) ...@@ -808,6 +809,17 @@ bool Item_param::get_time(TIME *res)
} }
bool Item_param::get_date(TIME *res, uint fuzzydate)
{
if (state == TIME_VALUE)
{
*res= value.time;
return 0;
}
return Item::get_date(res, fuzzydate);
}
double Item_param::val() double Item_param::val()
{ {
switch (state) { switch (state) {
......
...@@ -465,6 +465,16 @@ public: ...@@ -465,6 +465,16 @@ public:
/* Cached values for virtual methods to save us one switch. */ /* Cached values for virtual methods to save us one switch. */
enum Item_result item_result_type; enum Item_result item_result_type;
enum Type item_type; enum Type item_type;
/*
Used when this item is used in a temporary table.
This is NOT placeholder metadata sent to client, as this value
is assigned after sending metadata (in setup_one_conversion_function).
For example in case of 'SELECT ?' you'll get MYSQL_TYPE_STRING both
in result set and placeholders metadata, no matter what type you will
supply for this placeholder in mysql_stmt_execute.
*/
enum enum_field_types param_type;
/* /*
Offset of placeholder inside statement text. Used to create Offset of placeholder inside statement text. Used to create
no-placeholders version of this statement for the binary log. no-placeholders version of this statement for the binary log.
...@@ -475,12 +485,13 @@ public: ...@@ -475,12 +485,13 @@ public:
enum Item_result result_type () const { return item_result_type; } enum Item_result result_type () const { return item_result_type; }
enum Type type() const { return item_type; } enum Type type() const { return item_type; }
enum_field_types field_type() const { return MYSQL_TYPE_STRING; } enum_field_types field_type() const { return param_type; }
double val(); double val();
longlong val_int(); longlong val_int();
String *val_str(String*); String *val_str(String*);
bool get_time(TIME *tm); bool get_time(TIME *tm);
bool get_date(TIME *tm, uint fuzzydate);
int save_in_field(Field *field, bool no_conversions); int save_in_field(Field *field, bool no_conversions);
void set_null(); void set_null();
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include "mysql_priv.h" #include "mysql_priv.h"
#include <stdarg.h> #include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
bool Protocol::net_store_data(const char *from, uint length) bool Protocol::net_store_data(const char *from, uint length)
#else #else
...@@ -687,7 +689,7 @@ bool Protocol_simple::store_null() ...@@ -687,7 +689,7 @@ bool Protocol_simple::store_null()
#endif #endif
char buff[1]; char buff[1];
buff[0]= (char)251; buff[0]= (char)251;
return packet->append(buff, sizeof(buff), PACKET_BUFFET_EXTRA_ALLOC); return packet->append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
} }
#endif #endif
...@@ -990,7 +992,7 @@ bool Protocol_prep::store_tiny(longlong from) ...@@ -990,7 +992,7 @@ bool Protocol_prep::store_tiny(longlong from)
char buff[1]; char buff[1];
field_pos++; field_pos++;
buff[0]= (uchar) from; buff[0]= (uchar) from;
return packet->append(buff, sizeof(buff), PACKET_BUFFET_EXTRA_ALLOC); return packet->append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
} }
...@@ -1002,7 +1004,7 @@ bool Protocol_prep::store_short(longlong from) ...@@ -1002,7 +1004,7 @@ bool Protocol_prep::store_short(longlong from)
field_types[field_pos] == MYSQL_TYPE_YEAR); field_types[field_pos] == MYSQL_TYPE_YEAR);
#endif #endif
field_pos++; field_pos++;
char *to= packet->prep_append(2, PACKET_BUFFET_EXTRA_ALLOC); char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC);
if (!to) if (!to)
return 1; return 1;
int2store(to, (int) from); int2store(to, (int) from);
...@@ -1018,7 +1020,7 @@ bool Protocol_prep::store_long(longlong from) ...@@ -1018,7 +1020,7 @@ bool Protocol_prep::store_long(longlong from)
field_types[field_pos] == MYSQL_TYPE_LONG); field_types[field_pos] == MYSQL_TYPE_LONG);
#endif #endif
field_pos++; field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFET_EXTRA_ALLOC); char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
if (!to) if (!to)
return 1; return 1;
int4store(to, from); int4store(to, from);
...@@ -1033,7 +1035,7 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag) ...@@ -1033,7 +1035,7 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
field_types[field_pos] == MYSQL_TYPE_LONGLONG); field_types[field_pos] == MYSQL_TYPE_LONGLONG);
#endif #endif
field_pos++; field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFET_EXTRA_ALLOC); char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
if (!to) if (!to)
return 1; return 1;
int8store(to, from); int8store(to, from);
...@@ -1048,7 +1050,7 @@ bool Protocol_prep::store(float from, uint32 decimals, String *buffer) ...@@ -1048,7 +1050,7 @@ bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
field_types[field_pos] == MYSQL_TYPE_FLOAT); field_types[field_pos] == MYSQL_TYPE_FLOAT);
#endif #endif
field_pos++; field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFET_EXTRA_ALLOC); char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
if (!to) if (!to)
return 1; return 1;
float4store(to, from); float4store(to, from);
...@@ -1063,7 +1065,7 @@ bool Protocol_prep::store(double from, uint32 decimals, String *buffer) ...@@ -1063,7 +1065,7 @@ bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
field_types[field_pos] == MYSQL_TYPE_DOUBLE); field_types[field_pos] == MYSQL_TYPE_DOUBLE);
#endif #endif
field_pos++; field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFET_EXTRA_ALLOC); char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
if (!to) if (!to)
return 1; return 1;
float8store(to, from); float8store(to, from);
...@@ -1112,7 +1114,7 @@ bool Protocol_prep::store(TIME *tm) ...@@ -1112,7 +1114,7 @@ bool Protocol_prep::store(TIME *tm)
else else
length=0; length=0;
buff[0]=(char) length; // Length is stored first buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFET_EXTRA_ALLOC); return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
} }
bool Protocol_prep::store_date(TIME *tm) bool Protocol_prep::store_date(TIME *tm)
...@@ -1129,7 +1131,7 @@ bool Protocol_prep::store_time(TIME *tm) ...@@ -1129,7 +1131,7 @@ bool Protocol_prep::store_time(TIME *tm)
DBUG_ASSERT(field_types == 0 || DBUG_ASSERT(field_types == 0 ||
field_types[field_pos] == MYSQL_TYPE_TIME); field_types[field_pos] == MYSQL_TYPE_TIME);
#endif #endif
char buff[15],*pos; char buff[13], *pos;
uint length; uint length;
field_pos++; field_pos++;
pos= buff+1; pos= buff+1;
...@@ -1140,13 +1142,13 @@ bool Protocol_prep::store_time(TIME *tm) ...@@ -1140,13 +1142,13 @@ bool Protocol_prep::store_time(TIME *tm)
pos[7]= (uchar) tm->second; pos[7]= (uchar) tm->second;
int4store(pos+8, tm->second_part); int4store(pos+8, tm->second_part);
if (tm->second_part) if (tm->second_part)
length=11; length=12;
else if (tm->hour || tm->minute || tm->second || tm->day) else if (tm->hour || tm->minute || tm->second || tm->day)
length=8; length=8;
else else
length=0; length=0;
buff[0]=(char) length; // Length is stored first buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFET_EXTRA_ALLOC); return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
} }
#ifdef EMBEDDED_LIBRARY #ifdef EMBEDDED_LIBRARY
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#pragma interface /* gcc class implementation */ #pragma interface /* gcc class implementation */
#endif #endif
#define PACKET_BUFFET_EXTRA_ALLOC 1024
class i_string; class i_string;
class THD; class THD;
......
...@@ -333,19 +333,18 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len) ...@@ -333,19 +333,18 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
uchar *to= *pos; uchar *to= *pos;
TIME tm; TIME tm;
/* TODO: why length is compared with 8 here? */ tm.neg= (bool) to[0];
tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; day= (uint) sint4korr(to+1);
/* /*
Note, that though ranges of hour, minute and second are not checked Note, that though ranges of hour, minute and second are not checked
here we rely on them being < 256: otherwise here we rely on them being < 256: otherwise
we'll get buffer overflow in make_{date,time} functions, we'll get buffer overflow in make_{date,time} functions,
which are called when time value is converted to string. which are called when time value is converted to string.
*/ */
day= (uint) sint4korr(to+1);
tm.hour= (uint) to[5] + day * 24; tm.hour= (uint) to[5] + day * 24;
tm.minute= (uint) to[6]; tm.minute= (uint) to[6];
tm.second= (uint) to[7]; tm.second= (uint) to[7];
tm.second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0;
if (tm.hour > 838) if (tm.hour > 838)
{ {
/* TODO: add warning 'Data truncated' here */ /* TODO: add warning 'Data truncated' here */
...@@ -354,7 +353,6 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len) ...@@ -354,7 +353,6 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
tm.second= 59; tm.second= 59;
} }
tm.day= tm.year= tm.month= 0; tm.day= tm.year= tm.month= 0;
tm.neg= (bool)to[0];
param->set_time(&tm, TIMESTAMP_TIME, param->set_time(&tm, TIMESTAMP_TIME,
MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
...@@ -371,8 +369,10 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len) ...@@ -371,8 +369,10 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
uchar *to= *pos; uchar *to= *pos;
TIME tm; TIME tm;
tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0; tm.neg= 0;
tm.year= (uint) sint2korr(to);
tm.month= (uint) to[2];
tm.day= (uint) to[3];
/* /*
Note, that though ranges of hour, minute and second are not checked Note, that though ranges of hour, minute and second are not checked
here we rely on them being < 256: otherwise here we rely on them being < 256: otherwise
...@@ -387,10 +387,7 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len) ...@@ -387,10 +387,7 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
else else
tm.hour= tm.minute= tm.second= 0; tm.hour= tm.minute= tm.second= 0;
tm.year= (uint) sint2korr(to); tm.second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0;
tm.month= (uint) to[2];
tm.day= (uint) to[3];
tm.neg= 0;
param->set_time(&tm, TIMESTAMP_DATETIME, param->set_time(&tm, TIMESTAMP_DATETIME,
MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
...@@ -585,6 +582,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, ...@@ -585,6 +582,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
param->item_result_type= STRING_RESULT; param->item_result_type= STRING_RESULT;
} }
} }
param->param_type= (enum enum_field_types) param_type;
} }
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
......
...@@ -9802,6 +9802,73 @@ static void test_bug3796() ...@@ -9802,6 +9802,73 @@ static void test_bug3796()
myquery(rc); myquery(rc);
} }
static void test_bug4026()
{
MYSQL_STMT *stmt;
MYSQL_BIND bind[2];
MYSQL_TIME time_in, time_out;
MYSQL_TIME datetime_in, datetime_out;
const char *stmt_text;
int rc;
myheader("test_bug4026");
/* Check that microseconds are inserted and selected successfully */
/* Create a statement handle and prepare it with select */
stmt= mysql_stmt_init(mysql);
stmt_text= "SELECT ?, ?";
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
check_execute(stmt, rc);
/* Bind input buffers */
bzero(bind, sizeof(bind));
bzero(&time_in, sizeof(time_in));
bzero(&time_out, sizeof(time_out));
bzero(&datetime_in, sizeof(datetime_in));
bzero(&datetime_out, sizeof(datetime_out));
bind[0].buffer_type= MYSQL_TYPE_TIME;
bind[0].buffer= (char*) &time_in;
bind[1].buffer_type= MYSQL_TYPE_DATETIME;
bind[1].buffer= (char*) &datetime_in;
time_in.hour= 23;
time_in.minute= 59;
time_in.second= 59;
time_in.second_part= 123456;
datetime_in= time_in;
datetime_in.year= 2003;
datetime_in.month= 12;
datetime_in.day= 31;
mysql_stmt_bind_param(stmt, bind);
/* Execute the select statement */
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
bind[0].buffer= (char*) &time_out;
bind[1].buffer= (char*) &datetime_out;
mysql_stmt_bind_result(stmt, bind);
rc= mysql_stmt_fetch(stmt);
assert(rc == 0);
printf("%d:%d:%d.%lu\n", time_out.hour, time_out.minute, time_out.second,
time_out.second_part);
printf("%d-%d-%d %d:%d:%d.%lu\n", datetime_out.year, datetime_out.month,
datetime_out.day, datetime_out.hour,
datetime_out.minute, datetime_out.second,
datetime_out.second_part);
assert(memcmp(&time_in, &time_out, sizeof(time_in)) == 0);
assert(memcmp(&datetime_in, &datetime_out, sizeof(datetime_in)) == 0);
mysql_stmt_close(stmt);
}
/* /*
Read and parse arguments and MySQL options from my.cnf Read and parse arguments and MySQL options from my.cnf
*/ */
...@@ -10094,6 +10161,7 @@ int main(int argc, char **argv) ...@@ -10094,6 +10161,7 @@ int main(int argc, char **argv)
(Bug #3686 */ (Bug #3686 */
test_ps_i18n(); /* test for i18n support in binary protocol */ test_ps_i18n(); /* test for i18n support in binary protocol */
test_bug3796(); /* test for select concat(?, <string>) */ test_bug3796(); /* test for select concat(?, <string>) */
test_bug4026(); /* test microseconds precision of time types */
/* /*
XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH. DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment