summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/field.h20
-rw-r--r--sql/field_conv.cc163
2 files changed, 104 insertions, 79 deletions
diff --git a/sql/field.h b/sql/field.h
index 73922460037..cc4b79a9e4a 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -209,7 +209,7 @@ public:
DBUG_ENTER("Field::pack_length_from_metadata");
DBUG_RETURN(field_metadata);
}
- virtual uint row_pack_length() { return 0; }
+ virtual uint row_pack_length() const { return 0; }
virtual int save_field_metadata(uchar *first_byte)
{ return do_save_field_metadata(first_byte); }
@@ -733,7 +733,7 @@ public:
int store_decimal(const my_decimal *);
my_decimal *val_decimal(my_decimal *);
uint is_equal(Create_field *new_field);
- uint row_pack_length() { return pack_length(); }
+ uint row_pack_length() const { return pack_length(); }
uint32 pack_length_from_metadata(uint field_metadata) {
uint32 length= pack_length();
DBUG_PRINT("result", ("pack_length_from_metadata(%d): %u",
@@ -923,7 +923,7 @@ public:
uint size_of() const { return sizeof(*this); }
uint32 pack_length() const { return (uint32) bin_size; }
uint pack_length_from_metadata(uint field_metadata);
- uint row_pack_length() { return pack_length(); }
+ uint row_pack_length() const { return pack_length(); }
bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
uint16 mflags, int *order_var);
uint is_equal(Create_field *new_field);
@@ -1195,7 +1195,7 @@ public:
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return sizeof(float); }
- uint row_pack_length() { return pack_length(); }
+ uint row_pack_length() const { return pack_length(); }
void sql_type(String &str) const;
private:
int do_save_field_metadata(uchar *first_byte);
@@ -1235,7 +1235,7 @@ public:
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return sizeof(double); }
- uint row_pack_length() { return pack_length(); }
+ uint row_pack_length() const { return pack_length(); }
void sql_type(String &str) const;
private:
int do_save_field_metadata(uchar *first_byte);
@@ -1621,7 +1621,7 @@ public:
}
bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
uint16 mflags, int *order_var);
- uint row_pack_length() { return field_length; }
+ uint row_pack_length() const { return field_length; }
int pack_cmp(const uchar *a,const uchar *b,uint key_length,
my_bool insert_or_update);
int pack_cmp(const uchar *b,uint key_length,my_bool insert_or_update);
@@ -1671,7 +1671,7 @@ public:
enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
bool match_collation_to_optimize_range() const { return TRUE; }
enum ha_base_keytype key_type() const;
- uint row_pack_length() { return field_length; }
+ uint row_pack_length() const { return field_length; }
bool zero_pack() const { return 0; }
int reset(void) { bzero(ptr,field_length+length_bytes); return 0; }
uint32 pack_length() const { return (uint32) field_length+length_bytes; }
@@ -1797,7 +1797,7 @@ public:
*/
uint32 pack_length_no_ptr() const
{ return (uint32) (packlength); }
- uint row_pack_length() { return pack_length_no_ptr(); }
+ uint row_pack_length() const { return pack_length_no_ptr(); }
uint32 sort_length() const;
virtual uint32 max_data_length() const
{
@@ -1960,7 +1960,7 @@ public:
enum_field_types real_type() const { return MYSQL_TYPE_ENUM; }
uint pack_length_from_metadata(uint field_metadata)
{ return (field_metadata & 0x00ff); }
- uint row_pack_length() { return pack_length(); }
+ uint row_pack_length() const { return pack_length(); }
virtual bool zero_pack() const { return 0; }
bool optimize_range(uint idx, uint part) { return 0; }
bool eq_def(Field *field);
@@ -2081,7 +2081,7 @@ public:
uint32 pack_length() const { return (uint32) (field_length + 7) / 8; }
uint32 pack_length_in_rec() const { return bytes_in_rec; }
uint pack_length_from_metadata(uint field_metadata);
- uint row_pack_length()
+ uint row_pack_length() const
{ return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); }
bool compatible_field_size(uint metadata, Relay_log_info *rli,
uint16 mflags, int *order_var);
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 14c4fd257fe..7eb49b9dd92 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -447,79 +447,99 @@ static void do_expand_string(Copy_field *copy)
copy->to_length-copy->from_length, ' ');
}
+/**
+ Find how many bytes should be copied between Field_varstring fields
+ so that only the bytes in use in the 'from' field are copied.
+ Handles single and multi-byte charsets. Adds warning if not all
+ bytes in 'from' will fit into 'to'.
+
+ @param to Variable length field we're copying to
+ @param from Variable length field we're copying from
-static void do_varstring1(Copy_field *copy)
+ @return Number of bytes that should be copied from 'from' to 'to'.
+*/
+static uint get_varstring_copy_length(Field_varstring *to,
+ const Field_varstring *from)
{
- uint length= (uint) *(uchar*) copy->from_ptr;
- if (length > copy->to_length- 1)
+ CHARSET_INFO * const cs= from->charset();
+ const bool is_multibyte_charset= (cs->mbmaxlen != 1);
+ const uint to_byte_length= to->row_pack_length();
+
+ uint bytes_to_copy;
+ if (from->length_bytes == 1)
+ bytes_to_copy= *from->ptr;
+ else
+ bytes_to_copy= uint2korr(from->ptr);
+
+ if (is_multibyte_charset)
+ {
+ int well_formed_error;
+ const char *from_beg= reinterpret_cast<char*>(from->ptr + from->length_bytes);
+ const uint to_char_length= (to_byte_length) / cs->mbmaxlen;
+ const uint from_byte_length= bytes_to_copy;
+ bytes_to_copy=
+ cs->cset->well_formed_len(cs, from_beg,
+ from_beg + from_byte_length,
+ to_char_length,
+ &well_formed_error);
+ if (bytes_to_copy < from_byte_length)
+ {
+ if (from->table->in_use->count_cuted_fields)
+ to->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ }
+ else
{
- length=copy->to_length - 1;
- if (copy->from_field->table->in_use->count_cuted_fields)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_DATA_TRUNCATED, 1);
+ if (bytes_to_copy > (to_byte_length))
+ {
+ bytes_to_copy= to_byte_length;
+ if (from->table->in_use->count_cuted_fields)
+ to->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
}
- *(uchar*) copy->to_ptr= (uchar) length;
- memcpy(copy->to_ptr+1, copy->from_ptr + 1, length);
+ return bytes_to_copy;
}
+/**
+ A variable length string field consists of:
+ (a) 1 or 2 length bytes, depending on the VARCHAR column definition
+ (b) as many relevant character bytes, as defined in the length byte(s)
+ (c) unused padding up to the full length of the column
-static void do_varstring1_mb(Copy_field *copy)
-{
- int well_formed_error;
- CHARSET_INFO *cs= copy->from_field->charset();
- uint from_length= (uint) *(uchar*) copy->from_ptr;
- const uchar *from_ptr= copy->from_ptr + 1;
- uint to_char_length= (copy->to_length - 1) / cs->mbmaxlen;
- uint length= cs->cset->well_formed_len(cs, (char*) from_ptr,
- (char*) from_ptr + from_length,
- to_char_length, &well_formed_error);
- if (length < from_length)
- {
- if (current_thd->count_cuted_fields)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_DATA_TRUNCATED, 1);
- }
- *copy->to_ptr= (uchar) length;
- memcpy(copy->to_ptr + 1, from_ptr, length);
-}
+ This function only copies (a) and (b)
+ Condition for using this function: to and from must use the same
+ number of bytes for length, i.e: to->length_bytes==from->length_bytes
-static void do_varstring2(Copy_field *copy)
+ @param to Variable length field we're copying to
+ @param from Variable length field we're copying from
+*/
+static void copy_field_varstring(Field_varstring * const to,
+ const Field_varstring * const from)
{
- uint length=uint2korr(copy->from_ptr);
- if (length > copy->to_length- HA_KEY_BLOB_LENGTH)
- {
- length=copy->to_length-HA_KEY_BLOB_LENGTH;
- if (copy->from_field->table->in_use->count_cuted_fields)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_DATA_TRUNCATED, 1);
- }
- int2store(copy->to_ptr,length);
- memcpy(copy->to_ptr+HA_KEY_BLOB_LENGTH, copy->from_ptr + HA_KEY_BLOB_LENGTH,
- length);
-}
+ const uint length_bytes= from->length_bytes;
+ DBUG_ASSERT(length_bytes == to->length_bytes);
+ DBUG_ASSERT(length_bytes == 1 || length_bytes == 2);
+
+ const uint bytes_to_copy= get_varstring_copy_length(to, from);
+ if (length_bytes == 1)
+ *to->ptr= static_cast<uchar>(bytes_to_copy);
+ else
+ int2store(to->ptr, bytes_to_copy);
+ // memcpy should not be used for overlaping memory blocks
+ DBUG_ASSERT(to->ptr != from->ptr);
+ memcpy(to->ptr + length_bytes, from->ptr + length_bytes, bytes_to_copy);
+}
-static void do_varstring2_mb(Copy_field *copy)
+static void do_varstring(Copy_field *copy)
{
- int well_formed_error;
- CHARSET_INFO *cs= copy->from_field->charset();
- uint char_length= (copy->to_length - HA_KEY_BLOB_LENGTH) / cs->mbmaxlen;
- uint from_length= uint2korr(copy->from_ptr);
- const uchar *from_beg= copy->from_ptr + HA_KEY_BLOB_LENGTH;
- uint length= cs->cset->well_formed_len(cs, (char*) from_beg,
- (char*) from_beg + from_length,
- char_length, &well_formed_error);
- if (length < from_length)
- {
- if (current_thd->count_cuted_fields)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_DATA_TRUNCATED, 1);
- }
- int2store(copy->to_ptr, length);
- memcpy(copy->to_ptr+HA_KEY_BLOB_LENGTH, from_beg, length);
+ copy_field_varstring(static_cast<Field_varstring*>(copy->to_field),
+ static_cast<Field_varstring*>(copy->from_field));
}
-
+
/***************************************************************************
** The different functions that fills in a Copy_field class
@@ -711,11 +731,7 @@ Copy_field::get_copy_func(Field *to,Field *from)
((Field_varstring*) from)->length_bytes)
return do_field_string;
else
- return (((Field_varstring*) to)->length_bytes == 1 ?
- (from->charset()->mbmaxlen == 1 ? do_varstring1 :
- do_varstring1_mb) :
- (from->charset()->mbmaxlen == 1 ? do_varstring2 :
- do_varstring2_mb));
+ return do_varstring;
}
else if (to_length < from_length)
return (from->charset()->mbmaxlen == 1 ?
@@ -771,8 +787,20 @@ Copy_field::get_copy_func(Field *to,Field *from)
int field_conv(Field *to,Field *from)
{
if (to->real_type() == from->real_type() &&
- !(to->type() == MYSQL_TYPE_BLOB && to->table->copy_blobs))
+ !(to->type() == MYSQL_TYPE_BLOB && to->table->copy_blobs) &&
+ to->charset() == from->charset())
{
+ if (to->real_type() == MYSQL_TYPE_VARCHAR &&
+ from->real_type() == MYSQL_TYPE_VARCHAR)
+ {
+ Field_varstring *to_vc= static_cast<Field_varstring*>(to);
+ const Field_varstring *from_vc= static_cast<Field_varstring*>(from);
+ if (to_vc->length_bytes == from_vc->length_bytes)
+ {
+ copy_field_varstring(to_vc, from_vc);
+ return 0;
+ }
+ }
if (to->pack_length() == from->pack_length() &&
!(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) &&
to->real_type() != MYSQL_TYPE_ENUM &&
@@ -781,15 +809,12 @@ int field_conv(Field *to,Field *from)
(to->real_type() != MYSQL_TYPE_NEWDECIMAL ||
(to->field_length == from->field_length &&
(((Field_num*)to)->dec == ((Field_num*)from)->dec))) &&
- from->charset() == to->charset() &&
to->table->s->db_low_byte_first == from->table->s->db_low_byte_first &&
(!(to->table->in_use->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) ||
(to->type() != MYSQL_TYPE_DATE &&
to->type() != MYSQL_TYPE_DATETIME)) &&
- (from->real_type() != MYSQL_TYPE_VARCHAR ||
- ((Field_varstring*)from)->length_bytes ==
- ((Field_varstring*)to)->length_bytes))
+ (from->real_type() != MYSQL_TYPE_VARCHAR))
{ // Identical fields
// to->ptr==from->ptr may happen if one does 'UPDATE ... SET x=x'
memmove(to->ptr, from->ptr, to->pack_length());