Hi, Mats
Nice work! Approved!
Mats Kindahl wrote:
> #At file:///home/bzr/b37426-mysql-5.1-bugteam/
>
> 2667 Mats Kindahl 2008-06-25
> BUG#37426: RBR breaks for CHAR() UTF-8 fields > 85 chars
>
> In order to handle CHAR() fields, 8 bits were reserved for
> the size of the CHAR field. However, instead of denoting the
> number of characters in the field, field_length was used which
> denotes the number of bytes in the field.
>
> Since UTF-8 fields can have three bytes per character (and
> has been extended to have four bytes per character in 6.0),
> an extra two bits have been encoded in the field metadata
> work for fields of type Field_string (i.e., CHAR fields).
>
> Since the metadata word is filled, the extra bits have been
> encoded in the upper 4 bits of the real type (the most
> significant byte of the metadata word) by computing the
> bitwise xor of the extra two bits. Since the upper 4 bits
> of the real type always is 1111 for Field_string, this
> means that for fields of length <256, the encoding is
> identical to the encoding used in pre-5.1.26 servers, but
> for lengths of 256 or more, an unrecognized type is formed,
> causing an old slave (that does not handle lengths of 256
> or more) to stop.
> added:
> mysql-test/suite/bugs/r/rpl_bug37426.result
> mysql-test/suite/bugs/t/rpl_bug37426.test
> modified:
> mysql-test/extra/rpl_tests/rpl_row_basic.test
> mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result
> mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result
> sql/field.cc
> sql/field.h
>
> per-file messages:
> mysql-test/extra/rpl_tests/rpl_row_basic.test
> Adding test cases for replicating UTF-8 fields of lengths
> of 256 or more (bytes).
> mysql-test/suite/bugs/r/rpl_bug37426.result
> Result file change.
> mysql-test/suite/bugs/t/rpl_bug37426.test
> Added test for reported bug.
> mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result
> Result file change.
> mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result
> Result file change.
> sql/field.cc
> Encoding an extra two bits in the most significant nibble (4 bits)
> of the metadata word. Adding assertions to ensure that no attempt
> is made to use lengths longer than supported.
> sql/field.h
> Field length is now computed from most significant 4 bits
> of metadata word, or is equal to the row pack length if
> there is no metadata.
> === modified file 'mysql-test/extra/rpl_tests/rpl_row_basic.test'
> --- a/mysql-test/extra/rpl_tests/rpl_row_basic.test 2007-12-12 19:12:29 +0000
> +++ b/mysql-test/extra/rpl_tests/rpl_row_basic.test 2008-06-25 19:16:37 +0000
> @@ -259,7 +259,7 @@ DELETE FROM t1;
> query_vertical SELECT COUNT(*) FROM t1 ORDER BY c1,c2;
> sync_slave_with_master;
> set @@global.slave_exec_mode= default;
> -let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1);
> +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> disable_query_log;
> eval SELECT "$last_error" AS Last_SQL_Error;
> enable_query_log;
> @@ -272,3 +272,150 @@ query_vertical SELECT COUNT(*) FROM t1 O
> connection master;
> DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8;
> sync_slave_with_master;
> +
> +#
> +# BUG#37426: RBR breaks for CHAR() UTF8 fields > 85 chars
> +#
> +
> +# We have 4 combinations to test with respect to the field length
> +# (i.e., the number of bytes) of the CHAR fields:
> +#
> +# 1. Replicating from CHAR<256 to CHAR<256
> +# 2. Replicating from CHAR<256 to CHAR>255
> +# 3. Replicating from CHAR>255 to CHAR<256
> +# 4. Replicating from CHAR>255 to CHAR>255
> +
> +# We also make a special case of using the max size of a field on the
> +# master, i.e. CHAR(255) in UTF-8, giving another three cases.
> +#
> +# 5. Replicating UTF-8 CHAR(255) to CHAR(<256)
> +# 6. Replicating UTF-8 CHAR(255) to CHAR(>255)
> +# 7. Replicating UTF-8 CHAR(255) to CHAR(255) UTF-8
> +
> +connection master;
> +CREATE TABLE t1 (i INT NOT NULL,
> + c CHAR(16) CHARACTER SET utf8 NOT NULL,
> + j INT NOT NULL);
> +
> +CREATE TABLE t2 (i INT NOT NULL,
> + c CHAR(16) CHARACTER SET utf8 NOT NULL,
> + j INT NOT NULL);
> +
> +sync_slave_with_master;
> +ALTER TABLE t2 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;
> +
> +connection master;
> +CREATE TABLE t3 (i INT NOT NULL,
> + c CHAR(128) CHARACTER SET utf8 NOT NULL,
> + j INT NOT NULL);
> +sync_slave_with_master;
> +ALTER TABLE t3 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;
> +
> +connection master;
> +CREATE TABLE t4 (i INT NOT NULL,
> + c CHAR(128) CHARACTER SET utf8 NOT NULL,
> + j INT NOT NULL);
> +
> +CREATE TABLE t5 (i INT NOT NULL,
> + c CHAR(255) CHARACTER SET utf8 NOT NULL,
> + j INT NOT NULL);
> +sync_slave_with_master;
> +ALTER TABLE t5 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;
> +
> +connection master;
> +CREATE TABLE t6 (i INT NOT NULL,
> + c CHAR(255) CHARACTER SET utf8 NOT NULL,
> + j INT NOT NULL);
> +sync_slave_with_master;
> +ALTER TABLE t6 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;
> +
> +connection master;
> +CREATE TABLE t7 (i INT NOT NULL,
> + c CHAR(255) CHARACTER SET utf8 NOT NULL,
> + j INT NOT NULL);
> +
> +--echo [expecting slave to replicate correctly]
> +connection master;
> +INSERT INTO t1 VALUES (1, "", 1);
> +INSERT INTO t1 VALUES (2, repeat(_utf8'a', 16), 2);
> +
> +let $diff_table_1=master:test.t1;
> +let $diff_table_2=slave:test.t1;
> +source include/diff_tables.inc;
> +
> +--echo [expecting slave to replicate correctly]
> +connection master;
> +INSERT INTO t2 VALUES (1, "", 1);
> +INSERT INTO t2 VALUES (2, repeat(_utf8'a', 16), 2);
> +
> +let $diff_table_1=master:test.t2;
> +let $diff_table_2=slave:test.t2;
> +source include/diff_tables.inc;
> +
> +--echo [expecting slave to stop]
> +connection master;
> +INSERT INTO t3 VALUES (1, "", 1);
> +INSERT INTO t3 VALUES (2, repeat(_utf8'a', 128), 2);
> +
> +connection slave;
> +source include/wait_for_slave_sql_to_stop.inc;
> +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +disable_query_log;
> +eval SELECT "$last_error" AS Last_SQL_Error;
> +enable_query_log;
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +source include/wait_for_slave_to_start.inc;
> +
> +--echo [expecting slave to replicate correctly]
> +connection master;
> +INSERT INTO t4 VALUES (1, "", 1);
> +INSERT INTO t4 VALUES (2, repeat(_utf8'a', 128), 2);
> +
> +let $diff_table_1=master:test.t4;
> +let $diff_table_2=slave:test.t4;
> +source include/diff_tables.inc;
> +
> +--echo [expecting slave to stop]
> +connection master;
> +INSERT INTO t5 VALUES (1, "", 1);
> +INSERT INTO t5 VALUES (2, repeat(_utf8'a', 255), 2);
> +
> +connection slave;
> +source include/wait_for_slave_sql_to_stop.inc;
> +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +disable_query_log;
> +eval SELECT "$last_error" AS Last_SQL_Error;
> +enable_query_log;
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +source include/wait_for_slave_to_start.inc;
> +
> +--echo [expecting slave to stop]
> +connection master;
> +INSERT INTO t6 VALUES (1, "", 1);
> +INSERT INTO t6 VALUES (2, repeat(_utf8'a', 255), 2);
> +
> +connection slave;
> +source include/wait_for_slave_sql_to_stop.inc;
> +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +disable_query_log;
> +eval SELECT "$last_error" AS Last_SQL_Error;
> +enable_query_log;
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +source include/wait_for_slave_to_start.inc;
> +
> +--echo [expecting slave to replicate correctly]
> +connection master;
> +INSERT INTO t7 VALUES (1, "", 1);
> +INSERT INTO t7 VALUES (2, repeat(_utf8'a', 255), 2);
> +
> +let $diff_table_1=master:test.t7;
> +let $diff_table_2=slave:test.t7;
> +source include/diff_tables.inc;
> +
> +connection master;
> +drop table t1, t2, t3, t4, t5, t6, t7;
> +sync_slave_with_master;
> +
>
> === added file 'mysql-test/suite/bugs/r/rpl_bug37426.result'
> --- a/mysql-test/suite/bugs/r/rpl_bug37426.result 1970-01-01 00:00:00 +0000
> +++ b/mysql-test/suite/bugs/r/rpl_bug37426.result 2008-06-25 19:16:37 +0000
> @@ -0,0 +1,17 @@
> +stop slave;
> +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
> +reset master;
> +reset slave;
> +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
> +start slave;
> +CREATE TABLE char128_utf8 (
> +i1 INT NOT NULL,
> +c CHAR(128) CHARACTER SET utf8 NOT NULL,
> +i2 INT NOT NULL);
> +INSERT INTO char128_utf8 VALUES ( 1, "123", 1 );
> +SELECT * FROM char128_utf8;
> +i1 c i2
> +1 123 1
> +SELECT * FROM char128_utf8;
> +i1 c i2
> +1 123 1
>
> === added file 'mysql-test/suite/bugs/t/rpl_bug37426.test'
> --- a/mysql-test/suite/bugs/t/rpl_bug37426.test 1970-01-01 00:00:00 +0000
> +++ b/mysql-test/suite/bugs/t/rpl_bug37426.test 2008-06-25 19:16:37 +0000
> @@ -0,0 +1,22 @@
> +#############################################################
> +# Author: Mats Kindahl <mats@stripped>
> +# Date: 2008-06-18
> +# Purpose: Test for BUG#37426
> +# RBR breaks for CHAR() UTF8 fields > 85 chars
> +#############################################################
> +
> +source include/master-slave.inc;
> +source include/have_binlog_format_row.inc;
> +
> +connection master;
> +CREATE TABLE char128_utf8 (
> + i1 INT NOT NULL,
> + c CHAR(128) CHARACTER SET utf8 NOT NULL,
> + i2 INT NOT NULL);
> +
> +INSERT INTO char128_utf8 VALUES ( 1, "123", 1 );
> +
> +SELECT * FROM char128_utf8;
> +sync_slave_with_master;
> +
> +SELECT * FROM char128_utf8;
>
> === modified file 'mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result'
> --- a/mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result 2007-12-12 19:12:29 +0000
> +++ b/mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result 2008-06-25 19:16:37 +0000
> @@ -437,7 +437,70 @@ SELECT COUNT(*) FROM t1 ORDER BY c1,c2;
> COUNT(*) 0
> set @@global.slave_exec_mode= default;
> Last_SQL_Error
> -0
> +
> SELECT COUNT(*) FROM t1 ORDER BY c1,c2;
> COUNT(*) 0
> DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8;
> +CREATE TABLE t1 (i INT NOT NULL,
> +c CHAR(16) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +CREATE TABLE t2 (i INT NOT NULL,
> +c CHAR(16) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t2 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t3 (i INT NOT NULL,
> +c CHAR(128) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t3 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t4 (i INT NOT NULL,
> +c CHAR(128) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +CREATE TABLE t5 (i INT NOT NULL,
> +c CHAR(255) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t5 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t6 (i INT NOT NULL,
> +c CHAR(255) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t6 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t7 (i INT NOT NULL,
> +c CHAR(255) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +[expecting slave to replicate correctly]
> +INSERT INTO t1 VALUES (1, "", 1);
> +INSERT INTO t1 VALUES (2, repeat(_utf8'a', 16), 2);
> +Comparing tables master:test.t1 and slave:test.t1
> +[expecting slave to replicate correctly]
> +INSERT INTO t2 VALUES (1, "", 1);
> +INSERT INTO t2 VALUES (2, repeat(_utf8'a', 16), 2);
> +Comparing tables master:test.t2 and slave:test.t2
> +[expecting slave to stop]
> +INSERT INTO t3 VALUES (1, "", 1);
> +INSERT INTO t3 VALUES (2, repeat(_utf8'a', 128), 2);
> +Last_SQL_Error
> +Table definition on master and slave does not match: Column 1 size mismatch - master
> has size 384, test.t3 on slave has size 49. Master's column size should be <= the
> slave's column size.
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +[expecting slave to replicate correctly]
> +INSERT INTO t4 VALUES (1, "", 1);
> +INSERT INTO t4 VALUES (2, repeat(_utf8'a', 128), 2);
> +Comparing tables master:test.t4 and slave:test.t4
> +[expecting slave to stop]
> +INSERT INTO t5 VALUES (1, "", 1);
> +INSERT INTO t5 VALUES (2, repeat(_utf8'a', 255), 2);
> +Last_SQL_Error
> +Table definition on master and slave does not match: Column 1 size mismatch - master
> has size 765, test.t5 on slave has size 49. Master's column size should be <= the
> slave's column size.
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +[expecting slave to stop]
> +INSERT INTO t6 VALUES (1, "", 1);
> +INSERT INTO t6 VALUES (2, repeat(_utf8'a', 255), 2);
> +Last_SQL_Error
> +Table definition on master and slave does not match: Column 1 size mismatch - master
> has size 765, test.t6 on slave has size 385. Master's column size should be <= the
> slave's column size.
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +[expecting slave to replicate correctly]
> +INSERT INTO t7 VALUES (1, "", 1);
> +INSERT INTO t7 VALUES (2, repeat(_utf8'a', 255), 2);
> +Comparing tables master:test.t7 and slave:test.t7
> +drop table t1, t2, t3, t4, t5, t6, t7;
>
> === modified file 'mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result'
> --- a/mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result 2007-12-12 19:12:29 +0000
> +++ b/mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result 2008-06-25 19:16:37 +0000
> @@ -437,7 +437,70 @@ SELECT COUNT(*) FROM t1 ORDER BY c1,c2;
> COUNT(*) 0
> set @@global.slave_exec_mode= default;
> Last_SQL_Error
> -0
> +
> SELECT COUNT(*) FROM t1 ORDER BY c1,c2;
> COUNT(*) 0
> DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8;
> +CREATE TABLE t1 (i INT NOT NULL,
> +c CHAR(16) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +CREATE TABLE t2 (i INT NOT NULL,
> +c CHAR(16) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t2 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t3 (i INT NOT NULL,
> +c CHAR(128) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t3 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t4 (i INT NOT NULL,
> +c CHAR(128) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +CREATE TABLE t5 (i INT NOT NULL,
> +c CHAR(255) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t5 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t6 (i INT NOT NULL,
> +c CHAR(255) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +ALTER TABLE t6 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;
> +CREATE TABLE t7 (i INT NOT NULL,
> +c CHAR(255) CHARACTER SET utf8 NOT NULL,
> +j INT NOT NULL);
> +[expecting slave to replicate correctly]
> +INSERT INTO t1 VALUES (1, "", 1);
> +INSERT INTO t1 VALUES (2, repeat(_utf8'a', 16), 2);
> +Comparing tables master:test.t1 and slave:test.t1
> +[expecting slave to replicate correctly]
> +INSERT INTO t2 VALUES (1, "", 1);
> +INSERT INTO t2 VALUES (2, repeat(_utf8'a', 16), 2);
> +Comparing tables master:test.t2 and slave:test.t2
> +[expecting slave to stop]
> +INSERT INTO t3 VALUES (1, "", 1);
> +INSERT INTO t3 VALUES (2, repeat(_utf8'a', 128), 2);
> +Last_SQL_Error
> +Table definition on master and slave does not match: Column 1 size mismatch - master
> has size 384, test.t3 on slave has size 49. Master's column size should be <= the
> slave's column size.
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +[expecting slave to replicate correctly]
> +INSERT INTO t4 VALUES (1, "", 1);
> +INSERT INTO t4 VALUES (2, repeat(_utf8'a', 128), 2);
> +Comparing tables master:test.t4 and slave:test.t4
> +[expecting slave to stop]
> +INSERT INTO t5 VALUES (1, "", 1);
> +INSERT INTO t5 VALUES (2, repeat(_utf8'a', 255), 2);
> +Last_SQL_Error
> +Table definition on master and slave does not match: Column 1 size mismatch - master
> has size 765, test.t5 on slave has size 49. Master's column size should be <= the
> slave's column size.
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +[expecting slave to stop]
> +INSERT INTO t6 VALUES (1, "", 1);
> +INSERT INTO t6 VALUES (2, repeat(_utf8'a', 255), 2);
> +Last_SQL_Error
> +Table definition on master and slave does not match: Column 1 size mismatch - master
> has size 765, test.t6 on slave has size 385. Master's column size should be <= the
> slave's column size.
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
> +START SLAVE;
> +[expecting slave to replicate correctly]
> +INSERT INTO t7 VALUES (1, "", 1);
> +INSERT INTO t7 VALUES (2, repeat(_utf8'a', 255), 2);
> +Comparing tables master:test.t7 and slave:test.t7
> +drop table t1, t2, t3, t4, t5, t6, t7;
>
> === modified file 'sql/field.cc'
> --- a/sql/field.cc 2008-05-13 12:01:02 +0000
> +++ b/sql/field.cc 2008-06-25 19:16:37 +0000
> @@ -4037,7 +4037,6 @@ Field_real::pack(uchar *to, const uchar
> {
> DBUG_ENTER("Field_real::pack");
> DBUG_ASSERT(max_length >= pack_length());
> - DBUG_PRINT("debug", ("pack_length(): %u", pack_length()));
> #ifdef WORDS_BIGENDIAN
> if (low_byte_first != table->s->db_low_byte_first)
> {
> @@ -4056,7 +4055,6 @@ Field_real::unpack(uchar *to, const ucha
> uint param_data, bool low_byte_first)
> {
> DBUG_ENTER("Field_real::unpack");
> - DBUG_PRINT("debug", ("pack_length(): %u", pack_length()));
> #ifdef WORDS_BIGENDIAN
> if (low_byte_first != table->s->db_low_byte_first)
> {
> @@ -6724,6 +6722,25 @@ uchar *Field_string::pack(uchar *to, con
> @c param_data argument contains the result of field->real_type() from
> the master.
>
> + @note In order to be able to handle lengths exceeding 255 and be
> + backwards-compatible with pre-5.1.26 servers, an extra two bits of
> + the length has been added to the metadata in such a way that if
> + they are set, a new unrecognized type is generated. This will
> + cause pre-5.1-26 servers to stop due to a field type mismatch,
> + while new servers will be able to extract the extra bits. If the
> + length is <256, there will be no difference and both a new and an
> + old server will be able to handle it.
> +
> + @note The extra two bits are added to bits 13 and 14 of the
> + parameter data (with 1 being the least siginficant bit and 16 the
> + most significant bit of the word) by xoring the extra length bits
> + with the real type. Since all allowable types have 0xF as most
> + significant bits, lengths <256 will not affect the real type at
> + all, while all other values will result in a non-existant type in
> + the range 17-244.
> +
> + @see Field_string::do_save_field_metadata
> +
> @param to Destination of the data
> @param from Source of the data
> @param param_data Real type (upper) and length (lower) values
> @@ -6736,10 +6753,24 @@ Field_string::unpack(uchar *to,
> uint param_data,
> bool low_byte_first __attribute__((unused)))
> {
> - uint from_length=
> - param_data ? min(param_data & 0x00ff, field_length) : field_length;
> - uint length;
> + uint from_length, length;
> +
> + /*
> + Compute the declared length of the field on the master. This is
> + used to decide if one or two bytes should be read as length.
> + */
> + if (param_data)
> + from_length= (((param_data >> 4) & 0x300) ^ 0x300) + (param_data &
> 0x00ff);
> + else
> + from_length= field_length;
>
> + DBUG_PRINT("debug",
> + ("param_data: 0x%x, field_length: %u, from_length: %u",
> + param_data, field_length, from_length));
> + /*
> + Compute the actual length of the data by reading one or two bits
> + (depending on the declared field length on the master).
> + */
> if (from_length > 255)
> {
> length= uint2korr(from);
> @@ -6762,14 +6793,37 @@ Field_string::unpack(uchar *to,
> second byte of the field metadata array at index of *metadata_ptr and
> *(metadata_ptr + 1).
>
> + @note In order to be able to handle lengths exceeding 255 and be
> + backwards-compatible with pre-5.1.26 servers, an extra two bits of
> + the length has been added to the metadata in such a way that if
> + they are set, a new unrecognized type is generated. This will
> + cause pre-5.1-26 servers to stop due to a field type mismatch,
> + while new servers will be able to extract the extra bits. If the
> + length is <256, there will be no difference and both a new and an
> + old server will be able to handle it.
> +
> + @note The extra two bits are added to bits 13 and 14 of the
> + parameter data (with 1 being the least siginficant bit and 16 the
> + most significant bit of the word) by xoring the extra length bits
> + with the real type. Since all allowable types have 0xF as most
> + significant bits of the metadata word, lengths <256 will not affect
> + the real type at all, while all other values will result in a
> + non-existant type in the range 17-244.
> +
> + @see Field_string::unpack
> +
> @param metadata_ptr First byte of field metadata
>
> @returns number of bytes written to metadata_ptr
> */
> int Field_string::do_save_field_metadata(uchar *metadata_ptr)
> {
> - *metadata_ptr= real_type();
> - *(metadata_ptr + 1)= field_length;
> + DBUG_ASSERT(field_length < 1024);
> + DBUG_ASSERT((real_type() & 0xF0) == 0xF0);
> + DBUG_PRINT("debug", ("field_length: %u, real_type: %u",
> + field_length, real_type()));
> + *metadata_ptr= (real_type() ^ ((field_length & 0x300) >> 4));
> + *(metadata_ptr + 1)= field_length & 0xFF;
> return 2;
> }
>
>
> === modified file 'sql/field.h'
> --- a/sql/field.h 2008-05-13 12:01:02 +0000
> +++ b/sql/field.h 2008-06-25 19:16:37 +0000
> @@ -1425,7 +1425,12 @@ public:
> virtual const uchar *unpack(uchar* to, const uchar *from,
> uint param_data, bool low_byte_first);
> uint pack_length_from_metadata(uint field_metadata)
> - { return (field_metadata & 0x00ff); }
> + {
> + DBUG_PRINT("debug", ("field_metadata: 0x%04x", field_metadata));
> + if (field_metadata == 0)
> + return row_pack_length();
> + return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata
> & 0x00ff);
> + }
> uint row_pack_length() { return (field_length + 1); }
> int pack_cmp(const uchar *a,const uchar *b,uint key_length,
> my_bool insert_or_update);
>