[MDEV-20874] Wrong handling of 'table was dropped' error in purge thread Created: 2019-10-23  Updated: 2023-04-27

Status: Stalled
Project: MariaDB Server
Component/s: Storage Engine - InnoDB
Affects Version/s: 10.2, 10.3, 10.4
Fix Version/s: 10.4

Type: Bug Priority: Major
Reporter: Aleksey Midenkov Assignee: Aleksey Midenkov
Resolution: Unresolved Votes: 0
Labels: None

Issue Links:
Problem/Incident
is caused by MDEV-15114 ASAN heap-use-after-free in mem_heap_... Closed
is caused by MDEV-15855 Assertion `mysql_table' failed in inn... Closed
Relates
relates to MDEV-16678 Use MDL for innodb background threads... Closed
relates to MDEV-20865 Store foreign key info in TABLE_SHARE In Progress
relates to MDEV-20876 Remove node->vcol_op_failed() method Closed
relates to MDEV-21051 Store and read foreign key info into/... Closed

 Description   

There is possibility to get exception:

#1  0x00007f610fcb2535 in __GI_abort () at abort.c:79
#2  0x000000000131649a in ib::fatal::~fatal (this=0x7f61000a9b18) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/ut/ut0ut.cc:597
#3  0x0000000001200172 in row_mysql_handle_errors (new_err=0x7f61000aa574, trx=0x7f610927e130, thr=0x7f609c033cd8, savept=0x7f61000aa578) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0mysql.cc:790
#4  0x00000000012049a0 in row_update_for_mysql (prebuilt=0x7f609c032f58) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0mysql.cc:1908
#5  0x000000000101474e in ha_innobase::update_row (this=0x7f609c02c000, old_row=0x7f609c026150 "\377\001", new_row=0x7f609c026148 "\377\002") at /home/midenok/src/mariadb/10.5b/src/storage/innobase/handler/ha_innodb.cc:8777
#6  0x0000000000bf62aa in handler::ha_update_row (this=0x7f609c02c000, old_data=0x7f609c026150 "\377\001", new_data=0x7f609c026148 "\377\002") at /home/midenok/src/mariadb/10.5b/src/sql/handler.cc:6750
#7  0x000000000097da11 in mysql_update (thd=0x7f609c000d08, table_list=0x7f609c0145f8, fields=..., values=..., conds=0x0, order_num=0, order=0x0, limit=18446744073709551615, ignore=false, found_return=0x7f61000ac118, updated_return=0x7f61000ac110) at /home/midenok/src/mariadb/10.5b/src/sql/sql_update.cc:1046
#8  0x0000000000843e38 in mysql_execute_command (thd=0x7f609c000d08) at /home/midenok/src/mariadb/10.5b/src/sql/sql_parse.cc:4347
#9  0x000000000083b410 in mysql_parse (thd=0x7f609c000d08, rawbuf=0x7f609c014520 "update t1 set fld1= 2", length=21, parser_state=0x7f61000ad5c0, is_com_multi=false, is_next_command=false) at /home/midenok/src/mariadb/10.5b/src/sql/sql_parse.cc:7998

on wrongly thrown DB_OUT_OF_MEMORY from:

#0  innobase_allocate_row_for_vcol (thd=0x7f609c000d08, index=0x7f609c0829a8, heap=0x7f61000a8170, table=0x7f61000a8168, record=0x7f61000a8158, storage=0x7f61000a8160) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/handler/ha_innodb.cc:20860
#1  0x00000000011d688a in row_ins_foreign_fill_virtual (cascade=0x7f609c02eba8, rec=0x7f6108fc407d "", index=0x7f609c0829a8, node=0x7f609c033a00, foreign=0x7f609c083608, err=0x7f61000a89dc) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0ins.cc:968
#2  0x00000000011cc02e in row_ins_foreign_check_on_constraint (thr=0x7f609c033cd8, foreign=0x7f609c083608, pcur=0x7f61000a9550, entry=0x7f609c0280d0, mtr=0x7f61000a9050) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0ins.cc:1366
#3  0x00000000011c9bde in row_ins_check_foreign_constraint (check_ref=0, foreign=0x7f609c083608, table=0x7f609c026ee8, entry=0x7f609c0280d0, thr=0x7f609c033cd8) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0ins.cc:1848
#4  0x000000000127da44 in row_upd_check_references_constraints (node=0x7f609c033a00, pcur=0x7f609c026c60, table=0x7f609c026ee8, index=0x7f609c02aec8, offsets=0x7f609c080328, thr=0x7f609c033cd8, mtr=0x7f61000a9da0) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0upd.cc:296
#5  0x000000000127c794 in row_upd_clust_rec_by_insert (node=0x7f609c033a00, index=0x7f609c02aec8, thr=0x7f609c033cd8, referenced=1, mtr=0x7f61000a9da0) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0upd.cc:2759
#6  0x000000000127aa41 in row_upd_clust_step (node=0x7f609c033a00, thr=0x7f609c033cd8) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0upd.cc:3210
#7  0x0000000001276b22 in row_upd (node=0x7f609c033a00, thr=0x7f609c033cd8) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0upd.cc:3289
#8  0x0000000001276635 in row_upd_step (thr=0x7f609c033cd8) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0upd.cc:3433
#9  0x00000000012048e1 in row_update_for_mysql (prebuilt=0x7f609c032f58) at /home/midenok/src/mariadb/10.5b/src/storage/innobase/row/row0mysql.cc:1889
#10 0x000000000101474e in ha_innobase::update_row (this=0x7f609c02c000, old_row=0x7f609c026150 "\377\001", new_row=0x7f609c026148 "\377\002") at /home/midenok/src/mariadb/10.5b/src/storage/innobase/handler/ha_innodb.cc:8777
#11 0x0000000000bf62aa in handler::ha_update_row (this=0x7f609c02c000, old_data=0x7f609c026150 "\377\001", new_data=0x7f609c026148 "\377\002") at /home/midenok/src/mariadb/10.5b/src/sql/handler.cc:6750
#12 0x000000000097da11 in mysql_update (thd=0x7f609c000d08, table_list=0x7f609c0145f8, fields=..., values=..., conds=0x0, order_num=0, order=0x0, limit=18446744073709551615, ignore=false, found_return=0x7f61000ac118, updated_return=0x7f61000ac110) at /home/midenok/src/mariadb/10.5b/src/sql/sql_update.cc:1046
#13 0x0000000000843e38 in mysql_execute_command (thd=0x7f609c000d08) at /home/midenok/src/mariadb/10.5b/src/sql/sql_parse.cc:4347
#14 0x000000000083b410 in mysql_parse (thd=0x7f609c000d08, rawbuf=0x7f609c014520 "update t1 set fld1= 2", length=21, parser_state=0x7f61000ad5c0, is_com_multi=false, is_next_command=false) at /home/midenok/src/mariadb/10.5b/src/sql/sql_parse.cc:7998

the code from frame 0 returns error like this:

20854           if (!*table)
20855                   *table= innodb_find_table_for_vc(thd, index->table);
20856   
20857           /* For purge thread, there is a possiblity that table could have
20858           dropped, corrupted or unaccessible. */
20859           if (!*table)
20860                   return true;

but up in frame 1 it is handled like a memory error:

968             if (innobase_allocate_row_for_vcol(thd, index, &v_heap,
969                                                &mysql_table,
970                                                &record, &vcol_storage)) {
971                     if (v_heap) mem_heap_free(v_heap);
972                     *err = DB_OUT_OF_MEMORY;
973                     goto func_exit;
974             }

Make proper error handling by allowing innobase_allocate_row_for_vcol() return dberr_t and handle it properly up the stack. This requires to refactor a row of functions which were not supposed to fail with error (?!).

Handle DB_OUT_OF_MEMORY in row_mysql_handle_errors().

Affected tests: gcol.innodb_virtual_fk, innodb_zip.bug56680

More analysis



 Comments   
Comment by Marko Mäkelä [ 2020-05-19 ]

A non-debug assertion that was added by the change is failing here, while executing a test that does not use virtual columns.

Comment by Marko Mäkelä [ 2020-05-19 ]

I would like to see a successful run of the change, based on the latest 10.2, with an up-to-date commit date.

The change is rather large, and it is adding an output parameter to a large number of functions. It would be cleaner to use a return value rather than an output parameter. dberr_t already defines DB_SUCCESS as 0.

The Google C++ Style Guide discourages the use of non-const reference parameters in functions.

In some functions, we use DB_SUCCESS_LOCKED_REC as a special ‘success’ value. Something like could also be done in places like row_purge_poss_sec() to avoid introducing an output parameter.

I think that this change will need extensive RQG testing.

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