[MDEV-25440] Assertion `cmp_rec_rec(rec, old_rec, offsets, old_offsets, m_index) > 0' failed in PageBulk::insert Created: 2021-04-17  Updated: 2023-11-10  Resolved: 2022-01-26

Status: Closed
Project: MariaDB Server
Component/s: Character Sets, Storage Engine - InnoDB
Affects Version/s: 10.2.2, 10.3.0, 10.4.0, 10.5.0, 10.2, 10.3, 10.4, 10.5, 10.6
Fix Version/s: 10.4.23, 10.5.14, 10.6.6, 10.7.2, 10.8.1

Type: Bug Priority: Major
Reporter: Elena Stepanova Assignee: Marko Mäkelä
Resolution: Fixed Votes: 1
Labels: corruption, regression-10.2

Issue Links:
PartOf
includes MDEV-25904 New collation functions to compare In... Closed
Problem/Incident
causes MDEV-27768 MDEV-25440: Assertion `(cs->state & 0... Closed
is caused by MDEV-9711 NO PAD collations Closed
Relates
relates to MDEV-6929 Port Facebook Prefix Index Queries Op... Closed
relates to MDEV-12255 innodb_prefix_index_cluster_optimizat... Closed
relates to MDEV-12486 Incorrect results with prefix_index_c... Closed
relates to MDEV-23600 SIGFPE in row_search_with_covering_pr... Closed
relates to MDEV-26744 MyISAM, Aria, MEMORY: CHAR+nopad does... Open
relates to MDEV-25449 Add MY_COLLATION_HANDLER::strnncollsp... Closed
relates to MDEV-26743 InnoDB: CHAR+nopad does not work well Closed

 Description   

--source include/have_innodb.inc
 
CREATE TABLE t1 (a CHAR(8), id INT, PRIMARY KEY (a,id)) COLLATE utf8_nopad_bin ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
INSERT INTO t1 VALUES ('',1);
ALTER TABLE t1 ROW_FORMAT=DYNAMIC;
INSERT INTO t1 VALUES ('',2);
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
 
# Cleanup
DROP TABLE t1;

10.2 1ac4d0c1

mysqld: /data/src/10.2/storage/innobase/btr/btr0bulk.cc:199: void PageBulk::insert(const rec_t*, rec_offs*): Assertion `cmp_rec_rec(rec, old_rec, offsets, old_offsets, m_index) > 0' failed.
210417 20:36:07 [ERROR] mysqld got signal 6 ;
 
#7  0x00007faeda8c7f36 in __GI___assert_fail (assertion=0x561417051f60 "cmp_rec_rec(rec, old_rec, offsets, old_offsets, m_index) > 0", file=0x561417051cc0 "/data/src/10.2/storage/innobase/btr/btr0bulk.cc", line=199, function=0x561417051ec0 "void PageBulk::insert(const rec_t*, rec_offs*)") at assert.c:101
#8  0x00005614161ae9ce in PageBulk::insert (this=0x61a00007da80, rec=0x6190000e9d7a ' ' <repeats 24 times>, "\200", offsets=0x6190000e9db0) at /data/src/10.2/storage/innobase/btr/btr0bulk.cc:199
#9  0x00005614161b2567 in BtrBulk::insert (this=0x6060000cae20, tuple=0x6310000dc8b0, level=0) at /data/src/10.2/storage/innobase/btr/btr0bulk.cc:978
#10 0x0000561415f33d18 in BtrBulk::insert (this=0x6060000cae20, tuple=0x6310000dc8b0) at /data/src/10.2/storage/innobase/include/btr0bulk.h:313
#11 0x0000561415f9341e in row_merge_insert_index_tuples (index=0x618000036508, old_table=0x6190000dd508, fd=-1, block=0x0, row_buf=0x615000032d88, btr_bulk=0x6060000cae20, table_total_rows=2, pct_progress=0, pct_cost=50, crypt_block=0x0, space=6, stage=0x0) at /data/src/10.2/storage/innobase/row/row0merge.cc:3533
#12 0x0000561415f8ba0d in row_merge_read_clustered_index (trx=0x7faed2528910, table=0x61f0000428f0, old_table=0x6190000dd508, new_table=0x6190000e8408, online=true, index=0x6190000e8170, fts_sort_idx=0x0, psort_info=0x0, files=0x6030000606d0, key_numbers=0x6190000e8180, n_index=1, add_cols=0x0, add_v=0x0, col_map=0x6190000e81d0, add_autoinc=18446744073709551615, sequence=..., block=0x7faec369f000 "", skip_pk_sort=true, tmpfd=0x7faec40f0ca0, stage=0x607000013960, pct_cost=50, crypt_block=0x0, eval_table=0x61f0000428f0) at /data/src/10.2/storage/innobase/row/row0merge.cc:2391
#13 0x0000561415f9985b in row_merge_build_indexes (trx=0x7faed2528910, old_table=0x6190000dd508, new_table=0x6190000e8408, online=true, indexes=0x6190000e8170, key_numbers=0x6190000e8180, n_indexes=1, table=0x61f0000428f0, add_cols=0x0, col_map=0x6190000e81d0, add_autoinc=18446744073709551615, sequence=..., skip_pk_sort=true, stage=0x607000013960, add_v=0x0, eval_table=0x61f0000428f0) at /data/src/10.2/storage/innobase/row/row0merge.cc:4683
#14 0x0000561415d82d39 in ha_innobase::inplace_alter_table (this=0x61d0001f8710, altered_table=0x61f0000428f0, ha_alter_info=0x7faec40f1a10) at /data/src/10.2/storage/innobase/handler/handler0alter.cc:6294
#15 0x00005614153c3092 in handler::ha_inplace_alter_table (this=0x61d0001f8710, altered_table=0x61f0000428f0, ha_alter_info=0x7faec40f1a10) at /data/src/10.2/sql/handler.h:3798
#16 0x00005614153a7689 in mysql_inplace_alter_table (thd=0x62a0000ba270, table_list=0x62b0000003b0, table=0x61f000041af0, altered_table=0x61f0000428f0, ha_alter_info=0x7faec40f1a10, inplace_supported=HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE, target_mdl_request=0x7faec40f1aa0, alter_ctx=0x7faec40f1e80) at /data/src/10.2/sql/sql_table.cc:7458
#17 0x00005614153b6e0d in mysql_alter_table (thd=0x62a0000ba270, new_db=0x62b0000009d0 "test", new_name=0x0, create_info=0x7faec40f3120, table_list=0x62b0000003b0, alter_info=0x7faec40f3030, order_num=0, order=0x0, ignore=false) at /data/src/10.2/sql/sql_table.cc:9627
#18 0x00005614154fe45e in Sql_cmd_alter_table::execute (this=0x62b0000009e0, thd=0x62a0000ba270) at /data/src/10.2/sql/sql_alter.cc:333
#19 0x0000561415184e50 in mysql_execute_command (thd=0x62a0000ba270) at /data/src/10.2/sql/sql_parse.cc:6020
#20 0x00005614151906ee in mysql_parse (thd=0x62a0000ba270, rawbuf=0x62b000000290 "ALTER TABLE t1 ROW_FORMAT=REDUNDANT", length=35, parser_state=0x7faec40f4c10, is_com_multi=false, is_next_command=false) at /data/src/10.2/sql/sql_parse.cc:7794
#21 0x00005614151697d0 in dispatch_command (command=COM_QUERY, thd=0x62a0000ba270, packet=0x629000136271 "ALTER TABLE t1 ROW_FORMAT=REDUNDANT", packet_length=35, is_com_multi=false, is_next_command=false) at /data/src/10.2/sql/sql_parse.cc:1827
#22 0x000056141516658f in do_command (thd=0x62a0000ba270) at /data/src/10.2/sql/sql_parse.cc:1381
#23 0x00005614154ee94a in do_handle_one_connection (connect=0x61100004aa70) at /data/src/10.2/sql/sql_connect.cc:1336
#24 0x00005614154ee20d in handle_one_connection (arg=0x61100004aa70) at /data/src/10.2/sql/sql_connect.cc:1241
#25 0x0000561416890516 in pfs_spawn_thread (arg=0x61600000e7f0) at /data/src/10.2/storage/perfschema/pfs.cc:1869
#26 0x00007faedadd7609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#27 0x00007faeda9b3293 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Reproducible on 10.2-10.6.
No obvious immediate problem on a non-debug build.



 Comments   
Comment by Marko Mäkelä [ 2021-04-19 ]

10.2 635b5ce355473167af64e116e403a51bfaed184b

Run till exit from #0  cmp_rec_rec (rec1=rec1@entry=0x7f6820095fb2 ' ' <repeats 24 times>, "\200", rec2=rec2@entry=0x7f68360c8087 ' ' <repeats 24 times>, "\200", offsets1=offsets1@entry=0x7f6820095fe0, 
    offsets2=0x7f6820096000, index=0x7f6820195fd0, nulls_unequal=nulls_unequal@entry=false, matched_fields=0x0) at /mariadb/10.2o/storage/innobase/rem/rem0cmp.cc:1116
0x0000562b9a677c60 in PageBulk::insert (this=this@entry=0x7f6820095960, rec=rec@entry=0x7f6820095fb2 ' ' <repeats 24 times>, "\200", offsets=offsets@entry=0x7f6820095fe0)
    at /mariadb/10.2o/storage/innobase/btr/btr0bulk.cc:199
199			ut_ad(cmp_rec_rec(rec, old_rec, offsets, old_offsets, m_index)
Value returned is $1 = -1

The records that are submitted to comparison are:
(a,id,DB_TRX_ID,DB_ROLL_PTR)=('',1,0x503,(insert)),('',2,0x50f,(insert))

The assertion fails because the field id in old_rec is larger (2) than in rec (1).

We have skip_pk_sort=true, because the table-rebuilding ALTER TABLE can use the pre-existing ordering of PRIMARY KEY(a,id). The ordering was violated already when the second row was inserted. The problem is that two empty CHAR with exactly the same contents are being treated unequal (-32 will be returned) when searching the position for the second INSERT:

#0  my_strnncollsp_utf8_nopad_bin (cs=0x55efb6c27720 <my_charset_utf8_nopad_bin>, a=0x7fdbdc035098 ' ' <repeats 24 times>, "\002", a_length=8, b=0x7fdbf332407e ' ' <repeats 24 times>, "\200", b_length=24)
    at /mariadb/10.2o/strings/strcoll.ic:222
#1  0x000055efb5ece628 in innobase_mysql_cmp (prtype=72548862, a=0x7fdbdc035098 ' ' <repeats 24 times>, "\002", a_length=8, b=0x7fdbf332407e ' ' <repeats 24 times>, "\200", b_length=24)
    at /mariadb/10.2o/storage/innobase/rem/rem0cmp.cc:88
#2  0x000055efb5ecee27 in cmp_whole_field (mtype=13, prtype=72548862, a=0x7fdbdc035098 ' ' <repeats 24 times>, "\002", a_length=8, b=0x7fdbf332407e ' ' <repeats 24 times>, "\200", b_length=24)
    at /mariadb/10.2o/storage/innobase/rem/rem0cmp.cc:374
#3  0x000055efb5ed066c in cmp_data (mtype=13, prtype=72548862, data1=0x7fdbdc035098 ' ' <repeats 24 times>, "\002", len1=8, data2=0x7fdbf332407e ' ' <repeats 24 times>, "\200", len2=24)
    at /mariadb/10.2o/storage/innobase/rem/rem0cmp.cc:449
#4  0x000055efb5ecf838 in cmp_dtuple_rec_with_match_bytes (dtuple=0x7fdbdc024f10, rec=0x7fdbf332407e ' ' <repeats 24 times>, "\200", index=0x7fdbdc0410f0, offsets=0x7fdbe00f10d0, matched_fields=0x7fdbe00f1048, 
    matched_bytes=0x7fdbe00f1050) at /mariadb/10.2o/storage/innobase/rem/rem0cmp.cc:860
#5  0x000055efb5ea1c68 in page_cur_search_with_match_bytes (block=0x7fdbf2e1c480, index=0x7fdbdc0410f0, tuple=0x7fdbdc024f10, mode=PAGE_CUR_LE, iup_matched_fields=0x7fdbe00f18d8, 
    iup_matched_bytes=0x7fdbe00f18e0, ilow_matched_fields=0x7fdbe00f18e8, ilow_matched_bytes=0x7fdbe00f18f0, cursor=0x7fdbe00f2908) at /mariadb/10.2o/storage/innobase/page/page0cur.cc:707
#6  0x000055efb600a1a3 in btr_cur_search_to_nth_level_func (index=0x7fdbdc0410f0, level=0, tuple=0x7fdbdc024f10, mode=PAGE_CUR_LE, latch_mode=2, cursor=0x7fdbe00f2900, has_search_latch=0, 
    file=0x55efb6504b60 "/mariadb/10.2o/storage/innobase/row/row0ins.cc", line=2606, mtr=0x7fdbe00f2c60, autoinc=0) at /mariadb/10.2o/storage/innobase/btr/btr0cur.cc:1650
#7  0x000055efb5ef2c8b in btr_pcur_open_low (index=0x7fdbdc0410f0, level=0, tuple=0x7fdbdc024f10, mode=PAGE_CUR_LE, latch_mode=2, cursor=0x7fdbe00f2900, 
    file=0x55efb6504b60 "/mariadb/10.2o/storage/innobase/row/row0ins.cc", line=2606, autoinc=0, mtr=0x7fdbe00f2c60) at /mariadb/10.2o/storage/innobase/include/btr0pcur.ic:457
#8  0x000055efb5ef9138 in row_ins_clust_index_entry_low (flags=0, mode=2, index=0x7fdbdc0410f0, n_uniq=2, entry=0x7fdbdc024f10, n_ext=0, thr=0x7fdbdc033930)
    at /mariadb/10.2o/storage/innobase/row/row0ins.cc:2605

Note: We are passing b_length=24 and a_length=8 here. One is in bytes, the other in characters.

In ROW_FORMAT=REDUNDANT, InnoDB reserves n*mbmaxlen bytes for each CHAR column. In other ROW_FORMAT, CHAR columns of variable-length encoded character sets occupy n*mbminlen to n*mbmaxlen bytes.

The ‘incorrect’ length of 8 (characters, not bytes) was set here:

#0  0x000055efb5eefc2d in dfield_set_data (field=0x7fdbdc024f48, data=0x7fdbdc035098, len=8) at /mariadb/10.2o/storage/innobase/include/data0data.ic:97
#1  0x000055efb5efb04d in row_ins_index_entry_set_vals (index=0x7fdbdc0410f0, entry=0x7fdbdc024f10, row=0x7fdbdc0337a8) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3375
#2  0x000055efb5efb125 in row_ins_index_entry_step (node=0x7fdbdc0336c8, thr=0x7fdbdc033930) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3402
#3  0x000055efb5efb522 in row_ins (node=0x7fdbdc0336c8, thr=0x7fdbdc033930) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3548
#4  0x000055efb5efb8b2 in row_ins_step (thr=0x7fdbdc033930) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3672
#5  0x000055efb5f18d25 in row_insert_for_mysql (mysql_rec=0x7fdbdc035098 ' ' <repeats 24 times>, "\002", prebuilt=0x7fdbdc033180) at /mariadb/10.2o/storage/innobase/row/row0mysql.cc:1408
#6  0x000055efb5de3fb0 in ha_innobase::write_row (this=0x7fdbdc027540, record=0x7fdbdc035098 ' ' <repeats 24 times>, "\002") at /mariadb/10.2o/storage/innobase/handler/ha_innodb.cc:8252

The problem was introduced by bar in MariaDB Server 10.2.2 by MDEV-9711 (NO_PAD collations).

An easy fix would be to reject the creation of indexes on NO_PAD collated CHAR columns. A much more risky and costly fix would be to rewrite large parts of InnoDB to take care of this somehow. It could cause a performance regression, and we might still miss some cases.

Comment by Marko Mäkelä [ 2021-04-19 ]

With some advice from bar, I patched cmp_data() so that it would trim the extra padding. This is only a proof of concept of a fix, and my debug assertions would almost certainly fail for other than ROW_FORMAT=REDUNDANT tables. But, the patch made the test case pass.

Comment by Marko Mäkelä [ 2021-04-27 ]

I am not sure how to fix this. InnoDB only stores column and index field lengths in bytes.

MDEV-6929 introduced something that would convert a column to bytes, incorrectly (MDEV-12255, MDEV-12486, MDEV-23600).

Unless ROW_FORMAT=REDUNDANT, a CHAR column of a variable-length encoding such as UTF-8 or UTF-16 is actually stored as variable length in InnoDB, occupying n*mbminlen to n*mbmaxlen bytes. For example, the CHAR(3) CHARACTER SET utf8mb3 column value 'äh' would be stored in exactly 3 bytes, using 2 bytes for the first character and 1 byte for the next one. Trailing spaces will be trimmed in order to reach the minimum column value of 3*1 bytes.

At the low level, such as cmp_data_data(), we only have the column lengths in bytes, not in characters at all. How are we supposed to compare CHAR columns in a NO PAD collation in this case? If one of the CHAR(3) strings is 'äh' and another is 'ah ' (note the trailing space), which comparison should be invoked?

Comment by Alexander Barkov [ 2021-04-27 ]

What is 'ah '? Shouldn't it be 'ah' followed by seven spaces, to make nine bytes total, where nine is CHAR(3)*3, where the last 3 is mbmaxlen for utf8mb3?

This is what the Field::ptr representation provides.

Comment by Alexander Barkov [ 2021-04-27 ]

Sorry, I reread your comment and understood. It's an InnoDB specific representation which is different from Field::ptr representation.

Comment by Alexander Barkov [ 2021-04-27 ]

The function added by MDEV-25449 currently does not cover this case. You'll have to extend the values with trailing spaces to be at least 3 characters long before passing them to strnncollsp_nchars().

In the currently MDEV-25549 implementation, strnncollsp_nchars() can only skip redundant trailing spaces. But in cannot pad missing trailing spaces. This will need extra coding.

Comment by Marko Mäkelä [ 2021-04-27 ]

bar, thank you, and I am sorry for not making this clear in the beginning.
For InnoDB, it would be easiest if we could avoid the conversion from bytes to characters. The truncation of CHAR data happens in the function row_mysql_store_col_in_innobase_format() for other than ROW_FORMAT=REDUNDANT:

	} else if (comp && type == DATA_MYSQL
		   && dtype_get_mbminlen(dtype) == 1
		   && dtype_get_mbmaxlen(dtype) > 1) {
		/* In some cases we strip trailing spaces from UTF-8 and other
		multibyte charsets, from FIXED-length CHAR columns, to save
		space. UTF-8 would otherwise normally use 3 * the string length
		bytes to store an ASCII string! */

At that point, we do know the length of the full column in bytes:

		n_chars = dtype_get_len(dtype) / dtype_get_mbmaxlen(dtype);

But, in rem0cmp.cc we would currently not have that information available. We would have to add an additional parameter to many functions, which would not only be a large change but could also hurt performance. At that level, we basically only know the length of both fields in bytes, the collation code, and that the column is CHAR (mtype==DATA_MYSQL for CHAR encodings and collations other than latin1_swedish_ci).

For InnoDB, it would be easier if we had something that would override the NO PAD attribute of the collation, and just compare the two CHAR index fields as they would be compared without NO PAD. As far as I understand, CHAR should always be logically space-padded (hence NO PAD should make no difference), and we would never compare VARCHAR to CHAR inside InnoDB.

Comment by Sergei Golubchik [ 2022-01-20 ]

server part is ok as per MDEV-25904.
I don't think I can review InnoDB part.

Comment by Marko Mäkelä [ 2022-01-20 ]

I think that the InnoDB part can be best covered by Random Query Generator, possibly tweaked to increase the use of nopad collations.

Comment by Roel Van de Paar [ 2022-01-21 ]

Testing bb-10.4-MDEV-25440 using pquery with a full list of all possible charset and collations as per git grep 'charset_info.*nopad' include/m_ctype.h annd a mixed of REDUNDANT/DYNAMIC/notspecified for every table.

Comment by Alexander Barkov [ 2022-01-24 ]

The patches in https://github.com/mariadb/server/tree/bb-10.4-MDEV-25440 are OK to push.

Comment by Roel Van de Paar [ 2022-01-26 ]

OK to push, no directly related issues observed during testing.

Comment by Marko Mäkelä [ 2022-01-26 ]

This was not fixed in the 10.2 or 10.3 series, because the fix depends on MDEV-25904 which was only fixed in 10.4 and later.

Comment by Roel Van de Paar [ 2022-02-08 ]

Additional testing was done on the second iteration of the patch as requested by Marko.

Comment by Roel Van de Paar [ 2022-02-08 ]

Logged MDEV-27768 MDEV-25440: Assertion `(cs->state & 0x20000) == 0' failed in my_strnncollsp_nchars_generic_8bit

Generated at Thu Feb 08 09:37:43 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.