From b9e592a786a8fc9f12cb80cb42c24e274037f153 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 9 Dec 2024 16:54:31 +0530 Subject: [PATCH] MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' failed in cmp_rec_rec_simple_field Problem: ======= InnoDB wrongly stores the primary key field in externally stored off page during bulk insert operation. This leads to assert failure. Solution: ======== row_merge_buf_blob(): Should store the primary key fields inline. Store the variable length field data externally based on the row format of the table. row_merge_buf_write(): check whether the record size exceeds the maximum record size. row_merge_copy_blob_from_file(): Construct the tuple based on the variable length field --- .../suite/innodb/r/alter_copy_bulk.result | 24 +++ .../innodb/r/innodb-64k-crash,dynamic.rdiff | 8 + .../innodb/r/innodb-64k-crash,redundant.rdiff | 52 ++++++ .../suite/innodb/r/innodb-64k-crash.result | 50 ++++++ .../innodb/r/insert_into_empty,32k.rdiff | 8 +- .../suite/innodb/r/insert_into_empty,4k.rdiff | 34 +++- .../innodb/r/insert_into_empty,64k.rdiff | 8 +- .../suite/innodb/r/insert_into_empty,8k.rdiff | 38 +++++ .../suite/innodb/r/insert_into_empty.result | 23 +++ .../suite/innodb/t/alter_copy_bulk.test | 25 +++ .../suite/innodb/t/innodb-64k-crash.opt | 1 + .../suite/innodb/t/innodb-64k-crash.test | 62 +++++++ .../suite/innodb/t/insert_into_empty.test | 28 +++- storage/innobase/row/row0merge.cc | 156 ++++++++++-------- 14 files changed, 443 insertions(+), 74 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb-64k-crash,dynamic.rdiff create mode 100644 mysql-test/suite/innodb/r/innodb-64k-crash,redundant.rdiff create mode 100644 mysql-test/suite/innodb/r/insert_into_empty,8k.rdiff create mode 100644 mysql-test/suite/innodb/t/innodb-64k-crash.opt diff --git a/mysql-test/suite/innodb/r/alter_copy_bulk.result b/mysql-test/suite/innodb/r/alter_copy_bulk.result index e64853f72a4..cd57d95af96 100644 --- a/mysql-test/suite/innodb/r/alter_copy_bulk.result +++ b/mysql-test/suite/innodb/r/alter_copy_bulk.result @@ -66,4 +66,28 @@ SELECT COUNT(*) FROM t; COUNT(*) 2 DROP TABLE t1, t2, t; +# +# MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +# failed in cmp_rec_rec_simple_field +# +CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +INSERT INTO t1 VALUES +(REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), +(REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), +(REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), +(REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), +(REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), +(REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), +(REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), +(REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), +(REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), +(REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), +(REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), +(REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), +(REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), +(REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), +(REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), +(REPEAT('x',65535),'dd'); +ALTER TABLE t1 FORCE, ALGORITHM=COPY; +DROP TABLE t1; SET GLOBAL innodb_stats_persistent=@default_stats_persistent; diff --git a/mysql-test/suite/innodb/r/innodb-64k-crash,dynamic.rdiff b/mysql-test/suite/innodb/r/innodb-64k-crash,dynamic.rdiff new file mode 100644 index 00000000000..83c5e3aa787 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb-64k-crash,dynamic.rdiff @@ -0,0 +1,8 @@ +--- innodb-64k-crash.result 2024-11-28 10:37:26.491384671 +0530 ++++ innodb-64k-crash.reject 2024-11-28 11:05:46.461405444 +0530 +@@ -334,5 +334,4 @@ + REPEAT('s', 1024), REPEAT('t', 1024), + REPEAT('u', 1024), REPEAT('v', 1024), + REPEAT('w', 1024), REPEAT('x', 1024)); +-ERROR 42000: Row size too large (> 16383). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/innodb-64k-crash,redundant.rdiff b/mysql-test/suite/innodb/r/innodb-64k-crash,redundant.rdiff new file mode 100644 index 00000000000..637512a2448 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb-64k-crash,redundant.rdiff @@ -0,0 +1,52 @@ +--- innodb-64k-crash.result 2024-11-28 10:37:26.491384671 +0530 ++++ innodb-64k-crash.reject 2024-11-28 11:10:13.381250612 +0530 +@@ -290,49 +290,3 @@ + # MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' + # failed in cmp_rec_rec_simple_field + # +-CREATE TABLE t1(f1 int not null, f2 text, f3 text, f4 text, +-f5 text, f6 text, f7 text, f8 text, +-f9 text, f10 text, f11 text, f12 text, +-f13 text, f14 text, f15 text, f16 text, +-f17 text, f18 text, f19 text, f20 text, +-f21 text, f22 text, f23 text, f24 text, +-f25 text, PRIMARY KEY(f1))ENGINE=InnoDB; +-SET STATEMENT unique_checks=0,foreign_key_checks=0 FOR +-INSERT INTO t1 VALUES(1, REPEAT('a', 1024), REPEAT('b', 1024), +-REPEAT('c', 1024), REPEAT('d', 1024), +-REPEAT('e', 1024), REPEAT('f', 1024), +-REPEAT('g', 4096), REPEAT('h', 1024), +-REPEAT('i', 1024), REPEAT('j', 1024), +-REPEAT('k', 1024), REPEAT('l', 1024), +-REPEAT('m', 1024), REPEAT('n', 1024), +-REPEAT('o', 1024), REPEAT('p', 1024), +-REPEAT('q', 1024), REPEAT('r', 1024), +-REPEAT('s', 1024), REPEAT('t', 1024), +-REPEAT('u', 1024), REPEAT('v', 1024), +-REPEAT('w', 1024), REPEAT('x', 1024)), +-(2, REPEAT('a', 1024), REPEAT('b', 1024), +-REPEAT('c', 1024), REPEAT('d', 1024), +-REPEAT('e', 1024), REPEAT('f', 1024), +-REPEAT('g', 4096), REPEAT('h', 1024), +-REPEAT('i', 1024), REPEAT('j', 1024), +-REPEAT('k', 1024), REPEAT('l', 1024), +-REPEAT('m', 1024), REPEAT('n', 1024), +-REPEAT('o', 1024), REPEAT('p', 1024), +-REPEAT('q', 1024), REPEAT('r', 1024), +-REPEAT('s', 1024), REPEAT('t', 1024), +-REPEAT('u', 1024), REPEAT('v', 1024), +-REPEAT('w', 1024), REPEAT('x', 1024)), +-(3, REPEAT('a', 1024), REPEAT('b', 1024), +-REPEAT('c', 1024), REPEAT('d', 1024), +-REPEAT('e', 1024), REPEAT('f', 1024), +-REPEAT('g', 4096), REPEAT('h', 1024), +-REPEAT('i', 1024), REPEAT('j', 1024), +-REPEAT('k', 1024), REPEAT('l', 1024), +-REPEAT('m', 1024), REPEAT('n', 1024), +-REPEAT('o', 1024), REPEAT('p', 1024), +-REPEAT('q', 1024), REPEAT('r', 1024), +-REPEAT('s', 1024), REPEAT('t', 1024), +-REPEAT('u', 1024), REPEAT('v', 1024), +-REPEAT('w', 1024), REPEAT('x', 1024)); +-ERROR 42000: Row size too large (> 16383). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. +-DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/innodb-64k-crash.result b/mysql-test/suite/innodb/r/innodb-64k-crash.result index 1f3b41f75a6..2b00f9988c2 100644 --- a/mysql-test/suite/innodb/r/innodb-64k-crash.result +++ b/mysql-test/suite/innodb/r/innodb-64k-crash.result @@ -286,3 +286,53 @@ Table Op Msg_type Msg_text test.t1 check status OK test.t2 check status OK drop table t1,t2; +# +# MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +# failed in cmp_rec_rec_simple_field +# +CREATE TABLE t1(f1 int not null, f2 text, f3 text, f4 text, +f5 text, f6 text, f7 text, f8 text, +f9 text, f10 text, f11 text, f12 text, +f13 text, f14 text, f15 text, f16 text, +f17 text, f18 text, f19 text, f20 text, +f21 text, f22 text, f23 text, f24 text, +f25 text, PRIMARY KEY(f1))ENGINE=InnoDB; +SET STATEMENT unique_checks=0,foreign_key_checks=0 FOR +INSERT INTO t1 VALUES(1, REPEAT('a', 1024), REPEAT('b', 1024), +REPEAT('c', 1024), REPEAT('d', 1024), +REPEAT('e', 1024), REPEAT('f', 1024), +REPEAT('g', 4096), REPEAT('h', 1024), +REPEAT('i', 1024), REPEAT('j', 1024), +REPEAT('k', 1024), REPEAT('l', 1024), +REPEAT('m', 1024), REPEAT('n', 1024), +REPEAT('o', 1024), REPEAT('p', 1024), +REPEAT('q', 1024), REPEAT('r', 1024), +REPEAT('s', 1024), REPEAT('t', 1024), +REPEAT('u', 1024), REPEAT('v', 1024), +REPEAT('w', 1024), REPEAT('x', 1024)), +(2, REPEAT('a', 1024), REPEAT('b', 1024), +REPEAT('c', 1024), REPEAT('d', 1024), +REPEAT('e', 1024), REPEAT('f', 1024), +REPEAT('g', 4096), REPEAT('h', 1024), +REPEAT('i', 1024), REPEAT('j', 1024), +REPEAT('k', 1024), REPEAT('l', 1024), +REPEAT('m', 1024), REPEAT('n', 1024), +REPEAT('o', 1024), REPEAT('p', 1024), +REPEAT('q', 1024), REPEAT('r', 1024), +REPEAT('s', 1024), REPEAT('t', 1024), +REPEAT('u', 1024), REPEAT('v', 1024), +REPEAT('w', 1024), REPEAT('x', 1024)), +(3, REPEAT('a', 1024), REPEAT('b', 1024), +REPEAT('c', 1024), REPEAT('d', 1024), +REPEAT('e', 1024), REPEAT('f', 1024), +REPEAT('g', 4096), REPEAT('h', 1024), +REPEAT('i', 1024), REPEAT('j', 1024), +REPEAT('k', 1024), REPEAT('l', 1024), +REPEAT('m', 1024), REPEAT('n', 1024), +REPEAT('o', 1024), REPEAT('p', 1024), +REPEAT('q', 1024), REPEAT('r', 1024), +REPEAT('s', 1024), REPEAT('t', 1024), +REPEAT('u', 1024), REPEAT('v', 1024), +REPEAT('w', 1024), REPEAT('x', 1024)); +ERROR 42000: Row size too large (> 16383). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/insert_into_empty,32k.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,32k.rdiff index 9d40decbb30..ba7e68ad890 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty,32k.rdiff +++ b/mysql-test/suite/innodb/r/insert_into_empty,32k.rdiff @@ -1,4 +1,6 @@ -@@ -377,8 +377,6 @@ +--- insert_into_empty.result ++++ insert_into_empty,32k.result +@@ -446,12 +446,9 @@ c09 text, c10 text, c11 text, c12 text) ENGINE=InnoDB; SET GLOBAL INNODB_DEFAULT_ROW_FORMAT= COMPACT; ALTER TABLE t1 FORCE; @@ -7,3 +9,7 @@ INSERT IGNORE INTO t1 VALUES (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + CHECK TABLE t1; + Table Op Msg_type Msg_text + test.t1 check status OK diff --git a/mysql-test/suite/innodb/r/insert_into_empty,4k.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,4k.rdiff index ad3d03a7eae..358005bbeff 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty,4k.rdiff +++ b/mysql-test/suite/innodb/r/insert_into_empty,4k.rdiff @@ -1,10 +1,38 @@ ---- a/mysql-test/suite/innodb/r/insert_into_empty.result -+++ b/mysql-test/suite/innodb/r/insert_into_empty.result -@@ -430,6 +430,7 @@ +--- insert_into_empty.result ++++ insert_into_empty,4k.result +@@ -451,7 +451,7 @@ INSERT IGNORE INTO t1 VALUES (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. +ERROR 42000: Row size too large (> 1982). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. CHECK TABLE t1; Table Op Msg_type Msg_text test.t1 check status OK +@@ -541,26 +541,4 @@ + DELETE FROM t1; + commit; + DROP TABLE t1; +-# +-# MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +-# failed in cmp_rec_rec_simple_field +-# +-CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +-INSERT INTO t1 VALUES (REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), +-(REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), +-(REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), +-(REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), +-(REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), +-(REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), +-(REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), +-(REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), +-(REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), +-(REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), +-(REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), +-(REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), +-(REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), +-(REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), +-(REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), +-(REPEAT('x',65535),'dd'); +-DROP TABLE t1; + # End of 10.11 tests diff --git a/mysql-test/suite/innodb/r/insert_into_empty,64k.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,64k.rdiff index 9d40decbb30..e3d784a7c88 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty,64k.rdiff +++ b/mysql-test/suite/innodb/r/insert_into_empty,64k.rdiff @@ -1,4 +1,6 @@ -@@ -377,8 +377,6 @@ +--- insert_into_empty.result ++++ insert_into_empty,64k.result +@@ -446,12 +446,9 @@ c09 text, c10 text, c11 text, c12 text) ENGINE=InnoDB; SET GLOBAL INNODB_DEFAULT_ROW_FORMAT= COMPACT; ALTER TABLE t1 FORCE; @@ -7,3 +9,7 @@ INSERT IGNORE INTO t1 VALUES (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + CHECK TABLE t1; + Table Op Msg_type Msg_text + test.t1 check status OK diff --git a/mysql-test/suite/innodb/r/insert_into_empty,8k.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,8k.rdiff new file mode 100644 index 00000000000..99ad06d971a --- /dev/null +++ b/mysql-test/suite/innodb/r/insert_into_empty,8k.rdiff @@ -0,0 +1,38 @@ +--- insert_into_empty.result ++++ insert_into_empty,8k.result +@@ -451,7 +451,7 @@ + INSERT IGNORE INTO t1 VALUES + (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), + (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. ++ERROR 42000: Row size too large (> 4030). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + CHECK TABLE t1; + Table Op Msg_type Msg_text + test.t1 check status OK +@@ -541,26 +541,4 @@ + DELETE FROM t1; + commit; + DROP TABLE t1; +-# +-# MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +-# failed in cmp_rec_rec_simple_field +-# +-CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +-INSERT INTO t1 VALUES (REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), +-(REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), +-(REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), +-(REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), +-(REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), +-(REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), +-(REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), +-(REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), +-(REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), +-(REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), +-(REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), +-(REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), +-(REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), +-(REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), +-(REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), +-(REPEAT('x',65535),'dd'); +-DROP TABLE t1; + # End of 10.11 tests diff --git a/mysql-test/suite/innodb/r/insert_into_empty.result b/mysql-test/suite/innodb/r/insert_into_empty.result index cdc730d2b6d..4277d2c3fa6 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty.result +++ b/mysql-test/suite/innodb/r/insert_into_empty.result @@ -451,6 +451,7 @@ Warning 139 Row size too large (> 8126). Changing some columns to TEXT or BLOB o INSERT IGNORE INTO t1 VALUES (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. CHECK TABLE t1; Table Op Msg_type Msg_text test.t1 check status OK @@ -540,4 +541,26 @@ INSERT INTO t1 VALUES(2,0); DELETE FROM t1; commit; DROP TABLE t1; +# +# MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +# failed in cmp_rec_rec_simple_field +# +CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), +(REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), +(REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), +(REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), +(REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), +(REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), +(REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), +(REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), +(REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), +(REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), +(REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), +(REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), +(REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), +(REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), +(REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), +(REPEAT('x',65535),'dd'); +DROP TABLE t1; # End of 10.11 tests diff --git a/mysql-test/suite/innodb/t/alter_copy_bulk.test b/mysql-test/suite/innodb/t/alter_copy_bulk.test index 2d195617ea4..bc19aca00da 100644 --- a/mysql-test/suite/innodb/t/alter_copy_bulk.test +++ b/mysql-test/suite/innodb/t/alter_copy_bulk.test @@ -83,4 +83,29 @@ CREATE TABLE t engine=innodb SELECT t2.f2 FROM t2 JOIN t1 ON t1.f1 = t2.f1 AND t1.f3 = '' AND t1.f2=1 ; SELECT COUNT(*) FROM t; DROP TABLE t1, t2, t; + +--echo # +--echo # MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +--echo # failed in cmp_rec_rec_simple_field +--echo # +CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +INSERT INTO t1 VALUES + (REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), + (REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), + (REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), + (REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), + (REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), + (REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), + (REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), + (REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), + (REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), + (REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), + (REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), + (REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), + (REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), + (REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), + (REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), + (REPEAT('x',65535),'dd'); +ALTER TABLE t1 FORCE, ALGORITHM=COPY; +DROP TABLE t1; SET GLOBAL innodb_stats_persistent=@default_stats_persistent; diff --git a/mysql-test/suite/innodb/t/innodb-64k-crash.opt b/mysql-test/suite/innodb/t/innodb-64k-crash.opt new file mode 100644 index 00000000000..c856c2d215a --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb-64k-crash.opt @@ -0,0 +1 @@ +--innodb_sort_buffer_size=65536 diff --git a/mysql-test/suite/innodb/t/innodb-64k-crash.test b/mysql-test/suite/innodb/t/innodb-64k-crash.test index 93f6f79edf3..61735890618 100644 --- a/mysql-test/suite/innodb/t/innodb-64k-crash.test +++ b/mysql-test/suite/innodb/t/innodb-64k-crash.test @@ -2,6 +2,7 @@ --source include/have_innodb_64k.inc # Embedded server does not support restarting --source include/not_embedded.inc +--source innodb_default_row_format.inc let $MYSQLD_DATADIR= `select @@datadir`; @@ -314,3 +315,64 @@ connection default; check table t1,t2; drop table t1,t2; + +--echo # +--echo # MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +--echo # failed in cmp_rec_rec_simple_field +--echo # +let $row_format= `select @@global.innodb_default_row_format`; +if ($row_format != "redundant") +{ +CREATE TABLE t1(f1 int not null, f2 text, f3 text, f4 text, + f5 text, f6 text, f7 text, f8 text, + f9 text, f10 text, f11 text, f12 text, + f13 text, f14 text, f15 text, f16 text, + f17 text, f18 text, f19 text, f20 text, + f21 text, f22 text, f23 text, f24 text, + f25 text, PRIMARY KEY(f1))ENGINE=InnoDB; +let $error_code = ER_TOO_BIG_ROWSIZE; +if ($row_format == "dynamic") +{ + let $error_code = 0; +} + +--error $error_code +SET STATEMENT unique_checks=0,foreign_key_checks=0 FOR +INSERT INTO t1 VALUES(1, REPEAT('a', 1024), REPEAT('b', 1024), + REPEAT('c', 1024), REPEAT('d', 1024), + REPEAT('e', 1024), REPEAT('f', 1024), + REPEAT('g', 4096), REPEAT('h', 1024), + REPEAT('i', 1024), REPEAT('j', 1024), + REPEAT('k', 1024), REPEAT('l', 1024), + REPEAT('m', 1024), REPEAT('n', 1024), + REPEAT('o', 1024), REPEAT('p', 1024), + REPEAT('q', 1024), REPEAT('r', 1024), + REPEAT('s', 1024), REPEAT('t', 1024), + REPEAT('u', 1024), REPEAT('v', 1024), + REPEAT('w', 1024), REPEAT('x', 1024)), + (2, REPEAT('a', 1024), REPEAT('b', 1024), + REPEAT('c', 1024), REPEAT('d', 1024), + REPEAT('e', 1024), REPEAT('f', 1024), + REPEAT('g', 4096), REPEAT('h', 1024), + REPEAT('i', 1024), REPEAT('j', 1024), + REPEAT('k', 1024), REPEAT('l', 1024), + REPEAT('m', 1024), REPEAT('n', 1024), + REPEAT('o', 1024), REPEAT('p', 1024), + REPEAT('q', 1024), REPEAT('r', 1024), + REPEAT('s', 1024), REPEAT('t', 1024), + REPEAT('u', 1024), REPEAT('v', 1024), + REPEAT('w', 1024), REPEAT('x', 1024)), + (3, REPEAT('a', 1024), REPEAT('b', 1024), + REPEAT('c', 1024), REPEAT('d', 1024), + REPEAT('e', 1024), REPEAT('f', 1024), + REPEAT('g', 4096), REPEAT('h', 1024), + REPEAT('i', 1024), REPEAT('j', 1024), + REPEAT('k', 1024), REPEAT('l', 1024), + REPEAT('m', 1024), REPEAT('n', 1024), + REPEAT('o', 1024), REPEAT('p', 1024), + REPEAT('q', 1024), REPEAT('r', 1024), + REPEAT('s', 1024), REPEAT('t', 1024), + REPEAT('u', 1024), REPEAT('v', 1024), + REPEAT('w', 1024), REPEAT('x', 1024)); +DROP TABLE t1; +} diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test index a4e6592477f..79f6c23f7ee 100644 --- a/mysql-test/suite/innodb/t/insert_into_empty.test +++ b/mysql-test/suite/innodb/t/insert_into_empty.test @@ -484,7 +484,7 @@ ALTER TABLE t1 FORCE; let $page_size= `SELECT @@innodb_page_size`; let $error_code = 0; -if ($page_size == 4096) { +if ($page_size <= 16384) { let $error_code = ER_TOO_BIG_ROWSIZE; } @@ -593,4 +593,30 @@ INSERT INTO t1 VALUES(2,0); DELETE FROM t1; commit; DROP TABLE t1; + + +if ($page_size >= 16384) { +--echo # +--echo # MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +--echo # failed in cmp_rec_rec_simple_field +--echo # +CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), + (REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), + (REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), + (REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), + (REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), + (REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), + (REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), + (REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), + (REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), + (REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), + (REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), + (REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), + (REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), + (REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), + (REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), + (REPEAT('x',65535),'dd'); +DROP TABLE t1; +} --echo # End of 10.11 tests diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index f14ae66cf74..b4f260c3ab0 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -1057,7 +1057,8 @@ length in the field data @param heap heap to store the blob offset and length @return DB_SUCCESS if successful */ static dberr_t row_merge_write_blob_to_tmp_file( - dfield_t *field, merge_file_t *blob_file,mem_heap_t **heap) + dfield_t *field, uint32_t local_len, + merge_file_t *blob_file, mem_heap_t **heap) { if (blob_file->fd == OS_FILE_CLOSED) { @@ -1066,83 +1067,74 @@ static dberr_t row_merge_write_blob_to_tmp_file( return DB_OUT_OF_MEMORY; } uint64_t val= blob_file->offset; - uint32_t len= field->len; + uint32_t field_len= field->len; dberr_t err= os_file_write( IORequestWrite, "(bulk insert)", blob_file->fd, - field->data, blob_file->offset, len); + static_cast(field->data) + local_len, blob_file->offset, + field_len - local_len); if (err != DB_SUCCESS) return err; - byte *data= static_cast - (mem_heap_alloc(*heap, BTR_EXTERN_FIELD_REF_SIZE)); + byte *data= + static_cast(mem_heap_alloc(*heap, + local_len + BTR_EXTERN_FIELD_REF_SIZE)); + memcpy(data, field->data, local_len); + dfield_set_data(field, data, local_len + BTR_EXTERN_FIELD_REF_SIZE); + dfield_set_ext(field); + + data+= local_len; /* Write zeroes for first 8 bytes */ memset(data, 0, 8); /* Write offset for next 8 bytes */ mach_write_to_8(data + 8, val); /* Write length of the blob in 4 bytes */ - mach_write_to_4(data + 16, len); - blob_file->offset+= field->len; + mach_write_to_4(data + 16, field_len - local_len); + blob_file->offset+= (field_len - local_len); blob_file->n_rec++; - dfield_set_data(field, data, BTR_EXTERN_FIELD_REF_SIZE); - dfield_set_ext(field); return err; } -/** This function is invoked when tuple size is greater than -innodb_sort_buffer_size. Basically it recreates the tuple -by writing the blob field to the temporary file. -@param entry index fields to be encode the blob +/** Variable length field data or fixed length +character greater than 255 can be stored externally. Instead +of storing it externally, InnoDB should store it in temporary +file and write offset, length into the tuple field +@param fields index fields to be encode the blob @param blob_file file to store the blob data +@param index index for the tuple to be stored @param heap heap to store the blob offset and blob length -@return tuple which fits into sort_buffer_size */ -static dtuple_t* row_merge_buf_large_tuple(const dtuple_t &entry, - merge_file_t *blob_file, - mem_heap_t **heap) +@return error code */ +static +dberr_t row_merge_buf_blob(dfield_t *fields, + merge_file_t *blob_file, + const dict_index_t *index, + mem_heap_t **heap) { - if (!*heap) - *heap= mem_heap_create(DTUPLE_EST_ALLOC(entry.n_fields)); + const uint blob_prefix= dict_table_has_atomic_blobs(index->table) + ? 0 + : REC_ANTELOPE_MAX_INDEX_COL_LEN; + const uint min_local_len = blob_prefix + ? blob_prefix + FIELD_REF_SIZE + : 2 * FIELD_REF_SIZE; - dtuple_t *tuple= dtuple_copy(&entry, *heap); - for (ulint i= 0; i < tuple->n_fields; i++) + for (ulint i= index->first_user_field(); i < index->n_fields; i++) { - dfield_t *field= &tuple->fields[i]; - if (dfield_is_null(field) || field->len <= 2000) + dfield_t *field= &fields[i]; + if (dfield_is_null(field) || index->fields[i].fixed_len) continue; - dberr_t err= row_merge_write_blob_to_tmp_file(field, blob_file, heap); - if (err != DB_SUCCESS) - return nullptr; - } - - return tuple; -} - - -/** Write the field data whose length is more than 2000 bytes -into blob temporary file and write offset, length into the -tuple field -@param entry index fields to be encode the blob -@param n_fields number of fields in the entry -@param heap heap to store the blob offset and blob length -@param blob_file file to store the blob data */ -static dberr_t row_merge_buf_blob(const mtuple_t *entry, ulint n_fields, - mem_heap_t **heap, merge_file_t *blob_file) -{ - - if (!*heap) - *heap= mem_heap_create(100); - - for (ulint i= 0; i < n_fields; i++) - { - dfield_t *field= &entry->fields[i]; - if (dfield_is_null(field) || field->len <= 2000) - continue; - - dberr_t err= row_merge_write_blob_to_tmp_file(field, blob_file, heap); - if (err != DB_SUCCESS) - return err; + if (field->len > min_local_len && + DATA_BIG_COL(index->fields[i].col)) + { + if (*heap == nullptr) + *heap= mem_heap_create(256); + dberr_t err= + row_merge_write_blob_to_tmp_file(field, blob_prefix, + blob_file, heap); + if (err) + return err; + } } return DB_SUCCESS; @@ -1172,8 +1164,10 @@ dberr_t row_merge_buf_write(const row_merge_buf_t *buf, if (blob_file) { ut_ad(buf->index->is_primary()); - err = row_merge_buf_blob( - entry, n_fields, &blob_heap, blob_file); + + err = row_merge_buf_blob(entry->fields, + blob_file, buf->index, + &blob_heap); if (err != DB_SUCCESS) { goto func_exit; } @@ -1181,9 +1175,17 @@ dberr_t row_merge_buf_write(const row_merge_buf_t *buf, ulint rec_size= row_merge_buf_encode( &b, index, entry, n_fields); - if (blob_file && rec_size > srv_page_size) { - err = DB_TOO_BIG_RECORD; - goto func_exit; + if (blob_file) { + ulint rec_max_size = + (srv_page_size == UNIV_PAGE_SIZE_MAX) + ? REDUNDANT_REC_MAX_DATA_SIZE + : page_get_free_space_of_empty( + dict_table_is_comp( + index->table)) / 2; + if (rec_size > rec_max_size) { + err = DB_TOO_BIG_RECORD; + goto func_exit; + } } ut_ad(b < &block[srv_sort_buf_size]); @@ -3561,10 +3563,16 @@ in field data for the tuple @param tuple tuple to be inserted @param heap heap to allocate the memory for the blob storage @param blob_file file to handle blob data */ -static dberr_t row_merge_copy_blob_from_file(dtuple_t *tuple, mem_heap_t *heap, +static dberr_t row_merge_copy_blob_from_file(dtuple_t *tuple, + dict_index_t *index, + mem_heap_t *heap, merge_file_t *blob_file) { - for (ulint i = 0; i < dtuple_get_n_fields(tuple); i++) + ut_ad(tuple->n_fields == index->n_fields); + const uint blob_prefix= dict_table_has_atomic_blobs(index->table) + ? 0 + : REC_ANTELOPE_MAX_INDEX_COL_LEN; + for (ulint i = index->first_user_field(); i < tuple->n_fields; i++) { dfield_t *field= dtuple_get_nth_field(tuple, i); const byte *field_data= static_cast(dfield_get_data(field)); @@ -3575,15 +3583,18 @@ static dberr_t row_merge_copy_blob_from_file(dtuple_t *tuple, mem_heap_t *heap, ut_a(field_len >= BTR_EXTERN_FIELD_REF_SIZE); ut_ad(!dfield_is_null(field)); + field_data += blob_prefix; ut_ad(mach_read_from_8(field_data) == 0); uint64_t offset= mach_read_from_8(field_data + 8); uint32_t len= mach_read_from_4(field_data + 16); - byte *data= (byte*) mem_heap_alloc(heap, len); - if (dberr_t err= os_file_read(IORequestRead, blob_file->fd, data, + byte *data= (byte*) mem_heap_alloc(heap, blob_prefix + len); + memcpy(data, field->data, blob_prefix); + if (dberr_t err= os_file_read(IORequestRead, blob_file->fd, + data + blob_prefix, offset, len, nullptr)) return err; - dfield_set_data(field, data, len); + dfield_set_data(field, data, blob_prefix + len); } return DB_SUCCESS; @@ -3805,7 +3816,7 @@ row_merge_insert_index_tuples( if (!dtuple_get_n_ext(dtuple)) { } else if (blob_file) { error = row_merge_copy_blob_from_file( - dtuple, tuple_heap, blob_file); + dtuple, index, tuple_heap, blob_file); if (error != DB_SUCCESS) { break; } @@ -5172,6 +5183,7 @@ dberr_t row_merge_bulk_t::bulk_insert_buffered(const dtuple_t &row, const dict_index_t &ind, trx_t *trx) { + ut_ad(row.n_fields == ind.n_fields); dberr_t err= DB_SUCCESS; ulint i= 0; mem_heap_t *large_tuple_heap= nullptr; @@ -5197,8 +5209,16 @@ add_to_buf: if (buf->n_tuples == 0) { /* Tuple data size is greater than srv_sort_buf_size */ - dtuple_t *big_tuple= row_merge_buf_large_tuple( - row, &m_blob_file, &large_tuple_heap); + ut_ad(i == 0); + if (!large_tuple_heap) + large_tuple_heap= mem_heap_create(DTUPLE_EST_ALLOC(row.n_fields)); + + dtuple_t *big_tuple= dtuple_copy(&row, large_tuple_heap); + err= row_merge_buf_blob(big_tuple->fields, &m_blob_file, + &ind, &large_tuple_heap); + if (err) + goto func_exit; + if (row_merge_bulk_buf_add(buf, *ind.table, *big_tuple)) { i++;