Commit c9e1884c authored by Ramil Kalimullin's avatar Ramil Kalimullin

Fix for bug#19829:make test Failed in mysql_client_test

*with --with-charset=utf8*

Problem: wrong LONG TEXT field length is sent to a client 
when multibyte server character set used.

Fix: always limit field length sent to a client to 2^32,
as we store it in 4 byte slot.

Note: mysql_client_test changed accordingly.


sql/protocol.cc:
  Fix for bug#19829:make test Failed in mysql_client_test 
  *with --with-charset=utf8*
    - limit field length sent to client to UINT_MAX32 as 
      it may exceeds 32 bit slot for LONG TEXT fields if
      thd_charset->mbmaxlen > 1.
tests/mysql_client_test.c:
  Fix for bug#19829:make test Failed in mysql_client_test
  *with --with-charset=utf8*
    - checking field members have in mind that field length
      is limited to UINT_MAX32.
parent b19b8afe
...@@ -616,7 +616,8 @@ bool Protocol::send_fields(List<Item> *list, uint flags) ...@@ -616,7 +616,8 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
else else
{ {
/* With conversion */ /* With conversion */
uint max_char_len; ulonglong max_length;
uint32 field_length;
int2store(pos, thd_charset->number); int2store(pos, thd_charset->number);
/* /*
For TEXT/BLOB columns, field_length describes the maximum data For TEXT/BLOB columns, field_length describes the maximum data
...@@ -627,12 +628,21 @@ bool Protocol::send_fields(List<Item> *list, uint flags) ...@@ -627,12 +628,21 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
char_count * mbmaxlen, where character count is taken from the char_count * mbmaxlen, where character count is taken from the
definition of the column. In other words, the maximum number definition of the column. In other words, the maximum number
of characters here is limited by the column definition. of characters here is limited by the column definition.
When one has a LONG TEXT column with a single-byte
character set, and the connection character set is multi-byte, the
client may get fields longer than UINT_MAX32, due to
<character set column> -> <character set connection> conversion.
In that case column max length does not fit into the 4 bytes
reserved for it in the protocol.
*/ */
max_char_len= (field.type >= (int) MYSQL_TYPE_TINY_BLOB && max_length= (field.type >= MYSQL_TYPE_TINY_BLOB &&
field.type <= (int) MYSQL_TYPE_BLOB) ? field.type <= MYSQL_TYPE_BLOB) ?
field.length / item->collation.collation->mbminlen : field.length / item->collation.collation->mbminlen :
field.length / item->collation.collation->mbmaxlen; field.length / item->collation.collation->mbmaxlen;
int4store(pos+2, max_char_len * thd_charset->mbmaxlen); max_length*= thd_charset->mbmaxlen;
field_length= (max_length > UINT_MAX32) ? UINT_MAX32 : max_length;
int4store(pos + 2, field_length);
} }
pos[6]= field.type; pos[6]= field.type;
int2store(pos+7,field.flags); int2store(pos+7,field.flags);
......
...@@ -714,6 +714,7 @@ static void do_verify_prepare_field(MYSQL_RES *result, ...@@ -714,6 +714,7 @@ static void do_verify_prepare_field(MYSQL_RES *result,
{ {
MYSQL_FIELD *field; MYSQL_FIELD *field;
CHARSET_INFO *cs; CHARSET_INFO *cs;
ulonglong expected_field_length;
if (!(field= mysql_fetch_field_direct(result, no))) if (!(field= mysql_fetch_field_direct(result, no)))
{ {
...@@ -722,6 +723,8 @@ static void do_verify_prepare_field(MYSQL_RES *result, ...@@ -722,6 +723,8 @@ static void do_verify_prepare_field(MYSQL_RES *result,
} }
cs= get_charset(field->charsetnr, 0); cs= get_charset(field->charsetnr, 0);
DIE_UNLESS(cs); DIE_UNLESS(cs);
if ((expected_field_length= length * cs->mbmaxlen) > UINT_MAX32)
expected_field_length= UINT_MAX32;
if (!opt_silent) if (!opt_silent)
{ {
fprintf(stdout, "\n field[%d]:", no); fprintf(stdout, "\n field[%d]:", no);
...@@ -736,8 +739,8 @@ static void do_verify_prepare_field(MYSQL_RES *result, ...@@ -736,8 +739,8 @@ static void do_verify_prepare_field(MYSQL_RES *result,
fprintf(stdout, "\n org_table:`%s`\t(expected: `%s`)", fprintf(stdout, "\n org_table:`%s`\t(expected: `%s`)",
field->org_table, org_table); field->org_table, org_table);
fprintf(stdout, "\n database :`%s`\t(expected: `%s`)", field->db, db); fprintf(stdout, "\n database :`%s`\t(expected: `%s`)", field->db, db);
fprintf(stdout, "\n length :`%lu`\t(expected: `%lu`)", fprintf(stdout, "\n length :`%lu`\t(expected: `%llu`)",
field->length, length * cs->mbmaxlen); field->length, expected_field_length);
fprintf(stdout, "\n maxlength:`%ld`", field->max_length); fprintf(stdout, "\n maxlength:`%ld`", field->max_length);
fprintf(stdout, "\n charsetnr:`%d`", field->charsetnr); fprintf(stdout, "\n charsetnr:`%d`", field->charsetnr);
fprintf(stdout, "\n default :`%s`\t(expected: `%s`)", fprintf(stdout, "\n default :`%s`\t(expected: `%s`)",
...@@ -773,11 +776,11 @@ static void do_verify_prepare_field(MYSQL_RES *result, ...@@ -773,11 +776,11 @@ static void do_verify_prepare_field(MYSQL_RES *result,
as utf8. Field length is calculated as number of characters * maximum as utf8. Field length is calculated as number of characters * maximum
number of bytes a character can occupy. number of bytes a character can occupy.
*/ */
if (length && field->length != length * cs->mbmaxlen) if (length && (field->length != expected_field_length))
{ {
fprintf(stderr, "Expected field length: %d, got length: %d\n", fprintf(stderr, "Expected field length: %llu, got length: %lu\n",
(int) (length * cs->mbmaxlen), (int) field->length); expected_field_length, field->length);
DIE_UNLESS(field->length == length * cs->mbmaxlen); DIE_UNLESS(field->length == expected_field_length);
} }
if (def) if (def)
DIE_UNLESS(strcmp(field->def, def) == 0); DIE_UNLESS(strcmp(field->def, def) == 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment