Skip to content

Commit 68cbabb

Browse files
committed
MDEV-15938 - TINYTEXT CHARACTER SET utf8 COMPRESSED truncates data
Unexpected data truncation may occur when storing data to compressed blob column having multi byte variable length character sets. The reason was incorrect number of characters limit was enforced for blobs.
1 parent 9a84980 commit 68cbabb

File tree

6 files changed

+78
-34
lines changed

6 files changed

+78
-34
lines changed

mysql-test/main/column_compression.result

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,3 +1394,12 @@ SET column_compression_threshold=0;
13941394
INSERT INTO t1 VALUES('a');
13951395
SET column_compression_threshold=DEFAULT;
13961396
DROP TABLE t1;
1397+
#
1398+
# MDEV-15938 - TINYTEXT CHARACTER SET utf8 COMPRESSED truncates data
1399+
#
1400+
CREATE TABLE t1(a TINYTEXT COMPRESSED, b TINYTEXT) CHARACTER SET utf8;
1401+
INSERT INTO t1 VALUES (REPEAT(_latin1'a', 254), REPEAT(_latin1'a', 254));
1402+
SELECT CHAR_LENGTH(a), CHAR_LENGTH(b), LEFT(a, 10), LEFT(b, 10) FROM t1;
1403+
CHAR_LENGTH(a) CHAR_LENGTH(b) LEFT(a, 10) LEFT(b, 10)
1404+
254 254 aaaaaaaaaa aaaaaaaaaa
1405+
DROP TABLE t1;

mysql-test/main/column_compression.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,11 @@ SET column_compression_threshold=0;
112112
INSERT INTO t1 VALUES('a');
113113
SET column_compression_threshold=DEFAULT;
114114
DROP TABLE t1;
115+
116+
--echo #
117+
--echo # MDEV-15938 - TINYTEXT CHARACTER SET utf8 COMPRESSED truncates data
118+
--echo #
119+
CREATE TABLE t1(a TINYTEXT COMPRESSED, b TINYTEXT) CHARACTER SET utf8;
120+
INSERT INTO t1 VALUES (REPEAT(_latin1'a', 254), REPEAT(_latin1'a', 254));
121+
SELECT CHAR_LENGTH(a), CHAR_LENGTH(b), LEFT(a, 10), LEFT(b, 10) FROM t1;
122+
DROP TABLE t1;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# MDEV-15938 - TINYTEXT CHARACTER SET utf8 COMPRESSED truncates data
3+
#
4+
CREATE TABLE t1(a TINYTEXT COMPRESSED, b TINYTEXT) CHARACTER SET utf16;
5+
INSERT INTO t1 VALUES (REPEAT(_latin1'a', 127), REPEAT(_latin1'a', 127));
6+
SELECT CHAR_LENGTH(a), CHAR_LENGTH(b), LEFT(a, 10), LEFT(b, 10) FROM t1;
7+
CHAR_LENGTH(a) CHAR_LENGTH(b) LEFT(a, 10) LEFT(b, 10)
8+
127 127 aaaaaaaaaa aaaaaaaaaa
9+
DROP TABLE t1;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--source include/have_utf16.inc
2+
3+
--echo #
4+
--echo # MDEV-15938 - TINYTEXT CHARACTER SET utf8 COMPRESSED truncates data
5+
--echo #
6+
CREATE TABLE t1(a TINYTEXT COMPRESSED, b TINYTEXT) CHARACTER SET utf16;
7+
INSERT INTO t1 VALUES (REPEAT(_latin1'a', 127), REPEAT(_latin1'a', 127));
8+
SELECT CHAR_LENGTH(a), CHAR_LENGTH(b), LEFT(a, 10), LEFT(b, 10) FROM t1;
9+
DROP TABLE t1;

sql/field.cc

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6968,23 +6968,23 @@ int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
69686968
{
69696969
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
69706970
uint copy_length;
6971-
String_copier copier;
6971+
int rc;
69726972

69736973
/* See the comment for Field_long::store(long long) */
69746974
DBUG_ASSERT(!table || table->in_use == current_thd);
69756975

6976-
copy_length= copier.well_formed_copy(field_charset,
6977-
(char*) ptr, field_length,
6978-
cs, from, length,
6979-
field_length / field_charset->mbmaxlen);
6976+
rc= well_formed_copy_with_check((char*) ptr, field_length,
6977+
cs, from, length,
6978+
field_length / field_charset->mbmaxlen,
6979+
false, &copy_length);
69806980

69816981
/* Append spaces if the string was shorter than the field. */
69826982
if (copy_length < field_length)
69836983
field_charset->cset->fill(field_charset,(char*) ptr+copy_length,
69846984
field_length-copy_length,
69856985
field_charset->pad_char);
69866986

6987-
return check_conversion_status(&copier, from + length, cs, false);
6987+
return rc;
69886988
}
69896989

69906990

@@ -7517,19 +7517,16 @@ int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
75177517
{
75187518
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
75197519
uint copy_length;
7520-
String_copier copier;
7520+
int rc;
75217521

7522-
copy_length= copier.well_formed_copy(field_charset,
7523-
(char*) ptr + length_bytes,
7524-
field_length,
7525-
cs, from, length,
7526-
field_length / field_charset->mbmaxlen);
7527-
if (length_bytes == 1)
7528-
*ptr= (uchar) copy_length;
7529-
else
7530-
int2store(ptr, copy_length);
7522+
rc= well_formed_copy_with_check((char*) get_data(), field_length,
7523+
cs, from, length,
7524+
field_length / field_charset->mbmaxlen,
7525+
true, &copy_length);
75317526

7532-
return check_conversion_status(&copier, from + length, cs, true);
7527+
store_length(copy_length);
7528+
7529+
return rc;
75337530
}
75347531

75357532

@@ -7952,7 +7949,7 @@ void Field_varstring::hash(ulong *nr, ulong *nr2)
79527949

79537950
int Field_longstr::compress(char *to, uint *to_length,
79547951
const char *from, uint length,
7955-
CHARSET_INFO *cs)
7952+
CHARSET_INFO *cs, size_t nchars)
79567953
{
79577954
THD *thd= get_thd();
79587955
char *buf= 0;
@@ -7961,19 +7958,14 @@ int Field_longstr::compress(char *to, uint *to_length,
79617958
if (String::needs_conversion_on_storage(length, cs, field_charset) ||
79627959
*to_length <= length)
79637960
{
7964-
String_copier copier;
7965-
const char *end= from + length;
7966-
79677961
if (!(buf= (char*) my_malloc(*to_length - 1, MYF(MY_WME))))
79687962
{
79697963
*to_length= 0;
79707964
return -1;
79717965
}
79727966

7973-
length= copier.well_formed_copy(field_charset, buf, *to_length - 1,
7974-
cs, from, length,
7975-
(*to_length - 1) / field_charset->mbmaxlen);
7976-
rc= check_conversion_status(&copier, end, cs, true);
7967+
rc= well_formed_copy_with_check(buf, *to_length - 1, cs, from, length,
7968+
nchars, true, &length);
79777969
from= buf;
79787970
}
79797971

@@ -8045,7 +8037,8 @@ int Field_varstring_compressed::store(const char *from, size_t length,
80458037
{
80468038
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
80478039
uint to_length= (uint)MY_MIN(field_length, field_charset->mbmaxlen * length + 1);
8048-
int rc= compress((char*) get_data(), &to_length, from, (uint)length, cs);
8040+
int rc= compress((char*) get_data(), &to_length, from, (uint) length, cs,
8041+
(to_length - 1) / field_charset->mbmaxlen);
80498042
store_length(to_length);
80508043
return rc;
80518044
}
@@ -8174,10 +8167,11 @@ int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
81748167
{
81758168
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
81768169
size_t copy_length, new_length;
8177-
String_copier copier;
8170+
uint copy_len;
81788171
char *tmp;
81798172
char buff[STRING_BUFFER_USUAL_SIZE];
81808173
String tmpstr(buff,sizeof(buff), &my_charset_bin);
8174+
int rc;
81818175

81828176
if (!length)
81838177
{
@@ -8244,13 +8238,13 @@ int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
82448238
bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
82458239
return 0;
82468240
}
8247-
copy_length= copier.well_formed_copy(field_charset,
8248-
(char*) value.ptr(), (uint)new_length,
8249-
cs, from, length);
8250-
Field_blob::store_length(copy_length);
8241+
rc= well_formed_copy_with_check((char*) value.ptr(), (uint) new_length,
8242+
cs, from, length,
8243+
length, true, &copy_len);
8244+
Field_blob::store_length(copy_len);
82518245
bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
82528246

8253-
return check_conversion_status(&copier, from + length, cs, true);
8247+
return rc;
82548248

82558249
oom_error:
82568250
/* Fatal OOM error */
@@ -8664,7 +8658,8 @@ int Field_blob_compressed::store(const char *from, size_t length,
86648658
if (value.alloc(to_length))
86658659
goto oom;
86668660

8667-
rc= compress((char*) value.ptr(), &to_length, tmp.ptr(), (uint) length, cs);
8661+
rc= compress((char*) value.ptr(), &to_length, tmp.ptr(), (uint) length, cs,
8662+
(uint) length);
86688663
set_ptr(to_length, (uchar*) value.ptr());
86698664
return rc;
86708665

sql/field.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1772,13 +1772,27 @@ class Field_longstr :public Field_str
17721772
return report_if_important_data(copier->source_end_pos(),
17731773
end, count_spaces);
17741774
}
1775+
int well_formed_copy_with_check(char *to, size_t to_length,
1776+
CHARSET_INFO *from_cs,
1777+
const char *from, size_t from_length,
1778+
size_t nchars, bool count_spaces,
1779+
uint *copy_length)
1780+
{
1781+
String_copier copier;
1782+
1783+
*copy_length= copier.well_formed_copy(field_charset, to, to_length,
1784+
from_cs, from, from_length,
1785+
nchars);
1786+
1787+
return check_conversion_status(&copier, from + from_length, from_cs, count_spaces);
1788+
}
17751789
bool cmp_to_string_with_same_collation(const Item_bool_func *cond,
17761790
const Item *item) const;
17771791
bool cmp_to_string_with_stricter_collation(const Item_bool_func *cond,
17781792
const Item *item) const;
17791793
int compress(char *to, uint *to_length,
17801794
const char *from, uint length,
1781-
CHARSET_INFO *cs);
1795+
CHARSET_INFO *cs, size_t nchars);
17821796
String *uncompress(String *val_buffer, String *val_ptr,
17831797
const uchar *from, uint from_length);
17841798
public:

0 commit comments

Comments
 (0)