summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_global.h2
-rw-r--r--include/my_sys.h2
-rw-r--r--include/mysql_com.h2
-rw-r--r--isam/_search.c8
-rw-r--r--isam/test2.c2
-rw-r--r--libmysql/libmysql.c109
-rw-r--r--myisam/mi_test2.c2
-rw-r--r--mysys/charset.c58
-rw-r--r--mysys/my_chsize.c2
-rw-r--r--mysys/my_compress.c2
-rw-r--r--mysys/my_handler.c8
-rw-r--r--sql/field.cc29
-rw-r--r--sql/item.cc421
-rw-r--r--sql/item.h72
-rw-r--r--sql/item_timefunc.cc77
-rw-r--r--sql/lock.cc4
-rw-r--r--sql/mysql_priv.h22
-rw-r--r--sql/opt_range.cc6
-rw-r--r--sql/protocol.cc58
-rw-r--r--sql/protocol.h4
-rw-r--r--sql/sql_class.cc33
-rw-r--r--sql/sql_class.h4
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_parse.cc5
-rw-r--r--sql/sql_prepare.cc272
-rw-r--r--sql/sql_select.cc4
-rw-r--r--sql/sql_string.cc45
-rw-r--r--sql/sql_string.h3
-rw-r--r--sql/time.cc122
-rw-r--r--tests/client_test.c209
30 files changed, 1060 insertions, 529 deletions
diff --git a/include/my_global.h b/include/my_global.h
index e7d924eb0ca..6916ea61e19 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -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)
diff --git a/include/my_sys.h b/include/my_sys.h
index 8beaa00eb16..922e6d8cff4 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -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 */
diff --git a/include/mysql_com.h b/include/mysql_com.h
index ada2bd1f679..a1da896af38 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -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
diff --git a/isam/_search.c b/isam/_search.c
index 7b61abfb46b..fbffd6786e1 100644
--- a/isam/_search.c
+++ b/isam/_search.c
@@ -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
diff --git a/isam/test2.c b/isam/test2.c
index 14a40f39865..4b22f2d679c 100644
--- a/isam/test2.c
+++ b/isam/test2.c
@@ -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,
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 2214ca68c81..b8ede73a0c5 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -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);
diff --git a/myisam/mi_test2.c b/myisam/mi_test2.c
index 56d8357536a..95c8ce56a13 100644
--- a/myisam/mi_test2.c
+++ b/myisam/mi_test2.c
@@ -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);
diff --git a/mysys/charset.c b/mysys/charset.c
index e58c851cf7c..c80b00db397 100644
--- a/mysys/charset.c
+++ b/mysys/charset.c
@@ -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);
+}
diff --git a/mysys/my_chsize.c b/mysys/my_chsize.c
index 653ea569172..cf26428d65f 100644
--- a/mysys/my_chsize.c
+++ b/mysys/my_chsize.c
@@ -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
diff --git a/mysys/my_compress.c b/mysys/my_compress.c
index dd076311188..0e37d2fef9b 100644
--- a/mysys/my_compress.c
+++ b/mysys/my_compress.c
@@ -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;
}
diff --git a/mysys/my_handler.c b/mysys/my_handler.c
index 35f620ccbcb..de0fba56d21 100644
--- a/mysys/my_handler.c
+++ b/mysys/my_handler.c
@@ -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
diff --git a/sql/field.cc b/sql/field.cc
index 944f18080f6..df9b4f84ae7 100644
--- a/sql/field.cc
+++ b/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);
}
diff --git a/sql/item.cc b/sql/item.cc
index 700d9482815..f3a13411fe3 100644
--- a/sql/item.cc
+++ b/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;
+ state= TIME_VALUE;
+ maybe_null= 0;
+ max_length= max_length_arg;
+ decimals= 0;
+ DBUG_VOID_RETURN;
+}
- ltime.time_type= type;
-
- item_is_time= TRUE;
- item_type= STRING_ITEM;
- 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;
- 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 and decimals are set after charset conversion */
+ /* sic: str may be not null-terminated, don't add DBUG_PRINT here */
+ DBUG_RETURN(FALSE);
}
-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_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(&ltime, 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;
+ }
+ case NULL_VALUE:
+ return NULL;
default:
- return (String*) &str_value;
+ 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);
- default:
- str->set("'", 1, default_charset());
-
- if (!item_is_time)
+ 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:
{
- 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;
- }
- }
+ 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;
}
- else
+ case STRING_VALUE:
+ case LONG_DATA_VALUE:
{
- 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, &ltime, &tmp);
- break;
- case TIMESTAMP_DATETIME:
- make_datetime((DATE_TIME_FORMAT*) 0, &ltime, &tmp);
- break;
- case TIMESTAMP_TIME:
- make_time((DATE_TIME_FORMAT*) 0, &ltime, &tmp);
- break;
- }
- str->append(tmp);
+ 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;
}
- str->append('\'');
+ case NULL_VALUE:
+ return &my_null_string;
+ default:
+ 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 */
diff --git a/sql/item.h b/sql/item.h
index 99a0516e439..ccf8e8685d0 100644
--- a/sql/item.h
+++ b/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*);
+ bool get_time(TIME *tm);
int save_in_field(Field *field, bool no_conversions);
+
void set_null();
- void set_int(longlong i);
+ void set_int(longlong i, uint32 max_length_arg);
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);
+ 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('?'); }
};
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index e8848243812..45d66addc9f 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -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(&ltime);
}
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(&ltime);
make_time((DATE_TIME_FORMAT *) 0, &ltime, &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(&ltime, &start);
ltime.time_type= TIMESTAMP_DATETIME;
+
+ value= (longlong) TIME_to_ulonglong_datetime(&ltime);
make_datetime((DATE_TIME_FORMAT *) 0, &ltime, &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(&ltime, &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(&ltime, &tm_tmp);
+ return (longlong) TIME_to_ulonglong_datetime(&ltime);
}
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(&ltime,1) && !str->alloc(11))
{
- make_date((DATE_TIME_FORMAT *) 0,&ltime, str);
+ make_date((DATE_TIME_FORMAT *) 0, &ltime, str);
return str;
}
diff --git a/sql/lock.cc b/sql/lock.cc
index 923932a768a..ac689495ca3 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -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++;
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index cf609a7da0a..fb9ff5be771 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -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);
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index ddedf4ca3f0..a5d2450e551 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -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
diff --git a/sql/protocol.cc b/sql/protocol.cc
index a5944af829d..44fc4eff9ad 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -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()
diff --git a/sql/protocol.h b/sql/protocol.h
index 17c8f0d321d..41885ec9f1f 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -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); }
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index f7992c3db9e..9d368db0229 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -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' */
@@ -504,6 +504,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
*/
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e74aca1434d..7894cf5fb2c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -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);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 3aa0e9511a7..5032e9c33f0 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -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
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 8ddac68bcdb..7596e37de93 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -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);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index a8e2cabe44b..18437265e0e 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -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;
- default:
+ 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:
+ /*
+ 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
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 6da778f6196..062fcbd1976 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -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
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 8a093738e2b..fb2d1661357 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -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);
+}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 32333b3b381..c24511a9f74 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -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);
};
diff --git a/sql/time.cc b/sql/time.cc
index db05d606292..6d15fa184a1 100644
--- a/sql/time.cc
+++ b/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);
+ }
+}
diff --git a/tests/client_test.c b/tests/client_test.c
index 8225eb17707..725d794c4a5 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -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);