Bug#33222 - myisam-table drops rows when column is added

           and a char-field > 128 exists

CHECK TABLE (non-QUICK) and any form of repair table did wrongly rate
records as corrupted under the following conditions:
1. The table has dynamic row format and
2. it has a CHAR like column > 127 bytes (but not VARCHAR)
   (for multi-byte character sets this could be less than 127
   characters) and
3. it has records with > 127 bytes significant length in that column
   (a byte beyond byte position 127 must be non-space).
Affected were the statements CHECK TABLE, REPAIR TABLE, OPTIMIZE TABLE,
ALTER TABLE. CHECK TABLE reported and marked the table as crashed if any
record was present that fulfilled condition 3. The other statements
deleted these records.

The problem was a signed/unsigned compare in MyISAM code. A
char to uchar change became necessary after the big byte to uchar
change.
parent 9cae71c3
......@@ -2001,4 +2001,166 @@ CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1)
) ENGINE=MyISAM;
INSERT INTO t1 VALUES(REPEAT("a",128), 'b');
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
REPAIR TABLE t1;
Table Op Msg_type Msg_text
test.t1 repair status OK
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1)
) ENGINE=MyISAM;
INSERT INTO t1 VALUES(REPEAT("a",128), 'b');
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 check status OK
REPAIR TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 repair status OK
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1)
) ENGINE=MyISAM;
INSERT INTO t1 VALUES(REPEAT("a",128), 'b');
INSERT INTO t1 VALUES('b', 'b');
INSERT INTO t1 VALUES('c', 'b');
DELETE FROM t1 WHERE c1='b';
SELECT COUNT(*) FROM t1;
COUNT(*)
2
OPTIMIZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
SELECT COUNT(*) FROM t1;
COUNT(*)
2
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1),
KEY (c1)
) ENGINE=MyISAM;
# Insert 100 rows. Query log disabled.
UPDATE t1 SET c1=REPEAT("a",128) LIMIT 90;
SELECT COUNT(*) FROM t1;
COUNT(*)
100
ALTER TABLE t1 ENGINE=MyISAM;
SELECT COUNT(*) FROM t1;
COUNT(*)
100
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
CHECK TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
INSERT INTO t1 VALUES(REPEAT(_utf8 x'e0ae85',43), 'b');
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
REPAIR TABLE t1;
Table Op Msg_type Msg_text
test.t1 repair status OK
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
INSERT INTO t1 VALUES(REPEAT(_utf8 x'e0ae85',43), 'b');
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 check status OK
REPAIR TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 repair status OK
SELECT COUNT(*) FROM t1;
COUNT(*)
1
CHECK TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
INSERT INTO t1 VALUES(REPEAT(_utf8 x'e0ae85',43), 'b');
INSERT INTO t1 VALUES('b', 'b');
INSERT INTO t1 VALUES('c', 'b');
DELETE FROM t1 WHERE c1='b';
SELECT COUNT(*) FROM t1;
COUNT(*)
2
OPTIMIZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
SELECT COUNT(*) FROM t1;
COUNT(*)
2
DROP TABLE t1;
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1),
KEY (c1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
# Insert 100 rows. Query log disabled.
UPDATE t1 SET c1=REPEAT(_utf8 x'e0ae85',43) LIMIT 90;
SELECT COUNT(*) FROM t1;
COUNT(*)
100
ALTER TABLE t1 ENGINE=MyISAM;
SELECT COUNT(*) FROM t1;
COUNT(*)
100
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
CHECK TABLE t1 EXTENDED;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
End of 5.1 tests
......@@ -1261,5 +1261,175 @@ DELETE FROM t1 WHERE c1 >= 10;
CHECK TABLE t1;
DROP TABLE t1;
#
# Bug#33222 - myisam-table drops rows when column is added
# and a char-field > 128 exists
#
# Test #1 - CHECK TABLE sees wrong record, REPAR TABLE deletes it.
# Using a CHAR column that can have > 127 characters.
# Using a VARCHAR to create a table with dynamic row format.
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1)
) ENGINE=MyISAM;
INSERT INTO t1 VALUES(REPEAT("a",128), 'b');
SELECT COUNT(*) FROM t1;
CHECK TABLE t1;
REPAIR TABLE t1;
SELECT COUNT(*) FROM t1;
CHECK TABLE t1;
DROP TABLE t1;
#
# Test #2 - same as test #1, but using EXTENDED.
# Using a CHAR column that can have > 127 characters.
# Using a VARCHAR to create a table with dynamic row format.
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1)
) ENGINE=MyISAM;
INSERT INTO t1 VALUES(REPEAT("a",128), 'b');
SELECT COUNT(*) FROM t1;
CHECK TABLE t1 EXTENDED;
REPAIR TABLE t1 EXTENDED;
SELECT COUNT(*) FROM t1;
CHECK TABLE t1 EXTENDED;
DROP TABLE t1;
#
# Test #3 - same as test #1, but using OPTIMIZE TABLE.
# Using a CHAR column that can have > 127 characters.
# Using a VARCHAR to create a table with dynamic row format.
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1)
) ENGINE=MyISAM;
INSERT INTO t1 VALUES(REPEAT("a",128), 'b');
# Insert more rows and delete one in the middle to force optimize.
INSERT INTO t1 VALUES('b', 'b');
INSERT INTO t1 VALUES('c', 'b');
DELETE FROM t1 WHERE c1='b';
SELECT COUNT(*) FROM t1;
OPTIMIZE TABLE t1;
SELECT COUNT(*) FROM t1;
DROP TABLE t1;
#
# Test #4 - ALTER TABLE deletes rows.
# Using a CHAR column that can have > 127 characters.
# Using a VARCHAR to create a table with dynamic row format.
# Using an index which can be disabled during bulk insert.
CREATE TABLE t1 (
c1 CHAR(130),
c2 VARCHAR(1),
KEY (c1)
) ENGINE=MyISAM;
#
# Insert 100 rows. This turns bulk insert on during the copy phase of
# ALTER TABLE. Bulk insert disables keys before the insert and re-enables
# them by repair after the insert.
--disable_query_log
let $count= 100;
--echo # Insert $count rows. Query log disabled.
while ($count)
{
INSERT INTO t1 VALUES ('a', 'b');
dec $count;
}
--enable_query_log
#
# Change most of the rows into long character values with > 127 characters.
UPDATE t1 SET c1=REPEAT("a",128) LIMIT 90;
SELECT COUNT(*) FROM t1;
ALTER TABLE t1 ENGINE=MyISAM;
#
# With bug present, this shows that all long rows are gone.
SELECT COUNT(*) FROM t1;
CHECK TABLE t1;
CHECK TABLE t1 EXTENDED;
DROP TABLE t1;
#
# Test #5 - same as test #1 but UTF-8.
# Using a CHAR column that can have > 127 characters.
# Using a VARCHAR to create a table with dynamic row format.
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
# Using Tamil Letter A, Unicode U+0B85
INSERT INTO t1 VALUES(REPEAT(_utf8 x'e0ae85',43), 'b');
SELECT COUNT(*) FROM t1;
CHECK TABLE t1;
REPAIR TABLE t1;
SELECT COUNT(*) FROM t1;
CHECK TABLE t1;
DROP TABLE t1;
#
# Test #6 - same as test #2, but UTF-8.
# Using a CHAR column that can have > 127 characters.
# Using a VARCHAR to create a table with dynamic row format.
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
# Using Tamil Letter A, Unicode U+0B85
INSERT INTO t1 VALUES(REPEAT(_utf8 x'e0ae85',43), 'b');
SELECT COUNT(*) FROM t1;
CHECK TABLE t1 EXTENDED;
REPAIR TABLE t1 EXTENDED;
SELECT COUNT(*) FROM t1;
CHECK TABLE t1 EXTENDED;
DROP TABLE t1;
#
# Test #7 - same as test #3, but UTF-8.
# Using a CHAR column that can have > 127 characters.
# Using a VARCHAR to create a table with dynamic row format.
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
# Using Tamil Letter A, Unicode U+0B85
INSERT INTO t1 VALUES(REPEAT(_utf8 x'e0ae85',43), 'b');
# Insert more rows and delete one in the middle to force optimize.
INSERT INTO t1 VALUES('b', 'b');
INSERT INTO t1 VALUES('c', 'b');
DELETE FROM t1 WHERE c1='b';
SELECT COUNT(*) FROM t1;
OPTIMIZE TABLE t1;
SELECT COUNT(*) FROM t1;
DROP TABLE t1;
#
# Test #8 - same as test #4, but UTF-8.
# Using a CHAR column that can have > 42 UTF-8 characters.
# Using a VARCHAR to create a table with dynamic row format.
# Using an index which can be disabled during bulk insert.
CREATE TABLE t1 (
c1 CHAR(50),
c2 VARCHAR(1),
KEY (c1)
) ENGINE=MyISAM DEFAULT CHARSET UTF8;
#
# Insert 100 rows. This turns bulk insert on during the copy phase of
# ALTER TABLE. Bulk insert disables keys before the insert and re-enables
# them by repair after the insert.
--disable_query_log
let $count= 100;
--echo # Insert $count rows. Query log disabled.
while ($count)
{
INSERT INTO t1 VALUES ('a', 'b');
dec $count;
}
--enable_query_log
#
# Change most of the rows into long character values with > 42 characters.
# Using Tamil Letter A, Unicode U+0B85
UPDATE t1 SET c1=REPEAT(_utf8 x'e0ae85',43) LIMIT 90;
SELECT COUNT(*) FROM t1;
ALTER TABLE t1 ENGINE=MyISAM;
#
# With bug present, this shows that all long rows are gone.
SELECT COUNT(*) FROM t1;
CHECK TABLE t1;
CHECK TABLE t1 EXTENDED;
DROP TABLE t1;
--echo End of 5.1 tests
......@@ -1006,12 +1006,12 @@ uint _mi_rec_pack(MI_INFO *info, register uchar *to,
{
if (rec->length > 255 && new_length > 127)
{
to[0]=(char) ((new_length & 127)+128);
to[1]=(char) (new_length >> 7);
to+=2;
}
else
*to++= (char) new_length;
to[0]= (uchar) ((new_length & 127) + 128);
to[1]= (uchar) (new_length >> 7);
to+=2;
}
else
*to++= (uchar) new_length;
memcpy((uchar*) to,pos,(size_t) new_length); to+=new_length;
flag|=bit;
}
......@@ -1045,7 +1045,7 @@ uint _mi_rec_pack(MI_INFO *info, register uchar *to,
}
if ((bit= bit << 1) >= 256)
{
*packpos++ = (char) (uchar) flag;
*packpos++= (uchar) flag;
bit=1; flag=0;
}
}
......@@ -1055,9 +1055,9 @@ uint _mi_rec_pack(MI_INFO *info, register uchar *to,
}
}
if (bit != 1)
*packpos= (char) (uchar) flag;
*packpos= (uchar) flag;
if (info->s->calc_checksum)
*to++=(char) info->checksum;
*to++= (uchar) info->checksum;
DBUG_PRINT("exit",("packed length: %d",(int) (to-startpos)));
DBUG_RETURN((uint) (to-startpos));
} /* _mi_rec_pack */
......@@ -1128,12 +1128,14 @@ my_bool _mi_rec_check(MI_INFO *info,const uchar *record, uchar *rec_buff,
goto err;
if (rec->length > 255 && new_length > 127)
{
if (to[0] != (char) ((new_length & 127)+128) ||
to[1] != (char) (new_length >> 7))
goto err;
to+=2;
}
else if (*to++ != (char) new_length)
/* purecov: begin inspected */
if (to[0] != (uchar) ((new_length & 127) + 128) ||
to[1] != (uchar) (new_length >> 7))
goto err;
to+=2;
/* purecov: end */
}
else if (*to++ != (uchar) new_length)
goto err;
to+=new_length;
}
......
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