From 887383d4e496277f79500f38ae789d2d5595476c Mon Sep 17 00:00:00 2001
From: unknown <guilhem@gbichot2.(none)>
Date: Mon, 30 Oct 2006 12:44:33 +0100
Subject: [PATCH] Manually importing Ingo's fix for BUG#22119 "Changing
 MI_KEY_BLOCK_LENGTH makes a wrong myisamchk" in the Maria tree as it is
 really needed to get "ma_test_all" to pass (this bug showed up in Maria
 first, not in MyISAM). Now ma_test_all does not have corruption messages
 about test2 anymore, and shows the same output as mi_test_all except that
 ma_test_all has this at the start: lt-maria_chk: MARIA file test1
 lt-maria_chk: warning: Size of indexfile is: 8192          Should be: 16384
 MARIA-table 'test1' is usable but should be fixed This was already true
 before importing the bugfix. Wonder if normal. NOTE: this bugfix is currently
 in 5.1-engines, in a few days will be in the main 5.1, then we'll merge 5.1
 into Maria: this will merge the bugfix into storage/myisam, but there will be
 no need to apply it to storage/maria again. I just couldn't wait a few days
 for the 5.1-engines->5.1 merge to be allowed.

mysql-test/r/maria.result:
  result update
mysql-test/t/maria.test:
  test for BUG#22119
storage/maria/ma_check.c:
  fix for BUG#22119
---
 mysql-test/r/maria.result | 45 ++++++++++++++++++++
 mysql-test/t/maria.test   | 44 +++++++++++++++++++
 storage/maria/ma_check.c  | 90 +++++++++++++++++++++++++++++++--------
 3 files changed, 162 insertions(+), 17 deletions(-)

diff --git a/mysql-test/r/maria.result b/mysql-test/r/maria.result
index 396b5da4bc..39c7c0b65d 100644
--- a/mysql-test/r/maria.result
+++ b/mysql-test/r/maria.result
@@ -1613,5 +1613,50 @@ create table t1 (a int not null, key key_block_size=1024 (a));
 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '=1024 (a))' at line 1
 create table t1 (a int not null, key `a` key_block_size=1024 (a));
 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'key_block_size=1024 (a))' at line 1
+CREATE TABLE t1 (
+c1 INT,
+c2 VARCHAR(300),
+KEY (c1) KEY_BLOCK_SIZE 1024,
+KEY (c2) KEY_BLOCK_SIZE 8192
+);
+INSERT INTO t1 VALUES (10, REPEAT('a', CEIL(RAND(10) * 300))),
+(11, REPEAT('b', CEIL(RAND() * 300))),
+(12, REPEAT('c', CEIL(RAND() * 300))),
+(13, REPEAT('d', CEIL(RAND() * 300))),
+(14, REPEAT('e', CEIL(RAND() * 300))),
+(15, REPEAT('f', CEIL(RAND() * 300))),
+(16, REPEAT('g', CEIL(RAND() * 300))),
+(17, REPEAT('h', CEIL(RAND() * 300))),
+(18, REPEAT('i', CEIL(RAND() * 300))),
+(19, REPEAT('j', CEIL(RAND() * 300))),
+(20, REPEAT('k', CEIL(RAND() * 300))),
+(21, REPEAT('l', CEIL(RAND() * 300))),
+(22, REPEAT('m', CEIL(RAND() * 300))),
+(23, REPEAT('n', CEIL(RAND() * 300))),
+(24, REPEAT('o', CEIL(RAND() * 300))),
+(25, REPEAT('p', CEIL(RAND() * 300))),
+(26, REPEAT('q', CEIL(RAND() * 300))),
+(27, REPEAT('r', CEIL(RAND() * 300))),
+(28, REPEAT('s', CEIL(RAND() * 300))),
+(29, REPEAT('t', CEIL(RAND() * 300))),
+(30, REPEAT('u', CEIL(RAND() * 300))),
+(31, REPEAT('v', CEIL(RAND() * 300))),
+(32, REPEAT('w', CEIL(RAND() * 300))),
+(33, REPEAT('x', CEIL(RAND() * 300))),
+(34, REPEAT('y', CEIL(RAND() * 300))),
+(35, REPEAT('z', CEIL(RAND() * 300)));
+INSERT INTO t1 SELECT * FROM t1;
+INSERT INTO t1 SELECT * FROM t1;
+CHECK TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	check	status	OK
+REPAIR TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	repair	status	OK
+DELETE FROM t1 WHERE c1 >= 10;
+CHECK TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	check	status	OK
+DROP TABLE t1;
 End of 5.1 tests
 set global storage_engine=MyISAM;
diff --git a/mysql-test/t/maria.test b/mysql-test/t/maria.test
index fa9983690c..4130831c48 100644
--- a/mysql-test/t/maria.test
+++ b/mysql-test/t/maria.test
@@ -939,6 +939,50 @@ create table t1 (a int not null, key key_block_size=1024 (a));
 --error 1064
 create table t1 (a int not null, key `a` key_block_size=1024 (a));
 
+
+#
+# Bug#22119 - Changing MI_KEY_BLOCK_LENGTH makes a wrong myisamchk
+#
+CREATE TABLE t1 (
+  c1 INT,
+  c2 VARCHAR(300),
+  KEY (c1) KEY_BLOCK_SIZE 1024,
+  KEY (c2) KEY_BLOCK_SIZE 8192
+  );
+INSERT INTO t1 VALUES (10, REPEAT('a', CEIL(RAND(10) * 300))),
+  (11, REPEAT('b', CEIL(RAND() * 300))),
+  (12, REPEAT('c', CEIL(RAND() * 300))),
+  (13, REPEAT('d', CEIL(RAND() * 300))),
+  (14, REPEAT('e', CEIL(RAND() * 300))),
+  (15, REPEAT('f', CEIL(RAND() * 300))),
+  (16, REPEAT('g', CEIL(RAND() * 300))),
+  (17, REPEAT('h', CEIL(RAND() * 300))),
+  (18, REPEAT('i', CEIL(RAND() * 300))),
+  (19, REPEAT('j', CEIL(RAND() * 300))),
+  (20, REPEAT('k', CEIL(RAND() * 300))),
+  (21, REPEAT('l', CEIL(RAND() * 300))),
+  (22, REPEAT('m', CEIL(RAND() * 300))),
+  (23, REPEAT('n', CEIL(RAND() * 300))),
+  (24, REPEAT('o', CEIL(RAND() * 300))),
+  (25, REPEAT('p', CEIL(RAND() * 300))),
+  (26, REPEAT('q', CEIL(RAND() * 300))),
+  (27, REPEAT('r', CEIL(RAND() * 300))),
+  (28, REPEAT('s', CEIL(RAND() * 300))),
+  (29, REPEAT('t', CEIL(RAND() * 300))),
+  (30, REPEAT('u', CEIL(RAND() * 300))),
+  (31, REPEAT('v', CEIL(RAND() * 300))),
+  (32, REPEAT('w', CEIL(RAND() * 300))),
+  (33, REPEAT('x', CEIL(RAND() * 300))),
+  (34, REPEAT('y', CEIL(RAND() * 300))),
+  (35, REPEAT('z', CEIL(RAND() * 300)));
+INSERT INTO t1 SELECT * FROM t1;
+INSERT INTO t1 SELECT * FROM t1;
+CHECK TABLE t1;
+REPAIR TABLE t1;
+DELETE FROM t1 WHERE c1 >= 10;
+CHECK TABLE t1;
+DROP TABLE t1;
+
 --echo End of 5.1 tests
 
 eval set global storage_engine=$default;
diff --git a/storage/maria/ma_check.c b/storage/maria/ma_check.c
index 03802ba698..f997b90a61 100644
--- a/storage/maria/ma_check.c
+++ b/storage/maria/ma_check.c
@@ -228,11 +228,12 @@ static int check_k_link(HA_CHECK *param, register MARIA_HA *info, uint nr)
   my_off_t next_link;
   uint block_size=(nr+1)*MARIA_MIN_KEY_BLOCK_LENGTH;
   ha_rows records;
-  char llbuff[21],*buff;
+  char llbuff[21], llbuff2[21], *buff;
   DBUG_ENTER("check_k_link");
+  DBUG_PRINT("enter", ("block_size: %u", block_size));
 
   if (param->testflag & T_VERBOSE)
-    printf("block_size %4d:",block_size);
+    printf("block_size %4u:", block_size); /* purecov: tested */
 
   next_link=info->s->state.key_del[nr];
   records= (ha_rows) (info->state->key_file_length / block_size);
@@ -242,14 +243,46 @@ static int check_k_link(HA_CHECK *param, register MARIA_HA *info, uint nr)
       DBUG_RETURN(1);
     if (param->testflag & T_VERBOSE)
       printf("%16s",llstr(next_link,llbuff));
-    if (next_link > info->state->key_file_length ||
-	next_link & (info->s->blocksize-1))
+
+    /* Key blocks must lay within the key file length entirely. */
+    if (next_link + block_size > info->state->key_file_length)
+    {
+      /* purecov: begin tested */
+      _ma_check_print_error(param, "Invalid key block position: %s  "
+                            "key block size: %u  file_length: %s",
+                            llstr(next_link, llbuff), block_size,
+                            llstr(info->state->key_file_length, llbuff2));
+      DBUG_RETURN(1);
+      /* purecov: end */
+    }
+    
+    /* Key blocks must be aligned at MARIA_MIN_KEY_BLOCK_LENGTH. */
+    if (next_link & (MARIA_MIN_KEY_BLOCK_LENGTH - 1))
+    {
+      /* purecov: begin tested */
+      _ma_check_print_error(param, "Mis-aligned key block: %s  "
+                            "minimum key block length: %u",
+                            llstr(next_link, llbuff), MARIA_MIN_KEY_BLOCK_LENGTH);
       DBUG_RETURN(1);
+      /* purecov: end */
+    }
+    
+    /*
+      Read the key block with MARIA_MIN_KEY_BLOCK_LENGTH to find next link.
+      If the key cache block size is smaller than block_size, we can so
+      avoid unecessary eviction of cache block.
+    */
     if (!(buff=key_cache_read(info->s->key_cache,
                               info->s->kfile, next_link, DFLT_INIT_HITS,
-                              (byte*) info->buff,
-			      maria_block_size, block_size, 1)))
+                              (byte*) info->buff, MARIA_MIN_KEY_BLOCK_LENGTH,
+                              MARIA_MIN_KEY_BLOCK_LENGTH, 1)))
+    {
+      /* purecov: begin tested */
+      _ma_check_print_error(param, "key cache read error for block: %s",
+                            llstr(next_link,llbuff));
       DBUG_RETURN(1);
+      /* purecov: end */
+    }
     next_link=mi_sizekorr(buff);
     records--;
     param->key_file_blocks+=block_size;
@@ -533,17 +566,37 @@ static int chk_index_down(HA_CHECK *param, MARIA_HA *info, MARIA_KEYDEF *keyinfo
                      ha_checksum *key_checksum, uint level)
 {
   char llbuff[22],llbuff2[22];
-  if (page > info->state->key_file_length || (page & (info->s->blocksize -1)))
-  {
-    my_off_t max_length=my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0));
-    _ma_check_print_error(param,"Wrong pagepointer: %s at page: %s",
-                llstr(page,llbuff),llstr(page,llbuff2));
-
-    if (page+info->s->blocksize > max_length)
+  DBUG_ENTER("chk_index_down");
+
+  /* Key blocks must lay within the key file length entirely. */
+  if (page + keyinfo->block_length > info->state->key_file_length)
+  {
+    /* purecov: begin tested */
+    /* Give it a chance to fit in the real file size. */
+    my_off_t max_length= my_seek(info->s->kfile, 0L, MY_SEEK_END, MYF(0));
+    _ma_check_print_error(param, "Invalid key block position: %s  "
+                          "key block size: %u  file_length: %s",
+                          llstr(page, llbuff), keyinfo->block_length,
+                          llstr(info->state->key_file_length, llbuff2));
+    if (page + keyinfo->block_length > max_length)
       goto err;
-    info->state->key_file_length=(max_length &
-                                  ~ (my_off_t) (info->s->blocksize-1));
+    /* Fix the remebered key file length. */
+    info->state->key_file_length= (max_length &
+                                   ~ (my_off_t) (keyinfo->block_length - 1));
+    /* purecov: end */
+  }
+
+  /* Key blocks must be aligned at MARIA_MIN_KEY_BLOCK_LENGTH. */
+  if (page & (MARIA_MIN_KEY_BLOCK_LENGTH - 1))
+  {
+    /* purecov: begin tested */
+    _ma_check_print_error(param, "Mis-aligned key block: %s  "
+                          "minimum key block length: %u",
+                          llstr(page, llbuff), MARIA_MIN_KEY_BLOCK_LENGTH);
+    goto err;
+    /* purecov: end */
   }
+
   if (!_ma_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0))
   {
     _ma_check_print_error(param,"Can't read key from filepos: %s",
@@ -554,9 +607,12 @@ static int chk_index_down(HA_CHECK *param, MARIA_HA *info, MARIA_KEYDEF *keyinfo
   if (chk_index(param,info,keyinfo,page,buff,keys,key_checksum,level))
     goto err;
 
-  return 0;
+  DBUG_RETURN(0);
+
+  /* purecov: begin tested */
 err:
-  return 1;
+  DBUG_RETURN(1);
+  /* purecov: end */
 }
 
 
-- 
2.30.9