From b95d2778aa9d4abe5331f9e6f593fd05e1cb2de6 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Mon, 15 Aug 2011 16:39:53 +0300 Subject: [PATCH] Fixes bugs found by testcase for lp:815022 and lp:726374 "ma_blockrec.c:3000: write_block_record: Assertion `cur_block[1].page_count == 0' failed with a multi-index Aria workload" The issues was: - For some tables with a lot of not packed fields, we didn't allocate enough memory in head page which caused DBUG_ASSERT's - Removed wrong DBUG_ASSERT() - Fixed a problem with underflow() where it generates a key page where all keys didn't fit. - Max key length is now limited by block_size/3 (was block_size /2). This is required for underflow() to work with packed keys. mysql-test/lib/v1/mysql-test-run.pl: Remove --alignment=8 as this doesn't work on 64 bit systems mysql-test/suite/maria/r/small_blocksize.result: Test case for Aria bug mysql-test/suite/maria/t/small_blocksize-master.opt: Test case for Aria bug mysql-test/suite/maria/t/small_blocksize.test: Test case for Aria bug storage/maria/ha_maria.cc: Fixed comment storage/maria/ma_bitmap.c: Fixed wrong variable usage in find_where_to_split_row() where we allocated too little memory for head page. We did not take into account space for head extents (long VARCHAR) when trying to split row on head page. This caused us to allocate too little space from bitmap which lead to ASSERT failures later. storage/maria/ma_blockrec.c: Made some argument const (to ensure they was not accidently changed) Removed wrong DBUG_ASSERT() storage/maria/ma_blockrec.h: Removed not used variable storage/maria/ma_delete.c: Added my_afree() in case of error More comments and DBUG_ASSERT() for underflow() storage/maria/ma_open.c: Make keyinfo->underflow_block_length smaller for packed keys. This has to be done as for long packed keys, underflow() otherwise generates a key page where all keys didn't fit. storage/maria/ma_page.c: New DBUG_ASSERT() storage/maria/ma_write.c: Fixed comment storage/maria/maria_def.h: We have to have space for at least 3 keys on a key page. (Otherwise the underflow() code doesn't work for packed keys, even when we have an underflow() for an empty key page) --- mysql-test/lib/v1/mysql-test-run.pl | 1 - .../suite/maria/r/small_blocksize.result | 75 +++++++++++++++++++ .../suite/maria/t/small_blocksize-master.opt | 1 + mysql-test/suite/maria/t/small_blocksize.test | 35 +++++++++ storage/maria/ha_maria.cc | 2 +- storage/maria/ma_bitmap.c | 34 +++++---- storage/maria/ma_blockrec.c | 11 +-- storage/maria/ma_blockrec.h | 1 - storage/maria/ma_delete.c | 27 +++++-- storage/maria/ma_open.c | 57 ++++++++------ storage/maria/ma_page.c | 1 + storage/maria/ma_write.c | 1 - storage/maria/maria_def.h | 2 +- 13 files changed, 198 insertions(+), 50 deletions(-) create mode 100644 mysql-test/suite/maria/r/small_blocksize.result create mode 100644 mysql-test/suite/maria/t/small_blocksize-master.opt create mode 100644 mysql-test/suite/maria/t/small_blocksize.test diff --git a/mysql-test/lib/v1/mysql-test-run.pl b/mysql-test/lib/v1/mysql-test-run.pl index 5671f58bbfe..a47d58a5180 100755 --- a/mysql-test/lib/v1/mysql-test-run.pl +++ b/mysql-test/lib/v1/mysql-test-run.pl @@ -5285,7 +5285,6 @@ sub valgrind_arguments { else { mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option - mtr_add_arg($args, "--alignment=8"); mtr_add_arg($args, "--leak-check=yes"); mtr_add_arg($args, "--num-callers=16"); mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) diff --git a/mysql-test/suite/maria/r/small_blocksize.result b/mysql-test/suite/maria/r/small_blocksize.result new file mode 100644 index 00000000000..41d9a561a4d --- /dev/null +++ b/mysql-test/suite/maria/r/small_blocksize.result @@ -0,0 +1,75 @@ +DROP TABLE if exists t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (col_longtext_ucs2 longtext, col_longtext_utf8 longtext, col_varchar_255_ucs2_key varchar(255), col_set_utf8 set ('a','b'), col_char_255_ucs2 char(255), col_char_255_ucs2_key char(255), col_enum_ucs2 enum ('a','b'), col_varchar_255_ucs2 varchar(255), col_longtext_ucs2_key longtext, col_longtext_utf8_key longtext, col_enum_utf8 enum ('a','b'), col_varchar_255_utf8_key varchar(1024), col_varchar_255_utf8 varchar(255), col_enum_ucs2_key enum ('a','b'), col_enum_utf8_key enum ('a','b'), col_set_utf8_key set ('a','b'), col_char_255_utf8 char(255), pk integer auto_increment, col_set_ucs2_key set ('a','b'), col_char_255_utf8_key char(255), col_set_ucs2 set ('a','b'), primary key (pk)) ENGINE=aria; +INSERT INTO t1 ( col_char_255_utf8, col_varchar_255_utf8_key, col_longtext_utf8_key ) VALUES ( 'lggnqojgqectqlkvskffihliqcwoakzzzjvhkqlwjybkngdbubskflpmzegdrk', REPEAT( 'a', 627 ), 'mlggnqojgqectqlkvskffihliqcwoakzzzjvhkqlwjybkngdbubskflpmzegdrklnipcmzbtwdqfnyinqfohgtiwmvfpbuslgobjhslxnaybcyebhsrlipnuvalhmvhlwbwujtvjsdrbyapfzprnxfgtrukwhywtkaoupsaogxsjxhqjkidvnpeytjgndtnrrbm' ); +UPDATE t1 SET col_varchar_255_utf8 = REPEAT('a', 197 ); +UPDATE t1 SET col_char_255_utf8 = 'bmjihzjtxegprqfvmczyzbavjuozkyxrlxvqyzcfvsjrhcccqnecyohzhzbgsbqkqvzmtlhtlcgzheirkyfwczoolilkrfimfnuoapyylbghdhdgfebjjajfoigagozypqtrflrvdiwfgqalsqbmlllsanvtuuutiaastqtbzeoaawl'; +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +drop table t1; +create table t1 (a int primary key auto_increment, e1 enum('a','b'), e2 enum('a','b'), vl int, bl int, c char(10), v1 varchar(10000), v2 varchar(10000), b1 blob, b2 blob) engine=aria; +insert into t1 (vl,bl) values (10,10),(100,100),(1000,1000),(5000,5000),(8000,12000); +update t1 set c="test", v1=repeat('a',vl),v2=repeat('b',vl/2),b1=repeat('c',bl),b2=repeat('d',bl); +insert into t1 (vl,bl) values (10,10),(100,100),(1000,1000),(1000,5000); +update t1 set c="test", v1=repeat(vl/4,'a'),v2=repeat(vl/5,'b'),b1=repeat(b1*2,'c'),b2=repeat(bl/2,'d'); +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +Warning 1292 Truncated incorrect INTEGER value: 'a' +Warning 1292 Truncated incorrect INTEGER value: 'b' +Warning 1292 Truncated incorrect INTEGER value: 'c' +Warning 1292 Truncated incorrect INTEGER value: 'd' +update t1 set c="test", v1=repeat('a',vl/4),v2=repeat('b',vl/5),b1=repeat('c',bl*2),b2=repeat('d',bl/2); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',vl),v2=repeat('b',vl),b1=repeat('c',bl*2),b2=repeat('d',bl/2); +update t1 set c="test", v1=repeat('a',vl/2),v2=repeat('b',vl/2),b1=repeat('c',bl/2),b2=repeat('d',bl/2); +update t1 set c="test", v1=repeat('a',vl/4),v2=repeat('b',vl/4),b1=repeat('c',bl/4),b2=repeat('d',bl/4); +update t1 set c="test", v1=repeat('a',vl/20),v2=repeat('b',vl),b1=repeat('c',bl/20),b2=repeat('d',bl/20); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',vl/100),b1=repeat('c',bl/100); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',vl),b1=repeat('c',bl); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',10),v2=repeat('b',10); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',2000),v2=repeat('b',2000); +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +drop table t1; diff --git a/mysql-test/suite/maria/t/small_blocksize-master.opt b/mysql-test/suite/maria/t/small_blocksize-master.opt new file mode 100644 index 00000000000..59fd35a7846 --- /dev/null +++ b/mysql-test/suite/maria/t/small_blocksize-master.opt @@ -0,0 +1 @@ +--aria-block-size=1024 diff --git a/mysql-test/suite/maria/t/small_blocksize.test b/mysql-test/suite/maria/t/small_blocksize.test new file mode 100644 index 00000000000..673558e6b20 --- /dev/null +++ b/mysql-test/suite/maria/t/small_blocksize.test @@ -0,0 +1,35 @@ +DROP TABLE if exists t1; + +# +# Test of extending updated rows. +# This caused failures in lp:815022 +# +CREATE TABLE t1 (col_longtext_ucs2 longtext, col_longtext_utf8 longtext, col_varchar_255_ucs2_key varchar(255), col_set_utf8 set ('a','b'), col_char_255_ucs2 char(255), col_char_255_ucs2_key char(255), col_enum_ucs2 enum ('a','b'), col_varchar_255_ucs2 varchar(255), col_longtext_ucs2_key longtext, col_longtext_utf8_key longtext, col_enum_utf8 enum ('a','b'), col_varchar_255_utf8_key varchar(1024), col_varchar_255_utf8 varchar(255), col_enum_ucs2_key enum ('a','b'), col_enum_utf8_key enum ('a','b'), col_set_utf8_key set ('a','b'), col_char_255_utf8 char(255), pk integer auto_increment, col_set_ucs2_key set ('a','b'), col_char_255_utf8_key char(255), col_set_ucs2 set ('a','b'), primary key (pk)) ENGINE=aria; +INSERT INTO t1 ( col_char_255_utf8, col_varchar_255_utf8_key, col_longtext_utf8_key ) VALUES ( 'lggnqojgqectqlkvskffihliqcwoakzzzjvhkqlwjybkngdbubskflpmzegdrk', REPEAT( 'a', 627 ), 'mlggnqojgqectqlkvskffihliqcwoakzzzjvhkqlwjybkngdbubskflpmzegdrklnipcmzbtwdqfnyinqfohgtiwmvfpbuslgobjhslxnaybcyebhsrlipnuvalhmvhlwbwujtvjsdrbyapfzprnxfgtrukwhywtkaoupsaogxsjxhqjkidvnpeytjgndtnrrbm' ); +UPDATE t1 SET col_varchar_255_utf8 = REPEAT('a', 197 ); +UPDATE t1 SET col_char_255_utf8 = 'bmjihzjtxegprqfvmczyzbavjuozkyxrlxvqyzcfvsjrhcccqnecyohzhzbgsbqkqvzmtlhtlcgzheirkyfwczoolilkrfimfnuoapyylbghdhdgfebjjajfoigagozypqtrflrvdiwfgqalsqbmlllsanvtuuutiaastqtbzeoaawl'; +check table t1; +drop table t1; + +create table t1 (a int primary key auto_increment, e1 enum('a','b'), e2 enum('a','b'), vl int, bl int, c char(10), v1 varchar(10000), v2 varchar(10000), b1 blob, b2 blob) engine=aria; + +insert into t1 (vl,bl) values (10,10),(100,100),(1000,1000),(5000,5000),(8000,12000); +update t1 set c="test", v1=repeat('a',vl),v2=repeat('b',vl/2),b1=repeat('c',bl),b2=repeat('d',bl); +insert into t1 (vl,bl) values (10,10),(100,100),(1000,1000),(1000,5000); +update t1 set c="test", v1=repeat(vl/4,'a'),v2=repeat(vl/5,'b'),b1=repeat(b1*2,'c'),b2=repeat(bl/2,'d'); +update t1 set c="test", v1=repeat('a',vl/4),v2=repeat('b',vl/5),b1=repeat('c',bl*2),b2=repeat('d',bl/2); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',vl),v2=repeat('b',vl),b1=repeat('c',bl*2),b2=repeat('d',bl/2); +update t1 set c="test", v1=repeat('a',vl/2),v2=repeat('b',vl/2),b1=repeat('c',bl/2),b2=repeat('d',bl/2); +update t1 set c="test", v1=repeat('a',vl/4),v2=repeat('b',vl/4),b1=repeat('c',bl/4),b2=repeat('d',bl/4); +update t1 set c="test", v1=repeat('a',vl/20),v2=repeat('b',vl),b1=repeat('c',bl/20),b2=repeat('d',bl/20); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',vl/100),b1=repeat('c',bl/100); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',vl),b1=repeat('c',bl); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',10),v2=repeat('b',10); +insert into t1 (vl,bl) values (100,100); +update t1 set c="test", v1=repeat('a',2000),v2=repeat('b',2000); +check table t1; +drop table t1; diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 58264513da9..33609afcb9b 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -874,7 +874,7 @@ double ha_maria::scan_time() } /* - We need to be able to store at least two keys on an index page as the + We need to be able to store at least 2 keys on an index page as the splitting algorithms depends on this. (With only one key on a page we also can't use any compression, which may make the index file much larger) diff --git a/storage/maria/ma_bitmap.c b/storage/maria/ma_bitmap.c index ac3d23b7079..a98ef71604d 100644 --- a/storage/maria/ma_bitmap.c +++ b/storage/maria/ma_bitmap.c @@ -1853,7 +1853,7 @@ static void use_head(MARIA_HA *info, pgcache_page_no_t page, uint size, find_where_to_split_row() share Maria share row Information of what is in the row (from calc_record_size()) - extents_length Number of bytes needed to store all extents + extents Max number of extents we have to store in header split_size Free size on the page (The head length must be less than this) @@ -1862,7 +1862,7 @@ static void use_head(MARIA_HA *info, pgcache_page_no_t page, uint size, */ static uint find_where_to_split_row(MARIA_SHARE *share, MARIA_ROW *row, - uint extents_length, uint split_size) + uint extents, uint split_size) { uint *lengths, *lengths_end; /* @@ -1872,19 +1872,20 @@ static uint find_where_to_split_row(MARIA_SHARE *share, MARIA_ROW *row, - One extent */ uint row_length= (row->min_length + - size_to_store_key_length(extents_length) + + size_to_store_key_length(extents) + ROW_EXTENT_SIZE); - DBUG_ASSERT(row_length < split_size); + DBUG_ASSERT(row_length <= split_size); + /* Store first in all_field_lengths the different parts that are written to the row. This needs to be in same order as in ma_block_rec.c::write_block_record() */ - row->null_field_lengths[-3]= extents_length; + row->null_field_lengths[-3]= extents * ROW_EXTENT_SIZE; row->null_field_lengths[-2]= share->base.fixed_not_null_fields_length; row->null_field_lengths[-1]= row->field_lengths_length; for (lengths= row->null_field_lengths - EXTRA_LENGTH_FIELDS, - lengths_end= (lengths + share->base.pack_fields - share->base.blobs + + lengths_end= (lengths + share->base.fields - share->base.blobs + EXTRA_LENGTH_FIELDS); lengths < lengths_end; lengths++) { if (row_length + *lengths > split_size) @@ -2040,18 +2041,19 @@ my_bool _ma_bitmap_find_place(MARIA_HA *info, MARIA_ROW *row, head_length+= ELEMENTS_RESERVED_FOR_MAIN_PART * ROW_EXTENT_SIZE; /* The first segment size is stored in 'row_length' */ - row_length= find_where_to_split_row(share, row, extents_length, + row_length= find_where_to_split_row(share, row, row->extents_count + + ELEMENTS_RESERVED_FOR_MAIN_PART-1, max_page_size); full_page_size= MAX_TAIL_SIZE(share->block_size); position= 0; - if (head_length - row_length <= full_page_size) + rest_length= head_length - row_length; + if (rest_length <= full_page_size) position= ELEMENTS_RESERVED_FOR_MAIN_PART -2; /* Only head and tail */ if (find_head(info, row_length, position)) goto abort; row->space_on_head_page= row_length; - rest_length= head_length - row_length; if (write_rest_of_head(info, position, rest_length)) goto abort; @@ -2137,16 +2139,22 @@ my_bool _ma_bitmap_find_new_place(MARIA_HA *info, MARIA_ROW *row, /* Allocate enough space */ head_length+= ELEMENTS_RESERVED_FOR_MAIN_PART * ROW_EXTENT_SIZE; - /* The first segment size is stored in 'row_length' */ - row_length= find_where_to_split_row(share, row, extents_length, free_size); + /* + The first segment size is stored in 'row_length' + We have to add ELEMENTS_RESERVED_FOR_MAIN_PART here as the extent + information may be up to this size when the header splits. + */ + row_length= find_where_to_split_row(share, row, row->extents_count + + ELEMENTS_RESERVED_FOR_MAIN_PART-1, + free_size); position= 0; - if (head_length - row_length < MAX_TAIL_SIZE(share->block_size)) + rest_length= head_length - row_length; + if (rest_length <= MAX_TAIL_SIZE(share->block_size)) position= ELEMENTS_RESERVED_FOR_MAIN_PART -2; /* Only head and tail */ use_head(info, page, row_length, position); row->space_on_head_page= row_length; - rest_length= head_length - row_length; if (write_rest_of_head(info, position, rest_length)) goto abort; diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c index 36cd63667e7..ad5ec3fe2b9 100644 --- a/storage/maria/ma_blockrec.c +++ b/storage/maria/ma_blockrec.c @@ -1721,7 +1721,7 @@ struct st_row_pos_info static my_bool get_head_or_tail_page(MARIA_HA *info, - MARIA_BITMAP_BLOCK *block, + const MARIA_BITMAP_BLOCK *block, uchar *buff, uint length, uint page_type, enum pagecache_page_lock lock, struct st_row_pos_info *res) @@ -1821,7 +1821,7 @@ crashed: */ static my_bool get_rowpos_in_head_or_tail_page(MARIA_HA *info, - MARIA_BITMAP_BLOCK *block, + const MARIA_BITMAP_BLOCK *block, uchar *buff, uint length, uint page_type, enum pagecache_page_lock lock, @@ -2257,7 +2257,7 @@ static void store_extent_info(uchar *to, for (block= first_block, end_block= first_block+count ; block < end_block; block++) { - /* The following is only false for marker blocks */ + /* The following is only false for marker (unused) blocks */ if (likely(block->used & BLOCKUSED_USED)) { uint page_count= block->page_count; @@ -3088,9 +3088,10 @@ static my_bool write_block_record(MARIA_HA *info, extent_data= row_extents_second_part + ((last_head_block - head_block) - 2) * ROW_EXTENT_SIZE; } - DBUG_ASSERT(uint2korr(extent_data+5) & TAIL_BIT); + /* Write information for tail block in the reserved space */ page_store(extent_data, head_tail_block->page); - int2store(extent_data + PAGE_STORE_SIZE, head_tail_block->page_count); + pagerange_store(extent_data + PAGE_STORE_SIZE, + head_tail_block->page_count); } } else diff --git a/storage/maria/ma_blockrec.h b/storage/maria/ma_blockrec.h index 2a323d16ffa..45f5613bb60 100644 --- a/storage/maria/ma_blockrec.h +++ b/storage/maria/ma_blockrec.h @@ -59,7 +59,6 @@ /* Minimum header size needed for a new row */ #define BASE_ROW_HEADER_SIZE FLAG_SIZE -#define TRANS_ROW_EXTRA_HEADER_SIZE TRANSID_SIZE #define PAGE_TYPE_MASK 7 enum en_page_type { UNALLOCATED_PAGE, HEAD_PAGE, TAIL_PAGE, BLOB_PAGE, MAX_PAGE_TYPE }; diff --git a/storage/maria/ma_delete.c b/storage/maria/ma_delete.c index ab66499ecfe..a4e485066f5 100644 --- a/storage/maria/ma_delete.c +++ b/storage/maria/ma_delete.c @@ -571,6 +571,7 @@ static int del(MARIA_HA *info, MARIA_KEY *key, endpos= leaf_page->buff + leaf_length; tmp_key.keyinfo= keyinfo; tmp_key.data= keybuff; + next_buff= 0; if (!(key_start= _ma_get_last_key(&tmp_key, leaf_page, endpos))) DBUG_RETURN(-1); @@ -597,9 +598,11 @@ static int del(MARIA_HA *info, MARIA_KEY *key, /* underflow writes "next_page" to disk */ ret_value= underflow(info, keyinfo, leaf_page, &next_page, endpos); - if (ret_value == 0 && leaf_page->size > - share->max_index_block_size) + if (ret_value < 0) + goto err; + if (leaf_page->size > share->max_index_block_size) { + DBUG_ASSERT(ret_value == 0); ret_value= (_ma_split_page(info, key, leaf_page, share->max_index_block_size, (uchar*) 0, 0, 0, @@ -632,6 +635,7 @@ static int del(MARIA_HA *info, MARIA_KEY *key, goto err; } my_afree(next_buff); + DBUG_ASSERT(leaf_page->size <= share->max_index_block_size); DBUG_RETURN(ret_value); } @@ -709,10 +713,14 @@ static int del(MARIA_HA *info, MARIA_KEY *key, KEY_OP_DEBUG_LOG_ADD_2)) goto err; + DBUG_ASSERT(leaf_page->size <= share->max_index_block_size); DBUG_RETURN(new_leaf_length <= (info->quick_mode ? MARIA_MIN_KEYBLOCK_LENGTH : (uint) keyinfo->underflow_block_length)); err: + if (next_buff) + my_afree(next_buff); + DBUG_RETURN(-1); } /* del */ @@ -731,9 +739,18 @@ err: leaf_page is saved to disk Caller must save anc_buff + For the algoritm to work, we have to ensure for packed keys that + key_length + (underflow_length + max_block_length + key_length) / 2 + <= block_length. + From which follows that underflow_length <= block_length - key_length *3 + For not packed keys we have: + (underflow_length + max_block_length + key_length) / 2 <= block_length + From which follows that underflow_length < block_length - key_length + This is ensured by setting of underflow_block_length. + @return @retval 0 ok - @retval 1 ok, but anc_buff did underflow + @retval 1 ok, but anc_page did underflow @retval -1 error */ @@ -1153,7 +1170,7 @@ static int underflow(MARIA_HA *info, MARIA_KEYDEF *keyinfo, _ma_kpointer(info,leaf_key.data + leaf_key.data_length + leaf_key.ref_length, leaf_page->pos); - /* Save key in anc_page */ + /* Save parting key found by _ma_find_half_pos() in anc_page */ DBUG_DUMP("anc_buff", anc_buff, new_anc_length); DBUG_DUMP_KEY("key_to_anc", &leaf_key); anc_end_pos= anc_buff + new_anc_length; @@ -1191,6 +1208,7 @@ static int underflow(MARIA_HA *info, MARIA_KEYDEF *keyinfo, bmove(leaf_buff+p_length+t_length, half_pos, tmp_length); (*keyinfo->store_key)(keyinfo,leaf_buff+p_length, &key_inserted); new_leaf_length= tmp_length + t_length + p_length; + DBUG_ASSERT(new_leaf_length <= share->max_index_block_size); leaf_page->size= new_leaf_length; leaf_page->flag= page_flag; @@ -1232,7 +1250,6 @@ static int underflow(MARIA_HA *info, MARIA_KEYDEF *keyinfo, /* Log changes to next page This contains original data with some suffix data deleted - */ DBUG_ASSERT(new_buff_length <= buff_length); if (_ma_log_suffix(&next_page, buff_length, new_buff_length)) diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c index f9ec70499f1..498904f89a7 100644 --- a/storage/maria/ma_open.c +++ b/storage/maria/ma_open.c @@ -563,21 +563,40 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) share->block_size= share->base.block_size; /* Convenience */ share->max_index_block_size= share->block_size - KEYPAGE_CHECKSUM_SIZE; + share->keypage_header= ((share->base.born_transactional ? + LSN_STORE_SIZE + TRANSID_SIZE : + 0) + KEYPAGE_KEYID_SIZE + KEYPAGE_FLAG_SIZE + + KEYPAGE_USED_SIZE); { HA_KEYSEG *pos=share->keyparts; uint32 ftkey_nr= 1; for (i=0 ; i < keys ; i++) { - share->keyinfo[i].share= share; - disk_pos=_ma_keydef_read(disk_pos, &share->keyinfo[i]); - share->keyinfo[i].key_nr= i; + MARIA_KEYDEF *keyinfo= &share->keyinfo[i]; + keyinfo->share= share; + disk_pos=_ma_keydef_read(disk_pos, keyinfo); + keyinfo->key_nr= i; + + /* See ma_delete.cc::underflow() */ + if (!(keyinfo->flag & (HA_BINARY_PACK_KEY | HA_PACK_KEY))) + keyinfo->underflow_block_length= keyinfo->block_length/3; + else + { + /* Packed key, ensure we don't get overflow in underflow() */ + keyinfo->underflow_block_length= + max((int) (share->max_index_block_size - keyinfo->maxlength * 3), + (int) (share->keypage_header + share->base.key_reflength)); + set_if_smaller(keyinfo->underflow_block_length, + keyinfo->block_length/3); + } + disk_pos_assert(share, - disk_pos + share->keyinfo[i].keysegs * HA_KEYSEG_SIZE, + disk_pos + keyinfo->keysegs * HA_KEYSEG_SIZE, end_pos); - if (share->keyinfo[i].key_alg == HA_KEY_ALG_RTREE) + if (keyinfo->key_alg == HA_KEY_ALG_RTREE) share->have_rtree= 1; - share->keyinfo[i].seg=pos; - for (j=0 ; j < share->keyinfo[i].keysegs; j++,pos++) + keyinfo->seg=pos; + for (j=0 ; j < keyinfo->keysegs; j++,pos++) { disk_pos=_ma_keyseg_read(disk_pos, pos); if (pos->type == HA_KEYTYPE_TEXT || @@ -595,25 +614,25 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) else if (pos->type == HA_KEYTYPE_BINARY) pos->charset= &my_charset_bin; } - if (share->keyinfo[i].flag & HA_SPATIAL) + if (keyinfo->flag & HA_SPATIAL) { #ifdef HAVE_SPATIAL uint sp_segs=SPDIMS*2; - share->keyinfo[i].seg=pos-sp_segs; - share->keyinfo[i].keysegs--; + keyinfo->seg=pos-sp_segs; + keyinfo->keysegs--; versioning= 0; #else my_errno=HA_ERR_UNSUPPORTED; goto err; #endif } - else if (share->keyinfo[i].flag & HA_FULLTEXT) + else if (keyinfo->flag & HA_FULLTEXT) { versioning= 0; DBUG_ASSERT(fulltext_keys); { uint k; - share->keyinfo[i].seg=pos; + keyinfo->seg=pos; for (k=0; k < FT_SEGS; k++) { *pos= ft_keysegs[k]; @@ -628,8 +647,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) } if (!share->ft2_keyinfo.seg) { - memcpy(&share->ft2_keyinfo, &share->keyinfo[i], - sizeof(MARIA_KEYDEF)); + memcpy(&share->ft2_keyinfo, keyinfo, sizeof(MARIA_KEYDEF)); share->ft2_keyinfo.keysegs=1; share->ft2_keyinfo.flag=0; share->ft2_keyinfo.keylength= @@ -639,10 +657,10 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) share->ft2_keyinfo.end=pos; setup_key_functions(& share->ft2_keyinfo); } - share->keyinfo[i].ftkey_nr= ftkey_nr++; + keyinfo->ftkey_nr= ftkey_nr++; } - setup_key_functions(share->keyinfo+i); - share->keyinfo[i].end=pos; + setup_key_functions(keyinfo); + keyinfo->end=pos; pos->type=HA_KEYTYPE_END; /* End */ pos->length=share->base.rec_reflength; pos->null_bit=0; @@ -686,10 +704,6 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) share->base.null_bytes + share->base.pack_bytes + test(share->options & HA_OPTION_CHECKSUM)); - share->keypage_header= ((share->base.born_transactional ? - LSN_STORE_SIZE + TRANSID_SIZE : - 0) + KEYPAGE_KEYID_SIZE + KEYPAGE_FLAG_SIZE + - KEYPAGE_USED_SIZE); share->kfile.file= kfile; if (open_flags & HA_OPEN_COPY) @@ -1580,7 +1594,6 @@ uchar *_ma_keydef_read(uchar *ptr, MARIA_KEYDEF *keydef) keydef->keylength = mi_uint2korr(ptr); ptr+= 2; keydef->minlength = mi_uint2korr(ptr); ptr+= 2; keydef->maxlength = mi_uint2korr(ptr); ptr+= 2; - keydef->underflow_block_length=keydef->block_length/3; keydef->version = 0; /* Not saved */ keydef->parser = &ft_default_parser; keydef->ftkey_nr = 0; diff --git a/storage/maria/ma_page.c b/storage/maria/ma_page.c index bcd50f028ec..9f6a7406ea7 100644 --- a/storage/maria/ma_page.c +++ b/storage/maria/ma_page.c @@ -193,6 +193,7 @@ my_bool _ma_write_keypage(MARIA_PAGE *page, enum pagecache_page_lock lock, nod_flag= _ma_test_if_nod(share, buff); DBUG_ASSERT(page->size == page_length); + DBUG_ASSERT(page->size <= share->max_index_block_size); DBUG_ASSERT(page->flag == _ma_get_keypage_flag(share, buff)); if (page->pos < share->base.keystart || diff --git a/storage/maria/ma_write.c b/storage/maria/ma_write.c index 9a6f2f1a440..629e07b872d 100644 --- a/storage/maria/ma_write.c +++ b/storage/maria/ma_write.c @@ -1068,7 +1068,6 @@ int _ma_split_page(MARIA_HA *info, MARIA_KEY *key, MARIA_PAGE *split_page, Returns pointer to start of key. key will contain the key. - return_key_length will contain the length of key after_key will contain the position to where the next key starts */ diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index 8ac33c51cc1..dcdb500f3a5 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -740,7 +740,7 @@ struct st_maria_handler { length=mi_uint2korr((key)+1)+3; } \ } -#define maria_max_key_length() ((maria_block_size - MAX_KEYPAGE_HEADER_SIZE)/2 - MARIA_INDEX_OVERHEAD_SIZE) +#define maria_max_key_length() ((maria_block_size - MAX_KEYPAGE_HEADER_SIZE)/3 - MARIA_INDEX_OVERHEAD_SIZE) #define get_pack_length(length) ((length) >= 255 ? 3 : 1) #define _ma_have_versioning(info) ((info)->row_flag & ROW_FLAG_TRANSID)