Support for character set conversion in binary protocol: another go
after Monty's review. - Item_param was rewritten. - it turns out that we can't convert string data to character set of connection on the fly, because they first should be written to the binary log. To support efficient conversion we need to rewrite prepared statements binlogging code first.
This commit is contained in:
parent
1b61cb6d0f
commit
f207b33a7b
@ -382,7 +382,7 @@ typedef unsigned short ushort;
|
||||
|
||||
#define CMP_NUM(a,b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1)
|
||||
#define sgn(a) (((a) < 0) ? -1 : ((a) > 0) ? 1 : 0)
|
||||
#define swap(t,a,b) { register t dummy; dummy = a; a = b; b = dummy; }
|
||||
#define swap_variables(t, a, b) { register t dummy; dummy= a; a= b; b= dummy; }
|
||||
#define test(a) ((a) ? 1 : 0)
|
||||
#define set_if_bigger(a,b) do { if ((a) < (b)) (a)=(b); } while(0)
|
||||
#define set_if_smaller(a,b) do { if ((a) > (b)) (a)=(b); } while(0)
|
||||
|
@ -762,6 +762,8 @@ extern char *get_charsets_dir(char *buf);
|
||||
extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
|
||||
extern my_bool init_compiled_charsets(myf flags);
|
||||
extern void add_compiled_collation(CHARSET_INFO *cs);
|
||||
extern ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to,
|
||||
const char *from, ulong length);
|
||||
|
||||
#ifdef __WIN__
|
||||
extern my_bool have_tcpip; /* Is set if tcpip is used */
|
||||
|
@ -359,6 +359,6 @@ char *net_store_length(char *pkg, ulonglong length);
|
||||
|
||||
#define NULL_LENGTH ((unsigned long) ~0) /* For net_store_length */
|
||||
#define MYSQL_STMT_HEADER 4
|
||||
#define MYSQL_LONG_DATA_HEADER 6
|
||||
#define MYSQL_LONG_DATA_HEADER 6
|
||||
|
||||
#endif
|
||||
|
@ -489,7 +489,7 @@ int _nisam_key_cmp(register N_KEYSEG *keyseg, register uchar *a, register uchar
|
||||
int alength,blength;
|
||||
|
||||
if (swap_flag)
|
||||
swap(uchar*,a,b);
|
||||
swap_variables(uchar*, a, b);
|
||||
alength= *a++; blength= *b++;
|
||||
if ((flag=(int) (keyseg->base.length-key_length)) < 0)
|
||||
flag=0;
|
||||
@ -504,7 +504,7 @@ int _nisam_key_cmp(register N_KEYSEG *keyseg, register uchar *a, register uchar
|
||||
if (*a == '-' && *b == '-')
|
||||
{
|
||||
swap_flag=1;
|
||||
swap(uchar*,a,b);
|
||||
swap_variables(uchar*, a, b);
|
||||
}
|
||||
end=a+alength;
|
||||
while (a < end)
|
||||
@ -531,7 +531,7 @@ int _nisam_key_cmp(register N_KEYSEG *keyseg, register uchar *a, register uchar
|
||||
if (swap_flag)
|
||||
{
|
||||
end=b+(int) (end-a);
|
||||
swap(uchar*,a,b);
|
||||
swap_variables(uchar*, a, b);
|
||||
}
|
||||
while (a < end)
|
||||
if (*a++ != *b++)
|
||||
@ -550,7 +550,7 @@ int _nisam_key_cmp(register N_KEYSEG *keyseg, register uchar *a, register uchar
|
||||
}
|
||||
}
|
||||
if (swap_flag)
|
||||
swap(uchar*,a,b);
|
||||
swap_variables(uchar*, a, b);
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_LONG_LONG
|
||||
|
@ -526,7 +526,7 @@ int main(int argc, char *argv[])
|
||||
if (j != 0 && k != 0)
|
||||
{
|
||||
if (j > k)
|
||||
swap(int,j,k);
|
||||
swap_variables(int, j, k);
|
||||
sprintf(key,"%6d",j);
|
||||
sprintf(key2,"%6d",k);
|
||||
range_records=nisam_records_in_range(file,0,key,0,HA_READ_AFTER_KEY,
|
||||
|
@ -88,8 +88,6 @@ my_bool net_flush(NET *net);
|
||||
static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data);
|
||||
static void append_wild(char *to,char *end,const char *wild);
|
||||
sig_handler pipe_sig_handler(int sig);
|
||||
static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
|
||||
const char *from, ulong length);
|
||||
|
||||
static my_bool mysql_client_init= 0;
|
||||
static my_bool org_my_init_done= 0;
|
||||
@ -1570,73 +1568,14 @@ void my_net_local_init(NET *net)
|
||||
ulong STDCALL
|
||||
mysql_escape_string(char *to,const char *from,ulong length)
|
||||
{
|
||||
return mysql_sub_escape_string(default_charset_info,to,from,length);
|
||||
return escape_string_for_mysql(default_charset_info, to, from, length);
|
||||
}
|
||||
|
||||
ulong STDCALL
|
||||
mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
|
||||
ulong length)
|
||||
{
|
||||
return mysql_sub_escape_string(mysql->charset,to,from,length);
|
||||
}
|
||||
|
||||
|
||||
static ulong
|
||||
mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
|
||||
const char *from, ulong length)
|
||||
{
|
||||
const char *to_start=to;
|
||||
const char *end;
|
||||
#ifdef USE_MB
|
||||
my_bool use_mb_flag=use_mb(charset_info);
|
||||
#endif
|
||||
for (end=from+length; from != end ; from++)
|
||||
{
|
||||
#ifdef USE_MB
|
||||
int l;
|
||||
if (use_mb_flag && (l = my_ismbchar(charset_info, from, end)))
|
||||
{
|
||||
while (l--)
|
||||
*to++ = *from++;
|
||||
from--;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
switch (*from) {
|
||||
case 0: /* Must be escaped for 'mysql' */
|
||||
*to++= '\\';
|
||||
*to++= '0';
|
||||
break;
|
||||
case '\n': /* Must be escaped for logs */
|
||||
*to++= '\\';
|
||||
*to++= 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*to++= '\\';
|
||||
*to++= 'r';
|
||||
break;
|
||||
case '\\':
|
||||
*to++= '\\';
|
||||
*to++= '\\';
|
||||
break;
|
||||
case '\'':
|
||||
*to++= '\\';
|
||||
*to++= '\'';
|
||||
break;
|
||||
case '"': /* Better safe than sorry */
|
||||
*to++= '\\';
|
||||
*to++= '"';
|
||||
break;
|
||||
case '\032': /* This gives problems on Win32 */
|
||||
*to++= '\\';
|
||||
*to++= 'Z';
|
||||
break;
|
||||
default:
|
||||
*to++= *from;
|
||||
}
|
||||
}
|
||||
*to=0;
|
||||
return (ulong) (to-to_start);
|
||||
return escape_string_for_mysql(mysql->charset, to, from, length);
|
||||
}
|
||||
|
||||
|
||||
@ -1978,7 +1917,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
|
||||
if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
|
||||
{
|
||||
/* This is second prepare with another statement */
|
||||
char buff[4];
|
||||
char buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */
|
||||
|
||||
mysql_stmt_free_result(stmt);
|
||||
/*
|
||||
@ -2353,6 +2292,14 @@ static my_bool execute(MYSQL_STMT * stmt, char *packet, ulong length)
|
||||
stmt->insert_id= mysql->insert_id;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
static void store_param_type(char **pos, MYSQL_BIND *param)
|
||||
{
|
||||
uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0);
|
||||
int2store(*pos, typecode);
|
||||
*pos+= 2;
|
||||
}
|
||||
|
||||
|
||||
int cli_stmt_execute(MYSQL_STMT *stmt)
|
||||
@ -2390,11 +2337,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
|
||||
that is sent to the server.
|
||||
*/
|
||||
for (param= stmt->params; param < param_end ; param++)
|
||||
{
|
||||
uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0);
|
||||
int2store(net->write_pos, typecode);
|
||||
net->write_pos+= 2;
|
||||
}
|
||||
store_param_type((char**) &net->write_pos, param);
|
||||
}
|
||||
|
||||
for (param= stmt->params; param < param_end; param++)
|
||||
@ -2804,25 +2747,27 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
|
||||
param->param_number);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
/* Mark for execute that the result is already sent */
|
||||
|
||||
/*
|
||||
Send long data packet if there is data or we're sending long data
|
||||
for the first time.
|
||||
*/
|
||||
if (length || param->long_data_used == 0)
|
||||
{
|
||||
MYSQL *mysql= stmt->mysql;
|
||||
char *packet, extra_data[MYSQL_LONG_DATA_HEADER];
|
||||
|
||||
/* Packet header: stmt id (4 bytes), param no (2 bytes) */
|
||||
char buff[MYSQL_LONG_DATA_HEADER];
|
||||
|
||||
int4store(buff, stmt->stmt_id);
|
||||
int2store(buff + 4, param_number);
|
||||
param->long_data_used= 1;
|
||||
|
||||
packet= extra_data;
|
||||
int4store(packet, stmt->stmt_id); packet+=4;
|
||||
int2store(packet, param_number); packet+=2;
|
||||
|
||||
/*
|
||||
Note that we don't get any ok packet from the server in this case
|
||||
This is intentional to save bandwidth.
|
||||
*/
|
||||
if ((*mysql->methods->advanced_command)(mysql, COM_LONG_DATA, extra_data,
|
||||
MYSQL_LONG_DATA_HEADER, data,
|
||||
length, 1))
|
||||
if ((*mysql->methods->advanced_command)(mysql, COM_LONG_DATA, buff,
|
||||
sizeof(buff), data, length, 1))
|
||||
{
|
||||
set_stmt_errmsg(stmt, mysql->net.last_error,
|
||||
mysql->net.last_errno, mysql->net.sqlstate);
|
||||
@ -3962,7 +3907,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
|
||||
mysql->stmts= list_delete(mysql->stmts, &stmt->list);
|
||||
if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
|
||||
{
|
||||
char buff[4];
|
||||
char buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */
|
||||
|
||||
if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
|
||||
mysql->unbuffered_fetch_owner= 0;
|
||||
@ -3997,7 +3942,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
|
||||
|
||||
my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
|
||||
{
|
||||
char buff[MYSQL_STMT_HEADER];
|
||||
char buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */
|
||||
MYSQL *mysql;
|
||||
MYSQL_BIND *param, *param_end;
|
||||
DBUG_ENTER("mysql_stmt_reset");
|
||||
@ -4010,7 +3955,7 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
|
||||
mysql= stmt->mysql->last_used_con;
|
||||
int4store(buff, stmt->stmt_id); /* Send stmt id to server */
|
||||
if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff,
|
||||
MYSQL_STMT_HEADER,0,0,0))
|
||||
sizeof(buff), 0, 0, 0))
|
||||
{
|
||||
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
|
||||
mysql->net.sqlstate);
|
||||
|
@ -643,7 +643,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
key_range min_key, max_key;
|
||||
if (j > k)
|
||||
swap(int,j,k);
|
||||
swap_variables(int, j, k);
|
||||
sprintf(key,"%6d",j);
|
||||
sprintf(key2,"%6d",k);
|
||||
|
||||
|
@ -622,3 +622,61 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name,
|
||||
|
||||
DBUG_RETURN(cs);
|
||||
}
|
||||
|
||||
|
||||
ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to,
|
||||
const char *from, ulong length)
|
||||
{
|
||||
const char *to_start= to;
|
||||
const char *end;
|
||||
#ifdef USE_MB
|
||||
my_bool use_mb_flag= use_mb(charset_info);
|
||||
#endif
|
||||
for (end= from + length; from != end; from++)
|
||||
{
|
||||
#ifdef USE_MB
|
||||
int l;
|
||||
if (use_mb_flag && (l= my_ismbchar(charset_info, from, end)))
|
||||
{
|
||||
while (l--)
|
||||
*to++= *from++;
|
||||
from--;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
switch (*from) {
|
||||
case 0: /* Must be escaped for 'mysql' */
|
||||
*to++= '\\';
|
||||
*to++= '0';
|
||||
break;
|
||||
case '\n': /* Must be escaped for logs */
|
||||
*to++= '\\';
|
||||
*to++= 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*to++= '\\';
|
||||
*to++= 'r';
|
||||
break;
|
||||
case '\\':
|
||||
*to++= '\\';
|
||||
*to++= '\\';
|
||||
break;
|
||||
case '\'':
|
||||
*to++= '\\';
|
||||
*to++= '\'';
|
||||
break;
|
||||
case '"': /* Better safe than sorry */
|
||||
*to++= '\\';
|
||||
*to++= '"';
|
||||
break;
|
||||
case '\032': /* This gives problems on Win32 */
|
||||
*to++= '\\';
|
||||
*to++= 'Z';
|
||||
break;
|
||||
default:
|
||||
*to++= *from;
|
||||
}
|
||||
}
|
||||
*to= 0;
|
||||
return (ulong) (to - to_start);
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags)
|
||||
We should never come here on any modern machine
|
||||
*/
|
||||
VOID(my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE)));
|
||||
swap(my_off_t, newlength, oldsize);
|
||||
swap_variables(my_off_t, newlength, oldsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -68,7 +68,7 @@ byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen)
|
||||
DBUG_PRINT("note",("Packet got longer on compression; Not compressed"));
|
||||
return 0;
|
||||
}
|
||||
swap(ulong, *len, *complen); /* *len is now packet length */
|
||||
swap_variables(ulong, *len, *complen); /* *len is now packet length */
|
||||
return compbuf;
|
||||
}
|
||||
|
||||
|
@ -331,7 +331,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
||||
|
||||
if (keyseg->flag & HA_REVERSE_SORT)
|
||||
{
|
||||
swap(uchar*,a,b);
|
||||
swap_variables(uchar*, a, b);
|
||||
swap_flag=1; /* Remember swap of a & b */
|
||||
end= a+ (int) (end-b);
|
||||
}
|
||||
@ -356,8 +356,8 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
||||
if (*b != '-')
|
||||
return -1;
|
||||
a++; b++;
|
||||
swap(uchar*,a,b);
|
||||
swap(int,alength,blength);
|
||||
swap_variables(uchar*, a, b);
|
||||
swap_variables(int, alength, blength);
|
||||
swap_flag=1-swap_flag;
|
||||
alength--; blength--;
|
||||
end=a+alength;
|
||||
@ -385,7 +385,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
||||
}
|
||||
|
||||
if (swap_flag) /* Restore pointers */
|
||||
swap(uchar*,a,b);
|
||||
swap_variables(uchar*, a, b);
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_LONG_LONG
|
||||
|
29
sql/field.cc
29
sql/field.cc
@ -423,30 +423,11 @@ bool Field::get_time(TIME *ltime)
|
||||
|
||||
void Field::store_time(TIME *ltime,timestamp_type type)
|
||||
{
|
||||
char buff[25];
|
||||
switch (type) {
|
||||
case TIMESTAMP_NONE:
|
||||
case TIMESTAMP_DATETIME_ERROR:
|
||||
store("",0,&my_charset_bin); // Probably an error
|
||||
break;
|
||||
case TIMESTAMP_DATE:
|
||||
sprintf(buff,"%04d-%02d-%02d", ltime->year,ltime->month,ltime->day);
|
||||
store(buff,10,&my_charset_bin);
|
||||
break;
|
||||
case TIMESTAMP_DATETIME:
|
||||
sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d",
|
||||
ltime->year,ltime->month,ltime->day,
|
||||
ltime->hour,ltime->minute,ltime->second);
|
||||
store(buff,19,&my_charset_bin);
|
||||
break;
|
||||
case TIMESTAMP_TIME:
|
||||
{
|
||||
ulong length= my_sprintf(buff, (buff, "%02d:%02d:%02d",
|
||||
ltime->hour,ltime->minute,ltime->second));
|
||||
store(buff,(uint) length, &my_charset_bin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
char buff[MAX_DATE_REP_LENGTH];
|
||||
String tmp;
|
||||
tmp.set(buff, sizeof(buff), &my_charset_bin);
|
||||
TIME_to_string(ltime, &tmp);
|
||||
store(buff, tmp.length(), &my_charset_bin);
|
||||
}
|
||||
|
||||
|
||||
|
431
sql/item.cc
431
sql/item.cc
@ -27,6 +27,8 @@ static void mark_as_dependent(THD *thd,
|
||||
SELECT_LEX *last, SELECT_LEX *current,
|
||||
Item_ident *item);
|
||||
|
||||
const String my_null_string("NULL", 4, default_charset_info);
|
||||
|
||||
/*****************************************************************************
|
||||
** Item functions
|
||||
*****************************************************************************/
|
||||
@ -623,13 +625,11 @@ default_set_param_func(Item_param *param,
|
||||
param->set_null();
|
||||
}
|
||||
|
||||
Item_param::Item_param(unsigned position) :
|
||||
value_is_set(FALSE),
|
||||
Item_param::Item_param(unsigned pos_in_query_arg) :
|
||||
state(NO_VALUE),
|
||||
item_result_type(STRING_RESULT),
|
||||
item_type(STRING_ITEM),
|
||||
item_is_time(FALSE),
|
||||
long_data_supplied(FALSE),
|
||||
pos_in_query(position),
|
||||
pos_in_query(pos_in_query_arg),
|
||||
set_param_func(default_set_param_func)
|
||||
{
|
||||
name= (char*) "?";
|
||||
@ -645,93 +645,93 @@ void Item_param::set_null()
|
||||
{
|
||||
DBUG_ENTER("Item_param::set_null");
|
||||
/* These are cleared after each execution by reset() method */
|
||||
null_value= value_is_set= 1;
|
||||
max_length= 0;
|
||||
null_value= 1;
|
||||
/*
|
||||
Because of NULL and string values we need to set max_length for each new
|
||||
placeholder value: user can submit NULL for any placeholder type, and
|
||||
string length can be different in each execution.
|
||||
*/
|
||||
max_length= 0;
|
||||
decimals= 0;
|
||||
state= NULL_VALUE;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void Item_param::set_int(longlong i)
|
||||
void Item_param::set_int(longlong i, uint32 max_length_arg)
|
||||
{
|
||||
DBUG_ENTER("Item_param::set_int");
|
||||
int_value= (longlong)i;
|
||||
item_type= INT_ITEM;
|
||||
value_is_set= 1;
|
||||
value.integer= (longlong) i;
|
||||
state= INT_VALUE;
|
||||
max_length= max_length_arg;
|
||||
decimals= 0;
|
||||
maybe_null= 0;
|
||||
max_length= 11;
|
||||
DBUG_PRINT("info", ("integer: %lld", int_value));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void Item_param::set_double(double value)
|
||||
void Item_param::set_double(double d)
|
||||
{
|
||||
DBUG_ENTER("Item_param::set_double");
|
||||
real_value=value;
|
||||
item_type= REAL_ITEM;
|
||||
value_is_set= 1;
|
||||
maybe_null= 0;
|
||||
value.real= d;
|
||||
state= REAL_VALUE;
|
||||
max_length= DBL_DIG + 8;
|
||||
decimals= NOT_FIXED_DEC;
|
||||
max_length= DBL_DIG + 8;;
|
||||
DBUG_PRINT("info", ("double: %lg", real_value));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
void Item_param::set_value(const char *str, uint length)
|
||||
{
|
||||
DBUG_ENTER("Item_param::set_value");
|
||||
str_value.copy(str,length,default_charset());
|
||||
item_type= STRING_ITEM;
|
||||
value_is_set= 1;
|
||||
maybe_null= 0;
|
||||
max_length= length;
|
||||
DBUG_PRINT("info", ("string: %s", str_value.ptr()));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
void Item_param::set_time(TIME *tm, timestamp_type type)
|
||||
void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg)
|
||||
{
|
||||
ltime.year= tm->year;
|
||||
ltime.month= tm->month;
|
||||
ltime.day= tm->day;
|
||||
|
||||
ltime.hour= tm->hour;
|
||||
ltime.minute= tm->minute;
|
||||
ltime.second= tm->second;
|
||||
DBUG_ENTER("Item_param::set_time");
|
||||
|
||||
ltime.second_part= tm->second_part;
|
||||
value.time= *tm;
|
||||
value.time.time_type= type;
|
||||
|
||||
ltime.neg= tm->neg;
|
||||
|
||||
ltime.time_type= type;
|
||||
|
||||
item_is_time= TRUE;
|
||||
item_type= STRING_ITEM;
|
||||
value_is_set= 1;
|
||||
state= TIME_VALUE;
|
||||
maybe_null= 0;
|
||||
switch(type)
|
||||
{
|
||||
case TIMESTAMP_DATE:
|
||||
max_length= 10;
|
||||
break;
|
||||
case TIMESTAMP_DATETIME:
|
||||
max_length= 19;
|
||||
break;
|
||||
case TIMESTAMP_TIME:
|
||||
max_length= 8;
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(0); // it should be impossible
|
||||
}
|
||||
max_length= max_length_arg;
|
||||
decimals= 0;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
void Item_param::set_longdata(const char *str, ulong length)
|
||||
{
|
||||
str_value.append(str,length);
|
||||
long_data_supplied= 1;
|
||||
value_is_set= 1;
|
||||
bool Item_param::set_str(const char *str, ulong length)
|
||||
{
|
||||
DBUG_ENTER("Item_param::set_str");
|
||||
/*
|
||||
Assign string with no conversion: data is converted only after it's
|
||||
been written to the binary log.
|
||||
*/
|
||||
if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin))
|
||||
DBUG_RETURN(TRUE);
|
||||
state= STRING_VALUE;
|
||||
maybe_null= 0;
|
||||
/* max_length and decimals are set after charset conversion */
|
||||
/* sic: str may be not null-terminated, don't add DBUG_PRINT here */
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
bool Item_param::set_longdata(const char *str, ulong length)
|
||||
{
|
||||
DBUG_ENTER("Item_param::set_longdata");
|
||||
|
||||
/*
|
||||
If client character set is multibyte, end of long data packet
|
||||
may hit at the middle of a multibyte character. Additionally,
|
||||
if binary log is open we must write long data value to the
|
||||
binary log in character set of client. This is why we can't
|
||||
convert long data to connection character set as it comes
|
||||
(here), and first have to concatenate all pieces together,
|
||||
write query to the binary log and only then perform conversion.
|
||||
*/
|
||||
if (str_value.append(str, length, &my_charset_bin))
|
||||
DBUG_RETURN(TRUE);
|
||||
state= LONG_DATA_VALUE;
|
||||
maybe_null= 0;
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
@ -747,9 +747,18 @@ void Item_param::set_longdata(const char *str, ulong length)
|
||||
*/
|
||||
|
||||
void Item_param::reset()
|
||||
{
|
||||
str_value.set("", 0, &my_charset_bin);
|
||||
value_is_set= long_data_supplied= 0;
|
||||
{
|
||||
/* Shrink string buffer if it's bigger than max possible CHAR column */
|
||||
if (str_value.alloced_length() > MAX_CHAR_WIDTH)
|
||||
str_value.free();
|
||||
else
|
||||
str_value.length(0);
|
||||
/*
|
||||
We must prevent all charset conversions unless data of str_value
|
||||
has been written to the binary log.
|
||||
*/
|
||||
str_value.set_charset(&my_charset_bin);
|
||||
state= NO_VALUE;
|
||||
maybe_null= 1;
|
||||
null_value= 0;
|
||||
}
|
||||
@ -758,155 +767,223 @@ void Item_param::reset()
|
||||
int Item_param::save_in_field(Field *field, bool no_conversions)
|
||||
{
|
||||
DBUG_ASSERT(current_thd->command == COM_EXECUTE);
|
||||
|
||||
if (null_value)
|
||||
return (int) set_field_to_null(field);
|
||||
|
||||
|
||||
field->set_notnull();
|
||||
if (item_result_type == INT_RESULT)
|
||||
{
|
||||
longlong nr=val_int();
|
||||
return field->store(nr);
|
||||
}
|
||||
if (item_result_type == REAL_RESULT)
|
||||
{
|
||||
double nr=val();
|
||||
return field->store(nr);
|
||||
}
|
||||
if (item_is_time)
|
||||
{
|
||||
field->store_time(<ime, ltime.time_type);
|
||||
|
||||
switch (state) {
|
||||
case INT_VALUE:
|
||||
return field->store(value.integer);
|
||||
case REAL_VALUE:
|
||||
return field->store(value.real);
|
||||
case TIME_VALUE:
|
||||
field->store_time(&value.time, value.time.time_type);
|
||||
return 0;
|
||||
case STRING_VALUE:
|
||||
case LONG_DATA_VALUE:
|
||||
return field->store(str_value.ptr(), str_value.length(),
|
||||
str_value.charset());
|
||||
case NULL_VALUE:
|
||||
return set_field_to_null(field);
|
||||
case NO_VALUE:
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
String *result=val_str(&str_value);
|
||||
return field->store(result->ptr(),result->length(),field->charset());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
bool Item_param::get_time(TIME *res)
|
||||
{
|
||||
*res=ltime;
|
||||
return 0;
|
||||
if (state == TIME_VALUE)
|
||||
{
|
||||
*res= value.time;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
If parameter value isn't supplied assertion will fire in val_str()
|
||||
which is called from Item::get_time().
|
||||
*/
|
||||
return Item::get_time(res);
|
||||
}
|
||||
|
||||
|
||||
double Item_param::val()
|
||||
{
|
||||
DBUG_ASSERT(value_is_set == 1);
|
||||
int err;
|
||||
if (null_value)
|
||||
switch (state) {
|
||||
case REAL_VALUE:
|
||||
return value.real;
|
||||
case INT_VALUE:
|
||||
return (double) value.integer;
|
||||
case STRING_VALUE:
|
||||
case LONG_DATA_VALUE:
|
||||
{
|
||||
int dummy_err;
|
||||
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||
str_value.length(), (char**) 0, &dummy_err);
|
||||
}
|
||||
case TIME_VALUE:
|
||||
/*
|
||||
This works for example when user says SELECT ?+0.0 and supplies
|
||||
time value for the placeholder.
|
||||
*/
|
||||
return (double) TIME_to_ulonglong(&value.time);
|
||||
case NULL_VALUE:
|
||||
return 0.0;
|
||||
switch (item_result_type) {
|
||||
case STRING_RESULT:
|
||||
return (double) my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||
str_value.length(), (char**) 0, &err);
|
||||
case INT_RESULT:
|
||||
return (double)int_value;
|
||||
default:
|
||||
return real_value;
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
longlong Item_param::val_int()
|
||||
{
|
||||
DBUG_ASSERT(value_is_set == 1);
|
||||
int err;
|
||||
if (null_value)
|
||||
return 0;
|
||||
switch (item_result_type) {
|
||||
case STRING_RESULT:
|
||||
return my_strntoll(str_value.charset(),
|
||||
str_value.ptr(),str_value.length(),10,
|
||||
(char**) 0,&err);
|
||||
case REAL_RESULT:
|
||||
return (longlong) (real_value+(real_value > 0 ? 0.5 : -0.5));
|
||||
switch (state) {
|
||||
case REAL_VALUE:
|
||||
return (longlong) (value.real + (value.real > 0 ? 0.5 : -0.5));
|
||||
case INT_VALUE:
|
||||
return value.integer;
|
||||
case STRING_VALUE:
|
||||
case LONG_DATA_VALUE:
|
||||
{
|
||||
int dummy_err;
|
||||
return my_strntoll(str_value.charset(), str_value.ptr(),
|
||||
str_value.length(), 10, (char**) 0, &dummy_err);
|
||||
}
|
||||
case TIME_VALUE:
|
||||
return (longlong) TIME_to_ulonglong(&value.time);
|
||||
case NULL_VALUE:
|
||||
return 0;
|
||||
default:
|
||||
return int_value;
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
String *Item_param::val_str(String* str)
|
||||
{
|
||||
DBUG_ASSERT(value_is_set == 1);
|
||||
if (null_value)
|
||||
return NULL;
|
||||
switch (item_result_type) {
|
||||
case INT_RESULT:
|
||||
str->set(int_value, &my_charset_bin);
|
||||
switch (state) {
|
||||
case STRING_VALUE:
|
||||
case LONG_DATA_VALUE:
|
||||
return &str_value;
|
||||
case REAL_VALUE:
|
||||
str->set(value.real, NOT_FIXED_DEC, &my_charset_bin);
|
||||
return str;
|
||||
case REAL_RESULT:
|
||||
str->set(real_value, 2, &my_charset_bin);
|
||||
case INT_VALUE:
|
||||
str->set(value.integer, &my_charset_bin);
|
||||
return str;
|
||||
case TIME_VALUE:
|
||||
{
|
||||
if (str->reserve(MAX_DATE_REP_LENGTH))
|
||||
break;
|
||||
TIME_to_string(&value.time, str);
|
||||
return str;
|
||||
default:
|
||||
return (String*) &str_value;
|
||||
}
|
||||
case NULL_VALUE:
|
||||
return NULL;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
Return Param item values in string format, for generating the dynamic
|
||||
query used in update/binary logs
|
||||
TODO: change interface and implementation to fill log data in place
|
||||
and avoid one more memcpy/alloc between str and log string.
|
||||
*/
|
||||
|
||||
String *Item_param::query_val_str(String* str)
|
||||
const String *Item_param::query_val_str(String* str) const
|
||||
{
|
||||
DBUG_ASSERT(value_is_set == 1);
|
||||
switch (item_result_type) {
|
||||
case INT_RESULT:
|
||||
case REAL_RESULT:
|
||||
return val_str(str);
|
||||
switch (state) {
|
||||
case INT_VALUE:
|
||||
str->set(value.integer, &my_charset_bin);
|
||||
break;
|
||||
case REAL_VALUE:
|
||||
str->set(value.real, NOT_FIXED_DEC, &my_charset_bin);
|
||||
break;
|
||||
case TIME_VALUE:
|
||||
{
|
||||
char *buf, *ptr;
|
||||
String tmp;
|
||||
str->length(0);
|
||||
/*
|
||||
TODO: in case of error we need to notify replication
|
||||
that binary log contains wrong statement
|
||||
*/
|
||||
if (str->reserve(MAX_DATE_REP_LENGTH+3))
|
||||
break;
|
||||
|
||||
/* Create date string inplace */
|
||||
buf= str->c_ptr_quick();
|
||||
ptr= buf;
|
||||
*ptr++= '\'';
|
||||
tmp.set(ptr, MAX_DATE_REP_LENGTH, &my_charset_bin);
|
||||
tmp.length(0);
|
||||
TIME_to_string(&value.time, &tmp);
|
||||
|
||||
ptr+= tmp.length();
|
||||
*ptr++= '\'';
|
||||
str->length((uint32) (ptr - buf));
|
||||
break;
|
||||
}
|
||||
case STRING_VALUE:
|
||||
case LONG_DATA_VALUE:
|
||||
{
|
||||
char *buf, *ptr;
|
||||
str->length(0);
|
||||
if (str->reserve(str_value.length()*2+3))
|
||||
break;
|
||||
|
||||
buf= str->c_ptr_quick();
|
||||
ptr= buf;
|
||||
*ptr++= '\'';
|
||||
ptr+= escape_string_for_mysql(str_value.charset(), ptr,
|
||||
str_value.ptr(), str_value.length());
|
||||
*ptr++= '\'';
|
||||
str->length(ptr - buf);
|
||||
break;
|
||||
}
|
||||
case NULL_VALUE:
|
||||
return &my_null_string;
|
||||
default:
|
||||
str->set("'", 1, default_charset());
|
||||
|
||||
if (!item_is_time)
|
||||
{
|
||||
str->append(str_value);
|
||||
const char *from= str->ptr();
|
||||
uint32 length= 1;
|
||||
|
||||
// Escape misc cases
|
||||
char *to= (char *)from, *end= (char *)to+str->length();
|
||||
for (to++; to != end ; length++, to++)
|
||||
{
|
||||
switch(*to) {
|
||||
case '\'':
|
||||
case '"':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\\': // TODO: Add remaining ..
|
||||
str->replace(length,0,"\\",1);
|
||||
to++; end++; length++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char buff[40];
|
||||
String tmp(buff,sizeof(buff), &my_charset_bin);
|
||||
|
||||
switch (ltime.time_type) {
|
||||
case TIMESTAMP_NONE:
|
||||
case TIMESTAMP_DATETIME_ERROR:
|
||||
tmp.length(0); // Should never happen
|
||||
break;
|
||||
case TIMESTAMP_DATE:
|
||||
make_date((DATE_TIME_FORMAT*) 0, <ime, &tmp);
|
||||
break;
|
||||
case TIMESTAMP_DATETIME:
|
||||
make_datetime((DATE_TIME_FORMAT*) 0, <ime, &tmp);
|
||||
break;
|
||||
case TIMESTAMP_TIME:
|
||||
make_time((DATE_TIME_FORMAT*) 0, <ime, &tmp);
|
||||
break;
|
||||
}
|
||||
str->append(tmp);
|
||||
}
|
||||
str->append('\'');
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Convert string from client character set to the character set of
|
||||
connection.
|
||||
*/
|
||||
|
||||
bool Item_param::convert_str_value(THD *thd)
|
||||
{
|
||||
bool rc= FALSE;
|
||||
if (state == STRING_VALUE || state == LONG_DATA_VALUE)
|
||||
{
|
||||
/*
|
||||
Check is so simple because all charsets were set up properly
|
||||
in setup_one_conversion_function, where typecode of
|
||||
placeholder was also taken into account: the variables are different
|
||||
here only if conversion is really necessary.
|
||||
*/
|
||||
if (value.cs_info.final_character_set_of_str_value !=
|
||||
value.cs_info.character_set_client)
|
||||
{
|
||||
rc= thd->convert_string(&str_value,
|
||||
value.cs_info.character_set_client,
|
||||
value.cs_info.final_character_set_of_str_value);
|
||||
}
|
||||
max_length= str_value.length();
|
||||
decimals= 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* End of Item_param related */
|
||||
|
||||
|
||||
|
76
sql/item.h
76
sql/item.h
@ -393,33 +393,63 @@ public:
|
||||
class Item_param :public Item
|
||||
{
|
||||
public:
|
||||
bool value_is_set;
|
||||
longlong int_value;
|
||||
double real_value;
|
||||
TIME ltime;
|
||||
enum enum_item_param_state
|
||||
{
|
||||
NO_VALUE, NULL_VALUE, INT_VALUE, REAL_VALUE,
|
||||
STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE
|
||||
} state;
|
||||
|
||||
union
|
||||
{
|
||||
longlong integer;
|
||||
double real;
|
||||
/*
|
||||
Character sets conversion info for string values.
|
||||
Character sets of client and connection defined at bind time are used
|
||||
for all conversions, even if one of them is later changed (i.e.
|
||||
between subsequent calls to mysql_stmt_execute).
|
||||
*/
|
||||
struct CONVERSION_INFO
|
||||
{
|
||||
CHARSET_INFO *character_set_client;
|
||||
/*
|
||||
This points at character set of connection if conversion
|
||||
to it is required (i. e. if placeholder typecode is not BLOB).
|
||||
Otherwise it's equal to character_set_client (to simplify
|
||||
check in convert_str_value()).
|
||||
*/
|
||||
CHARSET_INFO *final_character_set_of_str_value;
|
||||
} cs_info;
|
||||
TIME time;
|
||||
} value;
|
||||
|
||||
/* Cached values for virtual methods to save us one switch. */
|
||||
enum Item_result item_result_type;
|
||||
enum Type item_type;
|
||||
enum enum_field_types buffer_type;
|
||||
bool item_is_time;
|
||||
bool long_data_supplied;
|
||||
/*
|
||||
Offset of placeholder inside statement text. Used to create
|
||||
no-placeholders version of this statement for the binary log.
|
||||
*/
|
||||
uint pos_in_query;
|
||||
|
||||
Item_param(uint position);
|
||||
Item_param(uint pos_in_query_arg);
|
||||
|
||||
enum Item_result result_type () const { return item_result_type; }
|
||||
enum Type type() const { return item_type; }
|
||||
enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
|
||||
|
||||
double val();
|
||||
longlong val_int();
|
||||
String *val_str(String*);
|
||||
int save_in_field(Field *field, bool no_conversions);
|
||||
void set_null();
|
||||
void set_int(longlong i);
|
||||
void set_double(double i);
|
||||
void set_value(const char *str, uint length);
|
||||
void set_long_str(const char *str, ulong length);
|
||||
void set_long_binary(const char *str, ulong length);
|
||||
void set_longdata(const char *str, ulong length);
|
||||
void set_long_end();
|
||||
void set_time(TIME *tm, timestamp_type type);
|
||||
bool get_time(TIME *tm);
|
||||
int save_in_field(Field *field, bool no_conversions);
|
||||
|
||||
void set_null();
|
||||
void set_int(longlong i, uint32 max_length_arg);
|
||||
void set_double(double i);
|
||||
bool set_str(const char *str, ulong length);
|
||||
bool set_longdata(const char *str, ulong length);
|
||||
void set_time(TIME *tm, timestamp_type type, uint32 max_length_arg);
|
||||
void reset();
|
||||
/*
|
||||
Assign placeholder value from bind data.
|
||||
@ -429,10 +459,10 @@ public:
|
||||
*/
|
||||
void (*set_param_func)(Item_param *param, uchar **pos, ulong len);
|
||||
|
||||
enum Item_result result_type () const
|
||||
{ return item_result_type; }
|
||||
String *query_val_str(String *str);
|
||||
enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
|
||||
const String *query_val_str(String *str) const;
|
||||
|
||||
bool convert_str_value(THD *thd);
|
||||
|
||||
Item *new_item() { return new Item_param(pos_in_query); }
|
||||
/*
|
||||
If value for parameter was not set we treat it as non-const
|
||||
@ -440,7 +470,7 @@ public:
|
||||
parameter is constant during execution.
|
||||
*/
|
||||
virtual table_map used_tables() const
|
||||
{ return value_is_set ? (table_map)0 : PARAM_TABLE_BIT; }
|
||||
{ return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; }
|
||||
void print(String *str) { str->append('?'); }
|
||||
};
|
||||
|
||||
|
@ -1134,9 +1134,6 @@ void Item_func_curdate::fix_length_and_dec()
|
||||
|
||||
store_now_in_tm(current_thd->query_start(),&start);
|
||||
|
||||
value=(longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+
|
||||
((uint) start.tm_mon+1)*100+
|
||||
(uint) start.tm_mday);
|
||||
/* For getdate */
|
||||
ltime.year= start.tm_year+1900;
|
||||
ltime.month= start.tm_mon+1;
|
||||
@ -1147,6 +1144,7 @@ void Item_func_curdate::fix_length_and_dec()
|
||||
ltime.second_part=0;
|
||||
ltime.neg=0;
|
||||
ltime.time_type=TIMESTAMP_DATE;
|
||||
value= (longlong) TIME_to_ulonglong_date(<ime);
|
||||
}
|
||||
|
||||
String *Item_func_curdate::val_str(String *str)
|
||||
@ -1205,15 +1203,12 @@ void Item_func_curtime::fix_length_and_dec()
|
||||
|
||||
decimals=0;
|
||||
store_now_in_tm(current_thd->query_start(),&start);
|
||||
value=(longlong) ((ulong) ((uint) start.tm_hour)*10000L+
|
||||
(ulong) (((uint) start.tm_min)*100L+
|
||||
(uint) start.tm_sec));
|
||||
ltime.day= 0;
|
||||
ltime.hour= start.tm_hour;
|
||||
ltime.minute= start.tm_min;
|
||||
ltime.second= start.tm_sec;
|
||||
ltime.second_part= 0;
|
||||
ltime.neg= 0;
|
||||
value= TIME_to_ulonglong_time(<ime);
|
||||
make_time((DATE_TIME_FORMAT *) 0, <ime, &tmp);
|
||||
max_length= buff_length= tmp.length();
|
||||
}
|
||||
@ -1256,23 +1251,12 @@ void Item_func_now::fix_length_and_dec()
|
||||
collation.set(&my_charset_bin);
|
||||
|
||||
store_now_in_tm(current_thd->query_start(),&start);
|
||||
value=((longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+
|
||||
(((uint) start.tm_mon+1)*100+
|
||||
(uint) start.tm_mday))*(longlong) 1000000L+
|
||||
(longlong) ((ulong) ((uint) start.tm_hour)*10000L+
|
||||
(ulong) (((uint) start.tm_min)*100L+
|
||||
(uint) start.tm_sec)));
|
||||
|
||||
/* For getdate */
|
||||
ltime.year= start.tm_year+1900;
|
||||
ltime.month= start.tm_mon+1;
|
||||
ltime.day= start.tm_mday;
|
||||
ltime.hour= start.tm_hour;
|
||||
ltime.minute= start.tm_min;
|
||||
ltime.second= start.tm_sec;
|
||||
ltime.second_part= 0;
|
||||
ltime.neg= 0;
|
||||
localtime_to_TIME(<ime, &start);
|
||||
ltime.time_type= TIMESTAMP_DATETIME;
|
||||
|
||||
value= (longlong) TIME_to_ulonglong_datetime(<ime);
|
||||
|
||||
make_datetime((DATE_TIME_FORMAT *) 0, <ime, &tmp);
|
||||
max_length= buff_length= tmp.length();
|
||||
@ -1457,10 +1441,10 @@ uint Item_func_date_format::format_length(const String *format)
|
||||
|
||||
String *Item_func_date_format::val_str(String *str)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *format;
|
||||
TIME l_time;
|
||||
uint size;
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
|
||||
if (!is_time_format)
|
||||
{
|
||||
@ -1507,25 +1491,18 @@ null_date:
|
||||
|
||||
String *Item_func_from_unixtime::val_str(String *str)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
struct tm tm_tmp,*start;
|
||||
time_t tmp=(time_t) args[0]->val_int();
|
||||
struct tm tm_tmp;
|
||||
time_t tmp;
|
||||
TIME ltime;
|
||||
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
tmp= (time_t) args[0]->val_int();
|
||||
if ((null_value=args[0]->null_value))
|
||||
goto null_date;
|
||||
|
||||
localtime_r(&tmp,&tm_tmp);
|
||||
start=&tm_tmp;
|
||||
|
||||
ltime.year= start->tm_year+1900;
|
||||
ltime.month= start->tm_mon+1;
|
||||
ltime.day= start->tm_mday;
|
||||
ltime.hour= start->tm_hour;
|
||||
ltime.minute= start->tm_min;
|
||||
ltime.second= start->tm_sec;
|
||||
ltime.second_part= 0;
|
||||
ltime.neg=0;
|
||||
localtime_to_TIME(<ime, &tm_tmp);
|
||||
|
||||
if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
|
||||
goto null_date;
|
||||
@ -1540,19 +1517,17 @@ null_date:
|
||||
|
||||
longlong Item_func_from_unixtime::val_int()
|
||||
{
|
||||
TIME ltime;
|
||||
struct tm tm_tmp;
|
||||
time_t tmp;
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
time_t tmp=(time_t) (ulong) args[0]->val_int();
|
||||
|
||||
tmp= (time_t) (ulong) args[0]->val_int();
|
||||
if ((null_value=args[0]->null_value))
|
||||
return 0;
|
||||
struct tm tm_tmp,*start;
|
||||
localtime_r(&tmp,&tm_tmp);
|
||||
start= &tm_tmp;
|
||||
return ((longlong) ((ulong) ((uint) start->tm_year+1900)*10000L+
|
||||
(((uint) start->tm_mon+1)*100+
|
||||
(uint) start->tm_mday))*LL(1000000)+
|
||||
(longlong) ((ulong) ((uint) start->tm_hour)*10000L+
|
||||
(ulong) (((uint) start->tm_min)*100L+
|
||||
(uint) start->tm_sec)));
|
||||
localtime_to_TIME(<ime, &tm_tmp);
|
||||
return (longlong) TIME_to_ulonglong_datetime(<ime);
|
||||
}
|
||||
|
||||
bool Item_func_from_unixtime::get_date(TIME *ltime,
|
||||
@ -1561,17 +1536,9 @@ bool Item_func_from_unixtime::get_date(TIME *ltime,
|
||||
time_t tmp=(time_t) (ulong) args[0]->val_int();
|
||||
if ((null_value=args[0]->null_value))
|
||||
return 1;
|
||||
struct tm tm_tmp,*start;
|
||||
struct tm tm_tmp;
|
||||
localtime_r(&tmp,&tm_tmp);
|
||||
start= &tm_tmp;
|
||||
ltime->year= start->tm_year+1900;
|
||||
ltime->month= start->tm_mon+1;
|
||||
ltime->day= start->tm_mday;
|
||||
ltime->hour= start->tm_hour;
|
||||
ltime->minute=start->tm_min;
|
||||
ltime->second=start->tm_sec;
|
||||
ltime->second_part=0;
|
||||
ltime->neg=0;
|
||||
localtime_to_TIME(ltime, &tm_tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2035,7 +2002,7 @@ String *Item_date_typecast::val_str(String *str)
|
||||
|
||||
if (!get_arg0_date(<ime,1) && !str->alloc(11))
|
||||
{
|
||||
make_date((DATE_TIME_FORMAT *) 0,<ime, str);
|
||||
make_date((DATE_TIME_FORMAT *) 0, <ime, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
|
||||
{
|
||||
if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
|
||||
{
|
||||
swap(THR_LOCK_DATA *,*lock,sql_lock->locks[i]);
|
||||
swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
|
||||
lock++;
|
||||
found++;
|
||||
}
|
||||
@ -259,7 +259,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
|
||||
{
|
||||
if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
|
||||
{
|
||||
swap(TABLE *,*table,sql_lock->table[i]);
|
||||
swap_variables(TABLE *, *table, sql_lock->table[i]);
|
||||
table++;
|
||||
found++;
|
||||
}
|
||||
|
@ -284,6 +284,14 @@ void debug_sync_point(const char* lock_name, uint lock_timeout);
|
||||
#define WEEK_MONDAY_FIRST 1
|
||||
#define WEEK_YEAR 2
|
||||
#define WEEK_FIRST_WEEKDAY 4
|
||||
/*
|
||||
Required buffer length for make_date, make_time, make_datetime
|
||||
and TIME_to_string functions. Note, that the caller is still
|
||||
responsible to check that given TIME structure has values
|
||||
in valid ranges, otherwise size of the buffer could be not
|
||||
enough.
|
||||
*/
|
||||
#define MAX_DATE_REP_LENGTH 30
|
||||
|
||||
struct st_table;
|
||||
class THD;
|
||||
@ -995,9 +1003,17 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
|
||||
timestamp_type type);
|
||||
extern bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time,
|
||||
timestamp_type type, String *str);
|
||||
extern void make_time(DATE_TIME_FORMAT *format, TIME *l_time, String *str);
|
||||
void make_date(DATE_TIME_FORMAT *format, TIME *l_time, String *str);
|
||||
void make_datetime(DATE_TIME_FORMAT *format, TIME *l_time, String *str);
|
||||
void make_datetime(const DATE_TIME_FORMAT *format, const TIME *l_time,
|
||||
String *str);
|
||||
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);
|
||||
void TIME_to_string(const TIME *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);
|
||||
|
@ -1401,7 +1401,7 @@ key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
|
||||
{
|
||||
if (key1->part > key2->part)
|
||||
{
|
||||
swap(SEL_ARG *,key1,key2);
|
||||
swap_variables(SEL_ARG *, key1, key2);
|
||||
clone_flag=swap_clone_flag(clone_flag);
|
||||
}
|
||||
// key1->part < key2->part
|
||||
@ -1417,7 +1417,7 @@ key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
|
||||
key2->type != SEL_ARG::MAYBE_KEY) ||
|
||||
key1->type == SEL_ARG::MAYBE_KEY)
|
||||
{ // Put simple key in key2
|
||||
swap(SEL_ARG *,key1,key2);
|
||||
swap_variables(SEL_ARG *, key1, key2);
|
||||
clone_flag=swap_clone_flag(clone_flag);
|
||||
}
|
||||
|
||||
@ -1559,7 +1559,7 @@ key_or(SEL_ARG *key1,SEL_ARG *key2)
|
||||
{
|
||||
if (key2->use_count == 0 || key1->elements > key2->elements)
|
||||
{
|
||||
swap(SEL_ARG *,key1,key2);
|
||||
swap_variables(SEL_ARG *,key1,key2);
|
||||
}
|
||||
else if (!(key1=key1->clone_tree()))
|
||||
return 0; // OOM
|
||||
|
@ -455,6 +455,7 @@ void Protocol::init(THD *thd_arg)
|
||||
{
|
||||
thd=thd_arg;
|
||||
packet= &thd->packet;
|
||||
convert= &thd->convert_buffer;
|
||||
#ifndef DEBUG_OFF
|
||||
field_types= 0;
|
||||
#endif
|
||||
@ -691,6 +692,26 @@ bool Protocol_simple::store_null()
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
Auxilary function to convert string to the given character set
|
||||
and store in network buffer.
|
||||
*/
|
||||
|
||||
bool Protocol::store_string_aux(const char *from, uint length,
|
||||
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
|
||||
{
|
||||
/* 'tocs' is set 0 when client issues SET character_set_results=NULL */
|
||||
if (tocs && !my_charset_same(fromcs, tocs) &&
|
||||
fromcs != &my_charset_bin &&
|
||||
tocs != &my_charset_bin)
|
||||
{
|
||||
return convert->copy(from, length, fromcs, tocs) ||
|
||||
net_store_data(convert->ptr(), convert->length());
|
||||
}
|
||||
return net_store_data(from, length);
|
||||
}
|
||||
|
||||
|
||||
bool Protocol_simple::store(const char *from, uint length,
|
||||
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
|
||||
{
|
||||
@ -701,15 +722,7 @@ bool Protocol_simple::store(const char *from, uint length,
|
||||
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
||||
field_pos++;
|
||||
#endif
|
||||
if (tocs && !my_charset_same(fromcs, tocs) &&
|
||||
(fromcs != &my_charset_bin) &&
|
||||
(tocs != &my_charset_bin))
|
||||
{
|
||||
convert.copy(from, length, fromcs, tocs);
|
||||
return net_store_data(convert.ptr(), convert.length());
|
||||
}
|
||||
else
|
||||
return net_store_data(from, length);
|
||||
return store_string_aux(from, length, fromcs, tocs);
|
||||
}
|
||||
|
||||
|
||||
@ -724,15 +737,7 @@ bool Protocol_simple::store(const char *from, uint length,
|
||||
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
||||
field_pos++;
|
||||
#endif
|
||||
if (tocs && !my_charset_same(fromcs, tocs) &&
|
||||
(fromcs != &my_charset_bin) &&
|
||||
(tocs != &my_charset_bin))
|
||||
{
|
||||
convert.copy(from, length, fromcs, tocs);
|
||||
return net_store_data(convert.ptr(), convert.length());
|
||||
}
|
||||
else
|
||||
return net_store_data(from, length);
|
||||
return store_string_aux(from, length, fromcs, tocs);
|
||||
}
|
||||
|
||||
|
||||
@ -826,15 +831,7 @@ bool Protocol_simple::store(Field *field)
|
||||
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
|
||||
|
||||
field->val_str(&str);
|
||||
if (tocs && !my_charset_same(field->charset(), tocs) &&
|
||||
(field->charset() != &my_charset_bin) &&
|
||||
(tocs != &my_charset_bin))
|
||||
{
|
||||
convert.copy(str.ptr(), str.length(), str.charset(), tocs);
|
||||
return net_store_data(convert.ptr(), convert.length());
|
||||
}
|
||||
else
|
||||
return net_store_data(str.ptr(), str.length());
|
||||
return store_string_aux(str.ptr(), str.length(), str.charset(), tocs);
|
||||
}
|
||||
|
||||
|
||||
@ -947,8 +944,9 @@ void Protocol_prep::prepare_for_resend()
|
||||
}
|
||||
|
||||
|
||||
bool Protocol_prep::store(const char *from,uint length, CHARSET_INFO *cs)
|
||||
bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
|
||||
{
|
||||
CHARSET_INFO *tocs= thd->variables.character_set_results;
|
||||
#ifndef DEBUG_OFF
|
||||
DBUG_ASSERT(field_types == 0 ||
|
||||
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
|
||||
@ -956,7 +954,7 @@ bool Protocol_prep::store(const char *from,uint length, CHARSET_INFO *cs)
|
||||
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
||||
#endif
|
||||
field_pos++;
|
||||
return net_store_data(from, length);
|
||||
return store_string_aux(from, length, fromcs, tocs);
|
||||
}
|
||||
|
||||
bool Protocol_prep::store(const char *from,uint length,
|
||||
@ -969,7 +967,7 @@ bool Protocol_prep::store(const char *from,uint length,
|
||||
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
||||
#endif
|
||||
field_pos++;
|
||||
return net_store_data(from, length);
|
||||
return store_string_aux(from, length, fromcs, tocs);
|
||||
}
|
||||
|
||||
bool Protocol_prep::store_null()
|
||||
|
@ -30,7 +30,7 @@ class Protocol
|
||||
protected:
|
||||
THD *thd;
|
||||
String *packet;
|
||||
String convert;
|
||||
String *convert;
|
||||
uint field_pos;
|
||||
#ifndef DEBUG_OFF
|
||||
enum enum_field_types *field_types;
|
||||
@ -42,6 +42,8 @@ protected:
|
||||
MYSQL_FIELD *next_mysql_field;
|
||||
MEM_ROOT *alloc;
|
||||
#endif
|
||||
bool store_string_aux(const char *from, uint length,
|
||||
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
|
||||
public:
|
||||
Protocol() {}
|
||||
Protocol(THD *thd_arg) { init(thd_arg); }
|
||||
|
@ -106,13 +106,13 @@ bool foreign_key_prefix(Key *a, Key *b)
|
||||
if (a->generated)
|
||||
{
|
||||
if (b->generated && a->columns.elements > b->columns.elements)
|
||||
swap(Key*, a, b); // Put shorter key in 'a'
|
||||
swap_variables(Key*, a, b); // Put shorter key in 'a'
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!b->generated)
|
||||
return TRUE; // No foreign key
|
||||
swap(Key*, a, b); // Put generated key in 'a'
|
||||
swap_variables(Key*, a, b); // Put generated key in 'a'
|
||||
}
|
||||
|
||||
/* Test if 'a' is a prefix of 'b' */
|
||||
@ -503,6 +503,35 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Convert string from source character set to target character set inplace.
|
||||
|
||||
SYNOPSIS
|
||||
THD::convert_string
|
||||
|
||||
DESCRIPTION
|
||||
Convert string using convert_buffer - buffer for character set
|
||||
conversion shared between all protocols.
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
!0 out of memory
|
||||
*/
|
||||
|
||||
bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
|
||||
{
|
||||
if (convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs))
|
||||
return TRUE;
|
||||
/* If convert_buffer >> s copying is more efficient long term */
|
||||
if (convert_buffer.alloced_length() >= convert_buffer.length() * 2 ||
|
||||
!s->is_alloced())
|
||||
{
|
||||
return s->copy(convert_buffer);
|
||||
}
|
||||
s->swap(convert_buffer);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
Update some cache variables when character set changes
|
||||
*/
|
||||
|
@ -594,6 +594,7 @@ public:
|
||||
Protocol_prep protocol_prep; // Binary protocol
|
||||
HASH user_vars; // hash for user variables
|
||||
String packet; // dynamic buffer for network I/O
|
||||
String convert_buffer; // buffer for charset conversions
|
||||
struct sockaddr_in remote; // client socket address
|
||||
struct rand_struct rand; // used for authentication
|
||||
struct system_variables variables; // Changeable local variables
|
||||
@ -917,6 +918,9 @@ public:
|
||||
bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
|
||||
const char *from, uint from_length,
|
||||
CHARSET_INFO *from_cs);
|
||||
|
||||
bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs);
|
||||
|
||||
void add_changed_table(TABLE *table);
|
||||
void add_changed_table(const char *key, long key_length);
|
||||
CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length);
|
||||
|
@ -433,7 +433,7 @@ abort:
|
||||
Prepare items in INSERT statement
|
||||
|
||||
SYNOPSIS
|
||||
mysql_prepare_update()
|
||||
mysql_prepare_insert()
|
||||
thd - thread handler
|
||||
table_list - global table list
|
||||
insert_table_list - local table list of INSERT SELECT_LEX
|
||||
|
@ -1816,7 +1816,10 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
||||
return 1;
|
||||
thd->query[packet_length]=0;
|
||||
thd->query_length= packet_length;
|
||||
thd->packet.shrink(thd->variables.net_buffer_length);// Reclaim some memory
|
||||
|
||||
/* Reclaim some memory */
|
||||
thd->packet.shrink(thd->variables.net_buffer_length);
|
||||
thd->convert_buffer.shrink(thd->variables.net_buffer_length);
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
@ -76,8 +76,6 @@ Long data handling:
|
||||
#include <mysql.h>
|
||||
#endif
|
||||
|
||||
const String my_null_string("NULL", 4, default_charset_info);
|
||||
|
||||
/******************************************************************************
|
||||
Prepared_statement: statement which can contain placeholders
|
||||
******************************************************************************/
|
||||
@ -91,7 +89,6 @@ public:
|
||||
uint last_errno;
|
||||
char last_error[MYSQL_ERRMSG_SIZE];
|
||||
bool get_longdata_error;
|
||||
bool log_full_query;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
|
||||
uchar *read_pos);
|
||||
@ -239,7 +236,7 @@ static ulong get_param_length(uchar **packet, ulong len)
|
||||
none
|
||||
*/
|
||||
|
||||
void set_param_tiny(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_tiny(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (len < 1)
|
||||
@ -247,55 +244,55 @@ void set_param_tiny(Item_param *param, uchar **pos, ulong len)
|
||||
#endif
|
||||
int8 value= (int8) **pos;
|
||||
param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) :
|
||||
(longlong) value);
|
||||
(longlong) value, 4);
|
||||
*pos+= 1;
|
||||
}
|
||||
|
||||
void set_param_short(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_short(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
int16 value;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (len < 2)
|
||||
return;
|
||||
int16 value= sint2korr(*pos);
|
||||
value= sint2korr(*pos);
|
||||
#else
|
||||
int16 value;
|
||||
shortget(value, *pos);
|
||||
#endif
|
||||
param->set_int(param->unsigned_flag ? (longlong) ((uint16) value) :
|
||||
(longlong) value);
|
||||
(longlong) value, 6);
|
||||
*pos+= 2;
|
||||
}
|
||||
|
||||
void set_param_int32(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_int32(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
int32 value;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (len < 4)
|
||||
return;
|
||||
int32 value= sint4korr(*pos);
|
||||
value= sint4korr(*pos);
|
||||
#else
|
||||
int32 value;
|
||||
longget(value, *pos);
|
||||
#endif
|
||||
param->set_int(param->unsigned_flag ? (longlong) ((uint32) value) :
|
||||
(longlong) value);
|
||||
(longlong) value, 11);
|
||||
*pos+= 4;
|
||||
}
|
||||
|
||||
void set_param_int64(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_int64(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
longlong value;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (len < 8)
|
||||
return;
|
||||
param->set_int((longlong)sint8korr(*pos));
|
||||
*pos+= 8;
|
||||
value= (longlong) sint8korr(*pos);
|
||||
#else
|
||||
longlong value;
|
||||
longlongget(value, *pos);
|
||||
param->set_int(value);
|
||||
#endif
|
||||
param->set_int(value, 21);
|
||||
*pos+= 8;
|
||||
}
|
||||
|
||||
void set_param_float(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_float(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (len < 4)
|
||||
@ -307,7 +304,7 @@ void set_param_float(Item_param *param, uchar **pos, ulong len)
|
||||
*pos+= 4;
|
||||
}
|
||||
|
||||
void set_param_double(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_double(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (len < 8)
|
||||
@ -320,9 +317,10 @@ void set_param_double(Item_param *param, uchar **pos, ulong len)
|
||||
}
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
ulong length;
|
||||
uint day;
|
||||
|
||||
if ((length= get_param_length(pos, len)) >= 8)
|
||||
{
|
||||
@ -332,20 +330,33 @@ void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
/* TODO: why length is compared with 8 here? */
|
||||
tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0;
|
||||
|
||||
tm.day= (ulong) sint4korr(to+1);
|
||||
tm.hour= (uint) to[5];
|
||||
/*
|
||||
Note, that though ranges of hour, minute and second are not checked
|
||||
here we rely on them being < 256: otherwise
|
||||
we'll get buffer overflow in make_{date,time} functions,
|
||||
which are called when time value is converted to string.
|
||||
*/
|
||||
day= (uint) sint4korr(to+1);
|
||||
tm.hour= (uint) to[5] + day * 24;
|
||||
tm.minute= (uint) to[6];
|
||||
tm.second= (uint) to[7];
|
||||
|
||||
tm.year= tm.month= 0;
|
||||
if (tm.hour > 838)
|
||||
{
|
||||
/* TODO: add warning 'Data truncated' here */
|
||||
tm.hour= 838;
|
||||
tm.minute= 59;
|
||||
tm.second= 59;
|
||||
}
|
||||
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);
|
||||
}
|
||||
*pos+= length;
|
||||
}
|
||||
|
||||
void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
uint length;
|
||||
|
||||
@ -356,6 +367,11 @@ void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
|
||||
tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
|
||||
|
||||
/*
|
||||
Note, that though ranges of hour, minute and second are not checked
|
||||
here we rely on them being < 256: otherwise
|
||||
we'll get buffer overflow in make_{date,time} functions.
|
||||
*/
|
||||
if (length > 4)
|
||||
{
|
||||
tm.hour= (uint) to[4];
|
||||
@ -370,12 +386,13 @@ void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
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);
|
||||
}
|
||||
*pos+= length;
|
||||
}
|
||||
|
||||
void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
ulong length;
|
||||
|
||||
@ -383,7 +400,11 @@ void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
uchar *to= *pos;
|
||||
TIME tm;
|
||||
|
||||
/*
|
||||
Note, that though ranges of hour, minute and second are not checked
|
||||
here we rely on them being < 256: otherwise
|
||||
we'll get buffer overflow in make_{date,time} functions.
|
||||
*/
|
||||
tm.year= (uint) sint2korr(to);
|
||||
tm.month= (uint) to[2];
|
||||
tm.day= (uint) to[3];
|
||||
@ -392,7 +413,8 @@ void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
tm.second_part= 0;
|
||||
tm.neg= 0;
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_DATE);
|
||||
param->set_time(&tm, TIMESTAMP_DATE,
|
||||
MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
*pos+= length;
|
||||
}
|
||||
@ -412,8 +434,9 @@ void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
|
||||
tm.year= tm.month= 0;
|
||||
tm.neg= to->neg;
|
||||
param->set_time(&tm, TIMESTAMP_TIME,
|
||||
MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_TIME);
|
||||
}
|
||||
|
||||
void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
@ -431,7 +454,8 @@ void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
tm.month= to->month;
|
||||
tm.neg= 0;
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_DATETIME);
|
||||
param->set_time(&tm, TIMESTAMP_DATETIME,
|
||||
MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
|
||||
void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
@ -449,60 +473,111 @@ void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
tm.second_part= 0;
|
||||
tm.neg= 0;
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_DATE);
|
||||
param->set_time(&tm, TIMESTAMP_DATE,
|
||||
MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
#endif /*!EMBEDDED_LIBRARY*/
|
||||
|
||||
void set_param_str(Item_param *param, uchar **pos, ulong len)
|
||||
|
||||
static void set_param_str(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
ulong length= get_param_length(pos, len);
|
||||
param->set_value((const char *)*pos, length);
|
||||
param->set_str((const char *)*pos, length);
|
||||
*pos+= length;
|
||||
}
|
||||
|
||||
static void setup_one_conversion_function(Item_param *param, uchar param_type)
|
||||
|
||||
#undef get_param_length
|
||||
|
||||
static void setup_one_conversion_function(THD *thd, Item_param *param,
|
||||
uchar param_type)
|
||||
{
|
||||
switch (param_type) {
|
||||
case FIELD_TYPE_TINY:
|
||||
case MYSQL_TYPE_TINY:
|
||||
param->set_param_func= set_param_tiny;
|
||||
param->item_type= Item::INT_ITEM;
|
||||
param->item_result_type= INT_RESULT;
|
||||
break;
|
||||
case FIELD_TYPE_SHORT:
|
||||
case MYSQL_TYPE_SHORT:
|
||||
param->set_param_func= set_param_short;
|
||||
param->item_type= Item::INT_ITEM;
|
||||
param->item_result_type= INT_RESULT;
|
||||
break;
|
||||
case FIELD_TYPE_LONG:
|
||||
case MYSQL_TYPE_LONG:
|
||||
param->set_param_func= set_param_int32;
|
||||
param->item_type= Item::INT_ITEM;
|
||||
param->item_result_type= INT_RESULT;
|
||||
break;
|
||||
case FIELD_TYPE_LONGLONG:
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
param->set_param_func= set_param_int64;
|
||||
param->item_type= Item::INT_ITEM;
|
||||
param->item_result_type= INT_RESULT;
|
||||
break;
|
||||
case FIELD_TYPE_FLOAT:
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
param->set_param_func= set_param_float;
|
||||
param->item_type= Item::REAL_ITEM;
|
||||
param->item_result_type= REAL_RESULT;
|
||||
break;
|
||||
case FIELD_TYPE_DOUBLE:
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
param->set_param_func= set_param_double;
|
||||
param->item_type= Item::REAL_ITEM;
|
||||
param->item_result_type= REAL_RESULT;
|
||||
break;
|
||||
case FIELD_TYPE_TIME:
|
||||
case MYSQL_TYPE_TIME:
|
||||
param->set_param_func= set_param_time;
|
||||
param->item_type= Item::STRING_ITEM;
|
||||
param->item_result_type= STRING_RESULT;
|
||||
break;
|
||||
case FIELD_TYPE_DATE:
|
||||
case MYSQL_TYPE_DATE:
|
||||
param->set_param_func= set_param_date;
|
||||
param->item_type= Item::STRING_ITEM;
|
||||
param->item_result_type= STRING_RESULT;
|
||||
break;
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
param->set_param_func= set_param_datetime;
|
||||
param->item_type= Item::STRING_ITEM;
|
||||
param->item_result_type= STRING_RESULT;
|
||||
break;
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
param->set_param_func= set_param_str;
|
||||
param->value.cs_info.character_set_client= &my_charset_bin;
|
||||
param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
|
||||
param->item_type= Item::STRING_ITEM;
|
||||
param->item_result_type= STRING_RESULT;
|
||||
break;
|
||||
default:
|
||||
param->set_param_func= set_param_str;
|
||||
param->item_result_type= STRING_RESULT;
|
||||
/*
|
||||
The client library ensures that we won't get any other typecodes
|
||||
except typecodes above and typecodes for string types. Marking
|
||||
label as 'default' lets us to handle malformed packets as well.
|
||||
*/
|
||||
{
|
||||
CHARSET_INFO *fromcs= thd->variables.character_set_client;
|
||||
CHARSET_INFO *tocs= thd->variables.collation_connection;
|
||||
uint32 dummy_offset;
|
||||
|
||||
param->value.cs_info.character_set_client= fromcs;
|
||||
|
||||
/*
|
||||
Setup source and destination character sets so that they
|
||||
are different only if conversion is necessary: this will
|
||||
make later checks easier.
|
||||
*/
|
||||
param->value.cs_info.final_character_set_of_str_value=
|
||||
String::needs_conversion(0, fromcs, tocs, &dummy_offset) ?
|
||||
tocs : fromcs;
|
||||
param->set_param_func= set_param_str;
|
||||
/*
|
||||
Exact value of max_length is not known unless data is converted to
|
||||
charset of connection, so we have to set it later.
|
||||
*/
|
||||
param->item_type= Item::STRING_ITEM;
|
||||
param->item_result_type= STRING_RESULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,23 +606,21 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
if (param->long_data_supplied)
|
||||
res= param->query_val_str(&str);
|
||||
else
|
||||
if (param->state != Item_param::LONG_DATA_VALUE)
|
||||
{
|
||||
if (is_param_null(null_array, it - begin))
|
||||
{
|
||||
param->set_null();
|
||||
res= &my_null_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (read_pos >= data_end)
|
||||
DBUG_RETURN(1);
|
||||
param->set_param_func(param, &read_pos, data_end - read_pos);
|
||||
res= param->query_val_str(&str);
|
||||
}
|
||||
}
|
||||
res= param->query_val_str(&str);
|
||||
if (param->convert_str_value(thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
|
||||
if (query.replace(param->pos_in_query+length, 1, *res))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
@ -571,7 +644,7 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array,
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
if (!param->long_data_supplied)
|
||||
if (param->state != Item_param::LONG_DATA_VALUE)
|
||||
{
|
||||
if (is_param_null(null_array, it - begin))
|
||||
param->set_null();
|
||||
@ -582,6 +655,8 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array,
|
||||
param->set_param_func(param, &read_pos, data_end - read_pos);
|
||||
}
|
||||
}
|
||||
if (param->convert_str_value(stmt->thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
@ -603,6 +678,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
|
||||
*/
|
||||
Item_param **it= stmt->param_array;
|
||||
Item_param **end= it + stmt->param_count;
|
||||
THD *thd= stmt->thd;
|
||||
for (; it < end; ++it)
|
||||
{
|
||||
ushort typecode;
|
||||
@ -614,7 +690,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
|
||||
typecode= sint2korr(read_pos);
|
||||
read_pos+= 2;
|
||||
(**it).unsigned_flag= test(typecode & signed_bit);
|
||||
setup_one_conversion_function(*it, (uchar) (typecode & ~signed_bit));
|
||||
setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit));
|
||||
}
|
||||
}
|
||||
*data= read_pos;
|
||||
@ -625,6 +701,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
|
||||
|
||||
static bool emb_insert_params(Prepared_statement *stmt)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **it= stmt->param_array;
|
||||
Item_param **end= it + stmt->param_count;
|
||||
MYSQL_BIND *client_param= stmt->thd->client_params;
|
||||
@ -634,21 +711,22 @@ static bool emb_insert_params(Prepared_statement *stmt)
|
||||
for (; it < end; ++it, ++client_param)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
setup_one_conversion_function(param, client_param->buffer_type);
|
||||
param->unsigned_flag= client_param->is_unsigned;
|
||||
if (!param->long_data_supplied)
|
||||
setup_one_conversion_function(thd, param, client_param->buffer_type);
|
||||
if (param->state != Item_param::LONG_DATA_VALUE)
|
||||
{
|
||||
if (*client_param->is_null)
|
||||
param->set_null();
|
||||
else
|
||||
{
|
||||
uchar *buff= (uchar*)client_param->buffer;
|
||||
uchar *buff= (uchar*) client_param->buffer;
|
||||
param->set_param_func(param, &buff,
|
||||
client_param->length ?
|
||||
*client_param->length :
|
||||
client_param->buffer_length);
|
||||
}
|
||||
}
|
||||
if (param->convert_str_value(thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
@ -673,25 +751,22 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
|
||||
for (; it < end; ++it, ++client_param)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
setup_one_conversion_function(param, client_param->buffer_type);
|
||||
if (param->long_data_supplied)
|
||||
res= param->query_val_str(&str);
|
||||
else
|
||||
setup_one_conversion_function(thd, param, client_param->buffer_type);
|
||||
if (param->state != Item_param::LONG_DATA_VALUE)
|
||||
{
|
||||
if (*client_param->is_null)
|
||||
{
|
||||
param->set_null();
|
||||
res= &my_null_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
uchar *buff= (uchar*)client_param->buffer;
|
||||
uchar *buff= (uchar*)client_param->buffer;
|
||||
param->set_param_func(param, &buff,
|
||||
client_param->length ?
|
||||
*client_param->length :
|
||||
client_param->buffer_length);
|
||||
res= param->query_val_str(&str);
|
||||
}
|
||||
res= param->query_val_str(&str);
|
||||
if (param->convert_str_value(thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
}
|
||||
if (query.replace(param->pos_in_query+length, 1, *res))
|
||||
DBUG_RETURN(1);
|
||||
@ -736,7 +811,7 @@ static int mysql_test_insert(Prepared_statement *stmt,
|
||||
TABLE_LIST *insert_table_list=
|
||||
(TABLE_LIST*) lex->select_lex.table_list.first;
|
||||
my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
|
||||
DBUG_ENTER("mysql_test_insert_fields");
|
||||
DBUG_ENTER("mysql_test_insert");
|
||||
|
||||
if ((res= insert_precheck(thd, table_list, update)))
|
||||
DBUG_RETURN(res);
|
||||
@ -792,7 +867,7 @@ error:
|
||||
Validate UPDATE statement
|
||||
|
||||
SYNOPSIS
|
||||
mysql_test_delete()
|
||||
mysql_test_update()
|
||||
stmt prepared statemen handler
|
||||
tables list of tables queries
|
||||
|
||||
@ -1105,7 +1180,7 @@ end:
|
||||
|
||||
|
||||
/*
|
||||
Validate and prepare for execution CRETE TABLE statement
|
||||
Validate and prepare for execution CREATE TABLE statement
|
||||
|
||||
SYNOPSIS
|
||||
mysql_test_create_table()
|
||||
@ -1142,7 +1217,7 @@ static int mysql_test_create_table(Prepared_statement *stmt,
|
||||
|
||||
|
||||
/*
|
||||
Validate and prepare for execution multy update statement
|
||||
Validate and prepare for execution multi update statement
|
||||
|
||||
SYNOPSIS
|
||||
mysql_test_multiupdate()
|
||||
@ -1165,7 +1240,7 @@ static int mysql_test_multiupdate(Prepared_statement *stmt,
|
||||
|
||||
|
||||
/*
|
||||
Validate and prepare for execution multy delete statement
|
||||
Validate and prepare for execution multi delete statement
|
||||
|
||||
SYNOPSIS
|
||||
mysql_test_multidelete()
|
||||
@ -1337,8 +1412,8 @@ error:
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize array of parametes in statement from LEX.
|
||||
(We need to have quick access to items by number in mysql_send_longdata).
|
||||
Initialize array of parameters in statement from LEX.
|
||||
(We need to have quick access to items by number in mysql_stmt_get_longdata).
|
||||
This is to avoid using malloc/realloc in the parser.
|
||||
*/
|
||||
|
||||
@ -1540,7 +1615,6 @@ static void reset_stmt_params(Prepared_statement *stmt)
|
||||
mysql_stmt_execute()
|
||||
*/
|
||||
|
||||
|
||||
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
{
|
||||
ulong stmt_id= uint4korr(packet);
|
||||
@ -1552,7 +1626,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
|
||||
packet+= 9; /* stmt_id + 5 bytes of flags */
|
||||
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "execute", SEND_ERROR)))
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute",
|
||||
SEND_ERROR)))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
DBUG_PRINT("exec_query:", ("%s", stmt->query));
|
||||
@ -1606,7 +1681,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
reset_stmt_params(stmt);
|
||||
close_thread_tables(thd); // to close derived tables
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
/*
|
||||
/*
|
||||
Free Items that were created during this execution of the PS by query
|
||||
optimizer.
|
||||
*/
|
||||
@ -1616,7 +1691,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
set_params_data_err:
|
||||
reset_stmt_params(stmt);
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
|
||||
send_error(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
@ -1647,7 +1722,8 @@ void mysql_stmt_reset(THD *thd, char *packet)
|
||||
|
||||
DBUG_ENTER("mysql_stmt_reset");
|
||||
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "reset", SEND_ERROR)))
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset",
|
||||
SEND_ERROR)))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
stmt->get_longdata_error= 0;
|
||||
@ -1677,7 +1753,8 @@ void mysql_stmt_free(THD *thd, char *packet)
|
||||
|
||||
DBUG_ENTER("mysql_stmt_free");
|
||||
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "close", DONT_SEND_ERROR)))
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close",
|
||||
DONT_SEND_ERROR)))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
/* Statement map deletes statement on erase */
|
||||
@ -1705,43 +1782,50 @@ void mysql_stmt_free(THD *thd, char *packet)
|
||||
to the server. (No checking that we get a 'end of column' in the server)
|
||||
*/
|
||||
|
||||
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
|
||||
void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
|
||||
{
|
||||
ulong stmt_id;
|
||||
uint param_number;
|
||||
Prepared_statement *stmt;
|
||||
Item_param *param;
|
||||
char *packet_end= packet + packet_length - 1;
|
||||
|
||||
DBUG_ENTER("mysql_stmt_get_longdata");
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
/* The following should never happen */
|
||||
if (packet_length < MYSQL_LONG_DATA_HEADER+1)
|
||||
/* Minimal size of long data packet is 6 bytes */
|
||||
if ((ulong) (packet_end - packet) < MYSQL_LONG_DATA_HEADER)
|
||||
{
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "get_longdata");
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_send_long_data");
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
#endif
|
||||
|
||||
ulong stmt_id= uint4korr(pos);
|
||||
uint param_number= uint2korr(pos+4);
|
||||
stmt_id= uint4korr(packet);
|
||||
packet+= 4;
|
||||
|
||||
if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata",
|
||||
if (!(stmt=find_prepared_statement(thd, stmt_id, "mysql_stmt_send_long_data",
|
||||
DONT_SEND_ERROR)))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
param_number= uint2korr(packet);
|
||||
packet+= 2;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (param_number >= stmt->param_count)
|
||||
{
|
||||
/* Error will be sent in execute call */
|
||||
stmt->get_longdata_error= 1;
|
||||
stmt->last_errno= ER_WRONG_ARGUMENTS;
|
||||
sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata");
|
||||
sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
|
||||
"mysql_stmt_send_long_data");
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
pos+= MYSQL_LONG_DATA_HEADER; // Point to data
|
||||
#endif
|
||||
|
||||
Item_param *param= stmt->param_array[param_number];
|
||||
param= stmt->param_array[param_number];
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1);
|
||||
param->set_longdata(packet, (ulong) (packet_end - packet));
|
||||
#else
|
||||
param->set_longdata(thd->extra_data, thd->extra_length);
|
||||
#endif
|
||||
@ -1755,13 +1839,11 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
||||
param_array(0),
|
||||
param_count(0),
|
||||
last_errno(0),
|
||||
get_longdata_error(0),
|
||||
log_full_query(0)
|
||||
get_longdata_error(0)
|
||||
{
|
||||
*last_error= '\0';
|
||||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
log_full_query= 1;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
set_params= insert_params_withlog;
|
||||
#else
|
||||
|
@ -3044,12 +3044,12 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
|
||||
best_record_count=current_record_count;
|
||||
best_read_time=current_read_time;
|
||||
}
|
||||
swap(JOIN_TAB*,join->best_ref[idx],*pos);
|
||||
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
|
||||
find_best(join,rest_tables & ~real_table_bit,idx+1,
|
||||
current_record_count,current_read_time);
|
||||
if (thd->killed)
|
||||
return;
|
||||
swap(JOIN_TAB*,join->best_ref[idx],*pos);
|
||||
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
|
||||
}
|
||||
if (join->select_options & SELECT_STRAIGHT_JOIN)
|
||||
break; // Don't test all combinations
|
||||
|
@ -450,22 +450,25 @@ bool String::append(const char *s,uint32 arg_length)
|
||||
|
||||
bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
|
||||
{
|
||||
if (!arg_length) // Default argument
|
||||
if (!(arg_length= (uint32) strlen(s)))
|
||||
uint32 dummy_offset;
|
||||
uint32 add_length;
|
||||
|
||||
if (!arg_length && !(arg_length= (uint32)strlen(s)))
|
||||
return FALSE;
|
||||
if (cs != str_charset && str_charset->mbmaxlen > 1)
|
||||
|
||||
add_length= arg_length * str_charset->mbmaxlen;
|
||||
if (realloc(str_length + add_length))
|
||||
return TRUE;
|
||||
if (needs_conversion(arg_length, cs, str_charset, &dummy_offset))
|
||||
{
|
||||
uint32 add_length=arg_length * str_charset->mbmaxlen;
|
||||
if (realloc(str_length+ add_length))
|
||||
return TRUE;
|
||||
str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset,
|
||||
s, arg_length, cs);
|
||||
return FALSE;
|
||||
}
|
||||
if (realloc(str_length+arg_length))
|
||||
return TRUE;
|
||||
memcpy(Ptr+str_length,s,arg_length);
|
||||
str_length+=arg_length;
|
||||
else
|
||||
{
|
||||
memcpy(Ptr + str_length, s, arg_length);
|
||||
str_length+= arg_length;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -858,3 +861,23 @@ void String::print(String *str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Exchange state of this object and argument.
|
||||
|
||||
SYNOPSIS
|
||||
String::swap()
|
||||
|
||||
RETURN
|
||||
Target string will contain state of this object and vice versa.
|
||||
*/
|
||||
|
||||
void String::swap(String &s)
|
||||
{
|
||||
swap_variables(char *, Ptr, s.Ptr);
|
||||
swap_variables(uint32, str_length, s.str_length);
|
||||
swap_variables(uint32, Alloced_length, s.Alloced_length);
|
||||
swap_variables(bool, alloced, s.alloced);
|
||||
swap_variables(CHARSET_INFO*, str_charset, s.str_charset);
|
||||
}
|
||||
|
@ -299,4 +299,7 @@ public:
|
||||
return FALSE;
|
||||
}
|
||||
void print(String *print);
|
||||
|
||||
/* Swap two string objects. Efficient way to exchange data without memcpy. */
|
||||
void swap(String &s);
|
||||
};
|
||||
|
122
sql/time.cc
122
sql/time.cc
@ -1255,9 +1255,15 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
|
||||
MySQL doesn't support comparing of date/time/datetime strings that
|
||||
are not in arbutary order as dates are compared as strings in some
|
||||
context)
|
||||
This functions don't check that given TIME structure members are
|
||||
in valid range. If they are not, return value won't reflect any
|
||||
valid date either. Additionally, make_time doesn't take into
|
||||
account time->day member: it's assumed that days have been converted
|
||||
to hours already.
|
||||
****************************************************************************/
|
||||
|
||||
void make_time(DATE_TIME_FORMAT *format, TIME *l_time, String *str)
|
||||
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
|
||||
const TIME *l_time, String *str)
|
||||
{
|
||||
long length= my_sprintf((char*) str->ptr(),
|
||||
((char*) str->ptr(),
|
||||
@ -1271,7 +1277,8 @@ void make_time(DATE_TIME_FORMAT *format, TIME *l_time, String *str)
|
||||
}
|
||||
|
||||
|
||||
void make_date(DATE_TIME_FORMAT *format, TIME *l_time, String *str)
|
||||
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
|
||||
const TIME *l_time, String *str)
|
||||
{
|
||||
long length= my_sprintf((char*) str->ptr(),
|
||||
((char*) str->ptr(),
|
||||
@ -1284,7 +1291,8 @@ void make_date(DATE_TIME_FORMAT *format, TIME *l_time, String *str)
|
||||
}
|
||||
|
||||
|
||||
void make_datetime(DATE_TIME_FORMAT *format, TIME *l_time, String *str)
|
||||
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
|
||||
const TIME *l_time, String *str)
|
||||
{
|
||||
long length= my_sprintf((char*) str->ptr(),
|
||||
((char*) str->ptr(),
|
||||
@ -1330,3 +1338,111 @@ void make_truncated_value_warning(THD *thd, const char *str_val,
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_TRUNCATED_WRONG_VALUE, warn_buff);
|
||||
}
|
||||
|
||||
|
||||
/* 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 TIMESTAMP_DATETIME:
|
||||
return TIME_to_ulonglong_datetime(time);
|
||||
case TIMESTAMP_DATE:
|
||||
return TIME_to_ulonglong_date(time);
|
||||
case TIMESTAMP_TIME:
|
||||
return TIME_to_ulonglong_time(time);
|
||||
case TIMESTAMP_NONE:
|
||||
case TIMESTAMP_DATETIME_ERROR:
|
||||
return ULL(0);
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Convert struct DATE/TIME/DATETIME value to string using built-in
|
||||
MySQL time conversion formats.
|
||||
|
||||
SYNOPSIS
|
||||
TIME_to_string()
|
||||
|
||||
NOTE
|
||||
The string must have at least MAX_DATE_REP_LENGTH bytes reserved.
|
||||
*/
|
||||
|
||||
void TIME_to_string(const TIME *time, String *str)
|
||||
{
|
||||
switch (time->time_type) {
|
||||
case TIMESTAMP_DATETIME:
|
||||
make_datetime((DATE_TIME_FORMAT*) 0, time, str);
|
||||
break;
|
||||
case TIMESTAMP_DATE:
|
||||
make_date((DATE_TIME_FORMAT*) 0, time, str);
|
||||
break;
|
||||
case TIMESTAMP_TIME:
|
||||
make_time((DATE_TIME_FORMAT*) 0, time, str);
|
||||
break;
|
||||
case TIMESTAMP_NONE:
|
||||
case TIMESTAMP_DATETIME_ERROR:
|
||||
str->length(0);
|
||||
str->set_charset(&my_charset_bin);
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
@ -5622,12 +5622,22 @@ static void test_bind_date_conv(uint row_count)
|
||||
{
|
||||
tm[i].neg= 0;
|
||||
tm[i].second_part= second_part+count;
|
||||
tm[i].year= year+count;
|
||||
tm[i].month= month+count;
|
||||
tm[i].day= day+count;
|
||||
tm[i].hour= hour+count;
|
||||
tm[i].minute= minute+count;
|
||||
tm[i].second= sec+count;
|
||||
if (bind[i].buffer_type != MYSQL_TYPE_TIME)
|
||||
{
|
||||
tm[i].year= year+count;
|
||||
tm[i].month= month+count;
|
||||
tm[i].day= day+count;
|
||||
}
|
||||
else
|
||||
tm[i].year= tm[i].month= tm[i].day= 0;
|
||||
if (bind[i].buffer_type != MYSQL_TYPE_DATE)
|
||||
{
|
||||
tm[i].hour= hour+count;
|
||||
tm[i].minute= minute+count;
|
||||
tm[i].second= sec+count;
|
||||
}
|
||||
else
|
||||
tm[i].hour= tm[i].minute= tm[i].second = 0;
|
||||
}
|
||||
rc = mysql_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
@ -9455,6 +9465,7 @@ select col1 FROM t1 where col1=2");
|
||||
myquery(rc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This tests for various mysql_send_long_data bugs described in #1664
|
||||
*/
|
||||
@ -9681,6 +9692,189 @@ static void test_union_param()
|
||||
|
||||
mysql_stmt_close(stmt);
|
||||
}
|
||||
|
||||
|
||||
static void test_ps_i18n()
|
||||
{
|
||||
MYSQL_STMT *stmt;
|
||||
int rc;
|
||||
const char *stmt_text;
|
||||
MYSQL_BIND bind_array[2];
|
||||
|
||||
const char *koi8= "îÕ, ÚÁ ÒÙÂÁÌËÕ";
|
||||
const char *cp1251= "Íó, çà ðûáàëêó";
|
||||
char buf1[16], buf2[16];
|
||||
ulong buf1_len, buf2_len;
|
||||
|
||||
|
||||
myheader("test_ps_i18n");
|
||||
|
||||
stmt_text= "DROP TABLE IF EXISTS t1";
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
|
||||
myquery(rc);
|
||||
|
||||
/*
|
||||
Create table with binary columns, set session character set to cp1251,
|
||||
client character set to koi8, and make sure that there is conversion
|
||||
on insert and no conversion on select
|
||||
*/
|
||||
|
||||
stmt_text= "CREATE TABLE t1 (c1 VARBINARY(255), c2 VARBINARY(255))";
|
||||
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
|
||||
myquery(rc);
|
||||
|
||||
stmt_text= "SET CHARACTER_SET_CLIENT=koi8r, "
|
||||
"CHARACTER_SET_CONNECTION=cp1251, "
|
||||
"CHARACTER_SET_RESULTS=koi8r";
|
||||
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
|
||||
myquery(rc);
|
||||
|
||||
bzero(bind_array, sizeof(bind_array));
|
||||
|
||||
bind_array[0].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind_array[0].buffer= (char*) koi8;
|
||||
bind_array[0].buffer_length= strlen(koi8);
|
||||
|
||||
bind_array[1].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind_array[1].buffer= (char*) koi8;
|
||||
bind_array[1].buffer_length= strlen(koi8);
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
check_stmt(stmt);
|
||||
|
||||
stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)";
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||
check_execute(stmt, rc);
|
||||
|
||||
mysql_stmt_bind_param(stmt, bind_array);
|
||||
|
||||
mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8));
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
stmt_text= "SELECT c1, c2 FROM t1";
|
||||
|
||||
/* c1 and c2 are binary so no conversion will be done on select */
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||
check_execute(stmt, rc);
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
bind_array[0].buffer= buf1;
|
||||
bind_array[0].buffer_length= sizeof(buf1);
|
||||
bind_array[0].length= &buf1_len;
|
||||
|
||||
bind_array[1].buffer= buf2;
|
||||
bind_array[1].buffer_length= sizeof(buf2);
|
||||
bind_array[1].length= &buf2_len;
|
||||
|
||||
mysql_stmt_bind_result(stmt, bind_array);
|
||||
|
||||
rc= mysql_stmt_fetch(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
assert(buf1_len == strlen(cp1251));
|
||||
assert(buf2_len == strlen(cp1251));
|
||||
assert(!memcmp(buf1, cp1251, buf1_len));
|
||||
assert(!memcmp(buf2, cp1251, buf1_len));
|
||||
|
||||
rc= mysql_stmt_fetch(stmt);
|
||||
assert(rc == MYSQL_NO_DATA);
|
||||
|
||||
stmt_text= "DROP TABLE IF EXISTS t1";
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
|
||||
myquery(rc);
|
||||
|
||||
/*
|
||||
Now create table with two cp1251 columns, set client character
|
||||
set to koi8 and supply columns of one row as string and another as
|
||||
binary data. Binary data must not be converted on insert, and both
|
||||
columns must be converted to client character set on select.
|
||||
*/
|
||||
|
||||
stmt_text= "CREATE TABLE t1 (c1 VARCHAR(255) CHARACTER SET cp1251, "
|
||||
"c2 VARCHAR(255) CHARACTER SET cp1251)";
|
||||
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
|
||||
myquery(rc);
|
||||
|
||||
stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)";
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||
check_execute(stmt, rc);
|
||||
|
||||
/* this data must be converted */
|
||||
bind_array[0].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind_array[0].buffer= (char*) koi8;
|
||||
bind_array[0].buffer_length= strlen(koi8);
|
||||
|
||||
bind_array[1].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind_array[1].buffer= (char*) koi8;
|
||||
bind_array[1].buffer_length= strlen(koi8);
|
||||
|
||||
mysql_stmt_bind_param(stmt, bind_array);
|
||||
|
||||
mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8));
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
/* this data must not be converted */
|
||||
bind_array[0].buffer_type= MYSQL_TYPE_BLOB;
|
||||
bind_array[0].buffer= (char*) cp1251;
|
||||
bind_array[0].buffer_length= strlen(cp1251);
|
||||
|
||||
bind_array[1].buffer_type= MYSQL_TYPE_BLOB;
|
||||
bind_array[1].buffer= (char*) cp1251;
|
||||
bind_array[1].buffer_length= strlen(cp1251);
|
||||
|
||||
mysql_stmt_bind_param(stmt, bind_array);
|
||||
|
||||
mysql_stmt_send_long_data(stmt, 0, cp1251, strlen(cp1251));
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
/* Fetch data and verify that rows are in koi8 */
|
||||
|
||||
stmt_text= "SELECT c1, c2 FROM t1";
|
||||
|
||||
/* c1 and c2 are binary so no conversion will be done on select */
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||
check_execute(stmt, rc);
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
bind_array[0].buffer= buf1;
|
||||
bind_array[0].buffer_length= sizeof(buf1);
|
||||
bind_array[0].length= &buf1_len;
|
||||
|
||||
bind_array[1].buffer= buf2;
|
||||
bind_array[1].buffer_length= sizeof(buf2);
|
||||
bind_array[1].length= &buf2_len;
|
||||
|
||||
mysql_stmt_bind_result(stmt, bind_array);
|
||||
|
||||
while ((rc= mysql_stmt_fetch(stmt)) == 0)
|
||||
{
|
||||
assert(buf1_len == strlen(koi8));
|
||||
assert(buf2_len == strlen(koi8));
|
||||
assert(!memcmp(buf1, koi8, buf1_len));
|
||||
assert(!memcmp(buf2, koi8, buf1_len));
|
||||
}
|
||||
assert(rc == MYSQL_NO_DATA);
|
||||
mysql_stmt_close(stmt);
|
||||
|
||||
stmt_text= "DROP TABLE t1";
|
||||
mysql_real_query(mysql, stmt_text, strlen(stmt_text));
|
||||
}
|
||||
|
||||
/*
|
||||
Read and parse arguments and MySQL options from my.cnf
|
||||
*/
|
||||
@ -9825,7 +10019,6 @@ int main(int argc, char **argv)
|
||||
|
||||
start_time= time((time_t *)0);
|
||||
|
||||
test_union_param();
|
||||
client_query(); /* simple client query test */
|
||||
#if NOT_YET_WORKING
|
||||
/* Used for internal new development debugging */
|
||||
@ -9967,8 +10160,10 @@ int main(int argc, char **argv)
|
||||
test_union2(); /* repeatable execution of union (Bug #3577) */
|
||||
test_bug1664(); /* test for bugs in mysql_stmt_send_long_data()
|
||||
call (Bug #1664) */
|
||||
test_union_param();
|
||||
test_order_param(); /* ORDER BY with parameters in select list
|
||||
(Bug #3686 */
|
||||
test_ps_i18n(); /* test for i18n support in binary protocol */
|
||||
|
||||
end_time= time((time_t *)0);
|
||||
total_time+= difftime(end_time, start_time);
|
||||
|
Loading…
x
Reference in New Issue
Block a user