Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-20636

Potential SPATIAL INDEX corruption with ROW_FORMAT=COMPRESSED

    Details

      Description

      The function rtr_update_mbr_field() contains some problematic code updating the maximum bounded rectangle in the leftmost node pointer record of a leftmost node pointer page. If btr_cur_update_alloc_zip() does not hold, then the code would fall back to something else (goto update_mbr), which is only prepared to deal with leaf pages. The following patch simulates the condition for any ROW_FORMAT:

      diff --git a/storage/innobase/gis/gis0rtree.cc b/storage/innobase/gis/gis0rtree.cc
      index 2965a19a5e0..b1ede932daa 100644
      --- a/storage/innobase/gis/gis0rtree.cc
      +++ b/storage/innobase/gis/gis0rtree.cc
      @@ -184,6 +184,7 @@ rtr_index_build_node_ptr(
       	return(tuple);
       }
       
      +#if 0
       /**************************************************************//**
       In-place update the mbr field of a spatial index row.
       @return true if update is successful */
      @@ -259,6 +260,7 @@ rtr_update_mbr_field_in_place(
       
       	return(true);
       }
      +#endif
       
       /**************************************************************//**
       Update the mbr field of a spatial index row.
      @@ -333,6 +335,7 @@ rtr_update_mbr_field(
       	}
       
       	if (rec_info & REC_INFO_MIN_REC_FLAG) {
      +#if 0
       		/* When the rec is minimal rec in this level, we do
       		 in-place update for avoiding it move to other place. */
       
      @@ -349,6 +352,7 @@ rtr_update_mbr_field(
       				/* If there's not enought space for
       				inplace update zip page, we do delete
       				insert. */
      +#endif
       				ins_suc = false;
       
       				/* Since btr_cur_update_alloc_zip could
      @@ -361,6 +365,7 @@ rtr_update_mbr_field(
       				}
       
       				goto update_mbr;
      +#if 0
       			}
       
       			/* Record could be repositioned */
      @@ -400,6 +405,7 @@ rtr_update_mbr_field(
       			page_cur_delete_rec(btr_cur_get_page_cur(cursor2),
       					    index, offsets2, mtr);
       		}
      +#endif
       	} else if (page_get_n_recs(page) == 1) {
       		/* When there's only one rec in the page, we do insert/delete to
       		avoid page merge. */
      

      With this patch applied, an assertion would fail easily:

      10.2 4afe9d4b6dbffcebe8f9e5842fb79f0ff22b5aa0 with the above patch

      CURRENT_TEST: innodb_gis.kill_server
      mysqltest: At line 26: query 'CALL insert_t1(5000)' failed: 2013: Lost connection to MySQL server during query
      Version: '10.2.28-MariaDB-debug-log'  socket: '/dev/shm/10.2/mysql-test/var/tmp/34/mysqld.1.sock'  port: 16680  Source distribution
      mysqld: /mariadb/10.2o/storage/innobase/page/page0cur.cc:2335: void page_cur_delete_rec(page_cur_t *, const dict_index_t *, const ulint *, mtr_t *): Assertion `page_is_leaf(page)' failed.
      #7  0x000055fd0894f3d8 in page_cur_delete_rec (cursor=0x7fa51ab884c8, index=0x7fa4cc18cf88, offsets=0x7fa4cc054b28, mtr=0x7fa51ab89698) at /mariadb/10.2o/storage/innobase/page/page0cur.cc:2335
      #8  0x000055fd08bd58bf in rtr_update_mbr_field (cursor=0x7fa51ab884c0, offsets=0x7fa4cc054b28, cursor2=0x0, child_page=0x0, mbr=0x0, new_rec=0x0, mtr=0x7fa51ab89698) at /mariadb/10.2o/storage/innobase/gis/gis0rtree.cc:452
      #9  0x000055fd08bd8346 in rtr_adjust_upper_level (sea_cur=<optimized out>, block=<optimized out>, new_block=<optimized out>, mbr=<optimized out>, new_mbr=<optimized out>, direction=<optimized out>, mtr=<optimized out>, flags=<optimized out>) at /mariadb/10.2o/storage/innobase/gis/gis0rtree.cc:698
      #10 rtr_page_split_and_insert (flags=<optimized out>, cursor=<optimized out>, offsets=<optimized out>, heap=<optimized out>, tuple=<optimized out>, n_ext=<optimized out>, mtr=0x7fa51ab89698) at /mariadb/10.2o/storage/innobase/gis/gis0rtree.cc:1289
      #11 0x000055fd08a8ef27 in btr_root_raise_and_insert (flags=0, cursor=0x7fa51ab88a38, offsets=0x7fa51ab88a08, heap=0x7fa51ab88a20, tuple=0x7fa4cc066d68, n_ext=0, mtr=0x7fa51ab89698) at /mariadb/10.2o/storage/innobase/btr/btr0btr.cc:2025
      #12 0x000055fd08ab6922 in btr_cur_pessimistic_insert (flags=0, cursor=0x7fa51ab88a38, offsets=0x7fa51ab88a08, heap=0x7fa51ab88a20, entry=0x7fa4cc066d68, rec=0x7fa51ab89ba0, big_rec=0x7fa51ab88b20, n_ext=140347094629904, thr=0x7fa4cc096070, mtr=0x7fa51ab89698) at /mariadb/10.2o/storage/innobase/btr/btr0cur.cc:3395
      #13 0x000055fd0899de9d in row_ins_sec_index_entry_low (flags=<optimized out>, mode=<optimized out>, index=0x7fa4cc18cf88, offsets_heap=<optimized out>, heap=<optimized out>, entry=<optimized out>, trx_id=0, thr=0x7fa4cc096070, dup_chk_only=<optimized out>) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3091
      #14 0x000055fd0899fe66 in row_ins_sec_index_entry (index=<optimized out>, entry=<optimized out>, thr=<optimized out>, dup_chk_only=<optimized out>) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3263
      #15 0x000055fd089a0a16 in row_ins_index_entry (index=0x7fa4cc18cf88, entry=0x7fa4cc066d68, thr=<optimized out>) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3297
      #16 row_ins_index_entry_step (node=<optimized out>, thr=<optimized out>) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3445
      #17 row_ins (node=<optimized out>, thr=<optimized out>) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3581
      #18 row_ins_step (thr=0x7fa4cc096070) at /mariadb/10.2o/storage/innobase/row/row0ins.cc:3705
      #19 0x000055fd089ba400 in row_insert_for_mysql (mysql_rec=0x7fa4cc088ee8 "\376n\001", prebuilt=0x7fa4cc0958a8) at /mariadb/10.2o/storage/innobase/row/row0mysql.cc:1414
      #20 0x000055fd08890218 in ha_innobase::write_row (this=0x7fa4cc095120, record=0x7fa4cc088ee8 "\376n\001") at /mariadb/10.2o/storage/innobase/handler/ha_innodb.cc:8231
      #21 0x000055fd086d6b89 in handler::ha_write_row (this=0x7fa4cc095120, buf=0x7fa4cc088ee8 "\376n\001") at /mariadb/10.2o/sql/handler.cc:6089
      #22 0x000055fd084d52fe in write_record (thd=0x7fa4cc000cf8, table=0x7fa4cc080da8, info=0x7fa51ab8a880) at /mariadb/10.2o/sql/sql_insert.cc:1941
      #23 0x000055fd084d27f2 in mysql_insert (thd=<optimized out>, table_list=<optimized out>, fields=..., values_list=..., update_fields=..., update_values=..., duplic=DUP_ERROR, ignore=<optimized out>) at /mariadb/10.2o/sql/sql_insert.cc:1066
      #24 0x000055fd084f3750 in mysql_execute_command (thd=0x7fa4cc000cf8) at /mariadb/10.2o/sql/sql_parse.cc:4167
      #25 0x000055fd088158a2 in sp_instr_stmt::exec_core (this=0x7fa4cc071080, thd=0x2, nextp=0x7fa51ab8be5c) at /mariadb/10.2o/sql/sp_head.cc:3248
      #26 0x000055fd08814bcc in sp_lex_keeper::reset_lex_and_exec_core (this=0x7fa4cc0710c0, thd=0x7fa4cc000cf8, nextp=0x7fa51ab8be5c, open_tables=<optimized out>, instr=0x7fa4cc071080) at /mariadb/10.2o/sql/sp_head.cc:3011
      #27 0x000055fd0881532b in sp_instr_stmt::execute (this=0x7fa4cc071080, thd=0x7fa4cc000cf8, nextp=0x7fa51ab8be5c) at /mariadb/10.2o/sql/sp_head.cc:3164
      #28 0x000055fd0881038b in sp_head::execute (this=0x7fa4cc06d930, thd=0x7fa4cc000cf8, merge_da_on_success=<optimized out>) at /mariadb/10.2o/sql/sp_head.cc:1329
      #29 0x000055fd08811f21 in sp_head::execute_procedure (this=0x7fa4cc06d930, thd=<optimized out>, args=<optimized out>) at /mariadb/10.2o/sql/sp_head.cc:2118
      #30 0x000055fd084fb7a2 in do_execute_sp (thd=0x7fa4cc000cf8, sp=<optimized out>) at /mariadb/10.2o/sql/sql_parse.cc:2954
      #31 0x000055fd084f7a5a in mysql_execute_command (thd=0x7fa4cc000cf8) at /mariadb/10.2o/sql/sql_parse.cc:5572
      #32 0x000055fd084ef0e5 in mysql_parse (thd=0x7fa4cc000cf8, rawbuf=0x7fa4cc011940 "CALL insert_t1(5000)", length=<optimized out>, parser_state=0x7fa51ab8d300, is_com_multi=<optimized out>, is_next_command=<optimized out>) at /mariadb/10.2o/sql/sql_parse.cc:7759
      #33 0x000055fd084ebe80 in dispatch_command (command=<optimized out>, thd=0x7fa4cc000cf8, packet=0x7fa4cc008499 "CALL insert_t1(5000)", packet_length=3422624064, is_com_multi=<optimized out>, is_next_command=<optimized out>) at /mariadb/10.2o/sql/sql_parse.cc:1830
      

      Affected tests include the following: innodb_gis.kill_server innodb_gis.rtree_create_inplace innodb_gis.rtree_compress2 innodb_gis.rtree_recovery innodb_gis.rtree_compress innodb_gis.rtree_purge innodb_gis.rtree_search innodb_gis.rtree innodb_gis.multi_pk innodb_gis.rtree_concurrent_srch innodb_gis.rtree_rollback1 innodb_gis.rtree_split innodb_gis.update_root innodb_gis.rtree_debug innodb_gis.rtree_rollback2 innodb_gis.rollback

      Also, the redo log records MLOG_COMP_REC_UPDATE_IN_PLACE and MLOG_REC_UPDATE_IN_PLACE are supposed to be written for leaf-page records, not for node pointer records. There is one more strangeness related to that:

      		if (!rtr_update_mbr_field_in_place(index, rec,
      						   offsets, mbr, mtr)) {
      			return(false);
      		}
       
      		if (page_zip) {
      			page_zip_write_rec(page_zip, rec, index, offsets, 0);
      		}
      

      The function rtr_update_mbr_field_in_place() would only return false if redo logging is disabled. But, even if redo logging were disabled, we should carry on with the rest of the function. That condition is likely never taken, because the return would clearly lead to a memory leak of

      	heap = mem_heap_create(100);
      

      and I do not remember reports for such leaks.

        Attachments

          Activity

            People

            • Assignee:
              marko Marko Mäkelä
              Reporter:
              marko Marko Mäkelä
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated: