From 626afa7e5a0936fad899d509864c7e24bb64ce89 Mon Sep 17 00:00:00 2001 From: "acurtis/antony@xiphis.org/ltamd64.xiphis.org" <> Date: Tue, 24 Oct 2006 16:41:13 -0700 Subject: [PATCH 01/16] Bug#22694 "function plugin_foreach_with_mask() uses an uninitialized pointer" Fix uninitialized memory. --- sql/sql_plugin.cc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 34fb447792e..3b9fb1b249f 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -944,29 +944,28 @@ my_bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, state_mask= ~state_mask; // do it only once rw_rdlock(&THR_LOCK_plugin); + total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements + : plugin_hash[type].records; + /* + Do the alloca out here in case we do have a working alloca: + leaving the nested stack frame invalidates alloca allocation. + */ + plugins=(struct st_plugin_int **)my_alloca(total*sizeof(*plugins)); if (type == MYSQL_ANY_PLUGIN) { - total=plugin_array.elements; - plugins=(struct st_plugin_int **)my_alloca(total*sizeof(*plugins)); for (idx= 0; idx < total; idx++) { plugin= dynamic_element(&plugin_array, idx, struct st_plugin_int *); - if (plugin->state & state_mask) - continue; - plugins[idx]= plugin; + plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL; } } else { - HASH *hash= &plugin_hash[type]; - total=hash->records; - plugins=(struct st_plugin_int **)my_alloca(total*sizeof(*plugins)); + HASH *hash= plugin_hash + type; for (idx= 0; idx < total; idx++) { plugin= (struct st_plugin_int *) hash_element(hash, idx); - if (plugin->state & state_mask) - continue; - plugins[idx]= plugin; + plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL; } } rw_unlock(&THR_LOCK_plugin); From cdb8358503e38d3f6bc4aca624c9da1e0211a42e Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Wed, 25 Oct 2006 15:40:10 +0500 Subject: [PATCH 02/16] BUG#22053 - REPAIR table can crash server for some really damaged MyISAM tables When unpacking a blob column from broken row server crash could happen. This could rather happen when trying to repair a table using either REPAIR TABLE or myisamchk, though it also could happend when trying to access broken row using other SQL statements like SELECT if table is not marked as crashed. Fixed ulong overflow when trying to extract blob from broken row. Affects MyISAM only. --- myisam/mi_dynrec.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/myisam/mi_dynrec.c b/myisam/mi_dynrec.c index 4dec3055fa1..727f44341b1 100644 --- a/myisam/mi_dynrec.c +++ b/myisam/mi_dynrec.c @@ -992,9 +992,11 @@ ulong _mi_rec_unpack(register MI_INFO *info, register byte *to, byte *from, { uint size_length=rec_length- mi_portable_sizeof_char_ptr; ulong blob_length=_mi_calc_blob_length(size_length,from); - if ((ulong) (from_end-from) - size_length < blob_length || - min_pack_length > (uint) (from_end -(from+size_length+blob_length))) - goto err; + ulong from_left= (ulong) (from_end - from); + if (from_left < size_length || + from_left - size_length < blob_length || + from_left - size_length - blob_length < min_pack_length) + goto err; memcpy((byte*) to,(byte*) from,(size_t) size_length); from+=size_length; memcpy_fixed((byte*) to+size_length,(byte*) &from,sizeof(char*)); From 7dc7af51e03dde227861e3338a6113edd0d740f4 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 25 Oct 2006 13:39:40 +0200 Subject: [PATCH 03/16] Bug#22119 - Changing MI_KEY_BLOCK_LENGTH makes a wrong myisamchk When compiling with a default key block size greater than the smallest key block size used in a table, checking that table failed with bogus errors. The table was marked corrupt. This affected myisamchk and the server. The problem was that the default key block size was used at some places where sizes less or equal to the block size of the index in check was required. We do now use the key block size of the particular index when checking. A test case is available for later versions only. --- myisam/mi_check.c | 88 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/myisam/mi_check.c b/myisam/mi_check.c index b07c9904247..3a7817b7f03 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -251,11 +251,12 @@ static int check_k_link(MI_CHECK *param, register MI_INFO *info, uint nr) my_off_t next_link; uint block_size=(nr+1)*MI_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); @@ -265,14 +266,46 @@ static int check_k_link(MI_CHECK *param, register MI_INFO *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 */ + mi_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 MI_MIN_KEY_BLOCK_LENGTH. */ + if (next_link & (MI_MIN_KEY_BLOCK_LENGTH - 1)) + { + /* purecov: begin tested */ + mi_check_print_error(param, "Mis-aligned key block: %s " + "minimum key block length: %u", + llstr(next_link, llbuff), MI_MIN_KEY_BLOCK_LENGTH); + DBUG_RETURN(1); + /* purecov: end */ + } + + /* + Read the key block with MI_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, - myisam_block_size, block_size, 1))) + (byte*) info->buff, MI_MIN_KEY_BLOCK_LENGTH, + MI_MIN_KEY_BLOCK_LENGTH, 1))) + { + /* purecov: begin tested */ + mi_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; @@ -555,17 +588,37 @@ static int chk_index_down(MI_CHECK *param, MI_INFO *info, MI_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)); - mi_check_print_error(param,"Wrong pagepointer: %s at page: %s", - llstr(page,llbuff),llstr(page,llbuff2)); + DBUG_ENTER("chk_index_down"); - if (page+info->s->blocksize > max_length) + /* 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)); + mi_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 MI_MIN_KEY_BLOCK_LENGTH. */ + if (page & (MI_MIN_KEY_BLOCK_LENGTH - 1)) + { + /* purecov: begin tested */ + mi_check_print_error(param, "Mis-aligned key block: %s " + "minimum key block length: %u", + llstr(page, llbuff), MI_MIN_KEY_BLOCK_LENGTH); + goto err; + /* purecov: end */ + } + if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0)) { mi_check_print_error(param,"Can't read key from filepos: %s", @@ -576,9 +629,12 @@ static int chk_index_down(MI_CHECK *param, MI_INFO *info, MI_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 */ } From 931f7dbc892530c6977d795d3d59c5dc1e6015f2 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 25 Oct 2006 17:23:32 +0200 Subject: [PATCH 04/16] Bug#22119 - Changing MI_KEY_BLOCK_LENGTH makes a wrong myisamchk Added the promised test case. --- mysql-test/r/myisam.result | 45 ++++++++++++++++++++++++++++++++++++++ mysql-test/t/myisam.test | 43 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 69069825588..66a8e51c4d2 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -1757,4 +1757,49 @@ 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 diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 5a663718878..b40938b96a6 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -1054,4 +1054,47 @@ 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 From a8ff7bafb4ee760afed9c13e9c2d7197c9b57159 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Fri, 27 Oct 2006 17:40:06 +0200 Subject: [PATCH 05/16] Enterprise numbers, only --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 7c2d3e77b27..4e9ab05a176 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.29) +AM_INIT_AUTOMAKE(mysql, 5.0.30) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -19,7 +19,7 @@ SHARED_LIB_VERSION=$SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=29 +NDB_VERSION_BUILD=30 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? From c71395ce256d058f47639768c0f3b3b6b3764158 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Thu, 2 Nov 2006 00:05:02 +0400 Subject: [PATCH 06/16] BUG#23312 - server hangs 'closing tables' with insert delayed,flush tables,alter table Deadlock could happen if there are delayed insert + flush tables + alter table running concurrently. This is fixed by removing a redundant mutex lock when killing a delayed thread. --- sql/sql_insert.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 283fe571d53..2887d97bd8f 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1181,8 +1181,6 @@ void kill_delayed_threads(void) delayed_insert *tmp; while ((tmp=it++)) { - /* Ensure that the thread doesn't kill itself while we are looking at it */ - pthread_mutex_lock(&tmp->mutex); tmp->thd.killed=1; if (tmp->thd.mysys_var) { @@ -1201,7 +1199,6 @@ void kill_delayed_threads(void) } pthread_mutex_unlock(&tmp->thd.mysys_var->mutex); } - pthread_mutex_unlock(&tmp->mutex); } VOID(pthread_mutex_unlock(&LOCK_delayed_insert)); // For unlink from list } From 5f0fe17796f66db90bc170440c915d3560dfe758 Mon Sep 17 00:00:00 2001 From: "patg@radha.local" <> Date: Wed, 15 Nov 2006 21:24:58 -0500 Subject: [PATCH 07/16] BUG# 17044 Federated Storage Engine not UTF8 clean - Added 'SET NAMES " upon ::open - Added test and results for simple UTF test federated.test: BUG #17044 Federated Storage Engine not UTF8 clean New test. Using hex - pasting various charsets in the terminal doesn't work. federated.result: BUG# 17044 Federated Storage Engine not UTF8 clean New test results ha_federated.cc: BUG# 17044 Federated Storage Engine not UTF8 clean Upon ::open, set names to table's charset --- mysql-test/r/federated.result | 28 ++++++++++++++++++++++++++++ mysql-test/t/federated.test | 31 +++++++++++++++++++++++++++++++ sql/ha_federated.cc | 11 +++++++++++ 3 files changed, 70 insertions(+) diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index 2b44fc8bd7e..f5fc97cda80 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -1815,6 +1815,34 @@ engine = federated connection='mysql://root@127.0.0.1:SLAVE_PORT/federated/test'; drop table federated.test1, federated.test2; drop table federated.test; +set names utf8; +create table federated.t1 (a varchar(64)) DEFAULT CHARSET=utf8; +insert into federated.t1 values (0x6DC3A56E6164); +select hex(a) from federated.t1; +hex(a) +6DC3A56E6164 +set names utf8; +create table federated.t1 (a varchar(64)) +ENGINE=FEDERATED +CONNECTION='mysql://root@127.0.0.1:9308/federated/t1' +DEFAULT CHARSET=utf8; +select hex(a) from federated.t1; +hex(a) +6DC3A56E6164 +insert into federated.t1 values (0xC3A4C3B6C3BCC39F); +insert into federated.t1 values (0xD18DD184D184D0B5D0BAD182D0B8D0B2D0BDD183D18E); +select hex(a) from federated.t1; +hex(a) +6DC3A56E6164 +C3A4C3B6C3BCC39F +D18DD184D184D0B5D0BAD182D0B8D0B2D0BDD183D18E +select hex(a) from federated.t1; +hex(a) +6DC3A56E6164 +C3A4C3B6C3BCC39F +D18DD184D184D0B5D0BAD182D0B8D0B2D0BDD183D18E +drop table federated.t1; +drop table federated.t1; DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index c2218b3451b..c3912ad0d48 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1544,4 +1544,35 @@ drop table federated.test1, federated.test2; connection slave; drop table federated.test; +# +# BUG# 17044 Federated Storage Engine not UTF8 clean +# +connection slave; +set names utf8; +create table federated.t1 (a varchar(64)) DEFAULT CHARSET=utf8; + +insert into federated.t1 values (0x6DC3A56E6164); +select hex(a) from federated.t1; + +connection master; +set names utf8; +eval create table federated.t1 (a varchar(64)) +ENGINE=FEDERATED +CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1' +DEFAULT CHARSET=utf8; +select hex(a) from federated.t1; +insert into federated.t1 values (0xC3A4C3B6C3BCC39F); +insert into federated.t1 values (0xD18DD184D184D0B5D0BAD182D0B8D0B2D0BDD183D18E); +select hex(a) from federated.t1; + +connection slave; +select hex(a) from federated.t1; + +connection master; +drop table federated.t1; + +connection slave; +drop table federated.t1; + + source include/federated_cleanup.inc; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index 7919519cdce..9eb572d8cb0 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -1435,6 +1435,16 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked) /* Connect to foreign database mysql_real_connect() */ mysql= mysql_init(0); + + /* + BUG# 17044 Federated Storage Engine is not UTF8 clean + Add set names to whatever charset the table is at open + of table + */ + /* this sets the csname like 'set names utf8' */ + mysql_options(mysql,MYSQL_SET_CHARSET_NAME, + this->table->s->table_charset->csname); + if (!mysql || !mysql_real_connect(mysql, share->hostname, share->username, @@ -1451,6 +1461,7 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked) API silently reconnect. For future versions, we will need more logic to deal with transactions */ + mysql->reconnect= 1; ref_length= (table->s->primary_key != MAX_KEY ? From 671575211b08f48c3185f59fd52e99d8aabf2722 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Tue, 28 Nov 2006 15:46:01 +0100 Subject: [PATCH 08/16] Bug#23139 - myisamchk and mysqld crash when trying to access table A corrupted compressed table could crash the server and myisamchk. The data file of an uncompressed table contains just the records. There is no header in the data file. However the data file of a compressed table has a header. The header describes how the table was compressed. This information is necessary to extract the records from the compressed data file. Part of the compressed data file header are the [de]code tables. They are numeric representations of the Huffman trees used for coding and decoding. A Huffman tree is a binary tree. Every node has two childs. A child can be a leaf or a branch. Leaves contain the decoded value. Branches point to another tree node. Since the [de]code table is represented as an array of childs, the branches need to point at a child within the same array. The corruption of the compressed data file from the bug report was a couple of branches that pointed outside their array. This condition had not been correctly checked. I added some checks for the pointers in the decode tables. This type of corruption will no longer crash the server or myisamchk. No test case. A corrupted compressed table is required. --- myisam/mi_packrec.c | 363 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 327 insertions(+), 36 deletions(-) diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index feb8d33b015..c88b8ab6607 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -20,7 +20,10 @@ #define IS_CHAR ((uint) 32768) /* Bit if char (not offset) in tree */ -#if INT_MAX > 65536L +/* Some definitions to keep in sync with myisampack.c */ +#define HEAD_LENGTH 32 /* Length of fixed header */ + +#if INT_MAX > 32767 #define BITS_SAVED 32 #define MAX_QUICK_TABLE_BITS 9 /* Because we may shift in 24 bits */ #else @@ -42,6 +45,7 @@ { bits-=(bit+1); break; } \ pos+= *pos +/* Size in uint16 of a Huffman tree for byte compression of 256 byte values. */ #define OFFSET_TABLE_SIZE 512 static uint read_huff_table(MI_BIT_BUFF *bit_buff,MI_DECODE_TREE *decode_tree, @@ -132,7 +136,7 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) uint16 *decode_table,*tmp_buff; ulong elements,intervall_length; char *disk_cache,*intervall_buff; - uchar header[32]; + uchar header[HEAD_LENGTH]; MYISAM_SHARE *share=info->s; MI_BIT_BUFF bit_buff; DBUG_ENTER("_mi_read_pack_info"); @@ -150,12 +154,13 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) my_errno=HA_ERR_END_OF_FILE; goto err0; } + /* Only the first three bytes of magic number are independent of version. */ if (memcmp((byte*) header, (byte*) myisam_pack_file_magic, 3)) { my_errno=HA_ERR_WRONG_IN_RECORD; goto err0; } - share->pack.version= header[3]; + share->pack.version= header[3]; /* fourth byte of magic number */ share->pack.header_length= uint4korr(header+4); share->min_pack_length=(uint) uint4korr(header+8); share->max_pack_length=(uint) uint4korr(header+12); @@ -171,7 +176,22 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) share->base.min_block_length=share->min_pack_length+1; if (share->min_pack_length > 254) share->base.min_block_length+=2; + DBUG_PRINT("info", ("fixed header length: %u", HEAD_LENGTH)); + DBUG_PRINT("info", ("total header length: %u", share->pack.header_length)); + DBUG_PRINT("info", ("pack file version: %u", share->pack.version)); + DBUG_PRINT("info", ("min pack length: %u", share->min_pack_length)); + DBUG_PRINT("info", ("max pack length: %u", share->max_pack_length)); + DBUG_PRINT("info", ("elements of all trees: %u", elements)); + DBUG_PRINT("info", ("distinct values bytes: %u", intervall_length)); + DBUG_PRINT("info", ("number of code trees: %u", trees)); + DBUG_PRINT("info", ("bytes for record lgt: %u", share->pack.ref_length)); + DBUG_PRINT("info", ("record pointer length: %u", rec_reflength)); + /* + Memory segment #1: + - Decode tree heads + - Distinct column values + */ if (!(share->decode_trees=(MI_DECODE_TREE*) my_malloc((uint) (trees*sizeof(MI_DECODE_TREE)+ intervall_length*sizeof(byte)), @@ -179,11 +199,19 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) goto err0; intervall_buff=(byte*) (share->decode_trees+trees); + /* + Memory segment #2: + - Decode tables + - Quick decode tables + - Temporary decode table + - Compressed data file header cache + This segment will be reallocated after construction of the tables. + */ length=(uint) (elements*2+trees*(1 << myisam_quick_table_bits)); if (!(share->decode_tables=(uint16*) - my_malloc((length+OFFSET_TABLE_SIZE)*sizeof(uint16)+ - (uint) (share->pack.header_length+7), - MYF(MY_WME | MY_ZEROFILL)))) + my_malloc((length + OFFSET_TABLE_SIZE) * sizeof(uint16) + + (uint) (share->pack.header_length - sizeof(header)), + MYF(MY_WME | MY_ZEROFILL)))) goto err1; tmp_buff=share->decode_tables+length; disk_cache=(byte*) (tmp_buff+OFFSET_TABLE_SIZE); @@ -196,7 +224,7 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) huff_tree_bits=max_bit(trees ? trees-1 : 0); init_bit_buffer(&bit_buff, (uchar*) disk_cache, (uint) (share->pack.header_length-sizeof(header))); - /* Read new info for each field */ + /* Read new info for each field */ for (i=0 ; i < share->base.fields ; i++) { share->rec[i].base_type=(enum en_fieldtype) get_bits(&bit_buff,5); @@ -205,17 +233,26 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) share->rec[i].huff_tree=share->decode_trees+(uint) get_bits(&bit_buff, huff_tree_bits); share->rec[i].unpack=get_unpack_function(share->rec+i); + DBUG_PRINT("info", ("col: %2u type: %2u pack: %u slbits: %2u", + i, share->rec[i].base_type, share->rec[i].pack_type, + share->rec[i].space_length_bits)); } skip_to_next_byte(&bit_buff); + /* + Construct the decoding tables from the file header. Keep track of + the used memory. + */ decode_table=share->decode_tables; for (i=0 ; i < trees ; i++) if (read_huff_table(&bit_buff,share->decode_trees+i,&decode_table, &intervall_buff,tmp_buff)) goto err3; + /* Reallocate the decoding tables to the used size. */ decode_table=(uint16*) my_realloc((gptr) share->decode_tables, (uint) ((byte*) decode_table - (byte*) share->decode_tables), MYF(MY_HOLD_ON_ERROR)); + /* Fix the table addresses in the tree heads. */ { long diff=PTR_BYTE_DIFF(decode_table,share->decode_tables); share->decode_tables=decode_table; @@ -224,7 +261,7 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) diff, uint16*); } - /* Fix record-ref-length for keys */ + /* Fix record-ref-length for keys */ if (fix_keys) { for (i=0 ; i < share->base.keys ; i++) @@ -261,7 +298,23 @@ err0: } - /* Read on huff-code-table from datafile */ +/* + Read a huff-code-table from datafile. + + SYNOPSIS + read_huff_table() + bit_buff Bit buffer pointing at start of the + decoding table in the file header cache. + decode_tree Pointer to the decode tree head. + decode_table IN/OUT Address of a pointer to the next free space. + intervall_buff IN/OUT Address of a pointer to the next unused values. + tmp_buff Buffer for temporary extraction of a full + decoding table as read from bit_buff. + + RETURN + 0 OK. + 1 Error. +*/ static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree, uint16 **decode_table, byte **intervall_buff, @@ -270,19 +323,32 @@ static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree, uint min_chr,elements,char_bits,offset_bits,size,intervall_length,table_bits, next_free_offset; uint16 *ptr,*end; + DBUG_ENTER("read_huff_table"); - LINT_INIT(ptr); if (!get_bits(bit_buff,1)) { + /* Byte value compression. */ min_chr=get_bits(bit_buff,8); elements=get_bits(bit_buff,9); char_bits=get_bits(bit_buff,5); offset_bits=get_bits(bit_buff,5); intervall_length=0; ptr=tmp_buff; + DBUG_PRINT("info", ("byte value compression")); + DBUG_PRINT("info", ("minimum byte value: %u", min_chr)); + DBUG_PRINT("info", ("number of tree nodes: %u", elements)); + DBUG_PRINT("info", ("bits for values: %u", char_bits)); + DBUG_PRINT("info", ("bits for tree offsets: %u", offset_bits)); + if (elements > 256) + { + DBUG_PRINT("error", ("ERROR: illegal number of tree elements: %u", + elements)); + DBUG_RETURN(1); + } } else { + /* Distinct column value compression. */ min_chr=0; elements=get_bits(bit_buff,15); intervall_length=get_bits(bit_buff,16); @@ -290,13 +356,27 @@ static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree, offset_bits=get_bits(bit_buff,5); decode_tree->quick_table_bits=0; ptr= *decode_table; + DBUG_PRINT("info", ("distinct column value compression")); + DBUG_PRINT("info", ("number of tree nodes: %u", elements)); + DBUG_PRINT("info", ("value buffer length: %u", intervall_length)); + DBUG_PRINT("info", ("bits for value index: %u", char_bits)); + DBUG_PRINT("info", ("bits for tree offsets: %u", offset_bits)); } size=elements*2-2; + DBUG_PRINT("info", ("tree size in uint16: %u", size)); + DBUG_PRINT("info", ("tree size in bytes: %u", size * sizeof(uint16))); for (end=ptr+size ; ptr < end ; ptr++) { if (get_bit(bit_buff)) + { *ptr= (uint16) get_bits(bit_buff,offset_bits); + if ((ptr + *ptr >= end) || !*ptr) + { + DBUG_PRINT("error", ("ERROR: illegal pointer in decode tree")); + DBUG_RETURN(1); + } + } else *ptr= (uint16) (IS_CHAR + (get_bits(bit_buff,char_bits) + min_chr)); } @@ -306,11 +386,15 @@ static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree, decode_tree->intervalls= *intervall_buff; if (! intervall_length) { - table_bits=find_longest_bitstream(tmp_buff, tmp_buff+OFFSET_TABLE_SIZE); - if (table_bits == (uint) ~0) - return 1; + /* Byte value compression. ptr started from tmp_buff. */ + /* Find longest Huffman code from begin to end of tree in bits. */ + table_bits= find_longest_bitstream(tmp_buff, ptr); + if (table_bits >= OFFSET_TABLE_SIZE) + DBUG_RETURN(1); if (table_bits > myisam_quick_table_bits) table_bits=myisam_quick_table_bits; + DBUG_PRINT("info", ("table bits: %u", table_bits)); + next_free_offset= (1 << table_bits); make_quick_table(*decode_table,tmp_buff,&next_free_offset,0,table_bits, table_bits); @@ -319,105 +403,279 @@ static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree, } else { + /* Distinct column value compression. ptr started from *decode_table */ (*decode_table)=end; + /* + get_bits() moves some bytes to a cache buffer in advance. May need + to step back. + */ bit_buff->pos-= bit_buff->bits/8; + /* Copy the distinct column values from the buffer. */ memcpy(*intervall_buff,bit_buff->pos,(size_t) intervall_length); (*intervall_buff)+=intervall_length; bit_buff->pos+=intervall_length; bit_buff->bits=0; } - return 0; + DBUG_RETURN(0); } +/* + Make a quick_table for faster decoding. + + SYNOPSIS + make_quick_table() + to_table Target quick_table and remaining decode table. + decode_table Source Huffman (sub-)tree within tmp_buff. + next_free_offset IN/OUT Next free offset from to_table. + Starts behind quick_table on the top-level. + value Huffman bits found so far. + bits Remaining bits to be collected. + max_bits Total number of bits to collect (table_bits). + + DESCRIPTION + + The quick table is an array of 16-bit values. There exists one value + for each possible code representable by max_bits (table_bits) bits. + In most cases table_bits is 9. So there are 512 16-bit values. + + If the high-order bit (16) is set (IS_CHAR) then the array slot for + this value is a valid Huffman code for a resulting byte value. + + The low-order 8 bits (1..8) are the resulting byte value. + + Bits 9..14 are the length of the Huffman code for this byte value. + This means so many bits from the input stream were needed to + represent this byte value. The remaining bits belong to later + Huffman codes. This also means that for every Huffman code shorter + than table_bits there are multiple entires in the array, which + differ just in the unused bits. + + If the high-order bit (16) is clear (0) then the remaining bits are + the position of the remaining Huffman decode tree segment behind the + quick table. + + RETURN + void +*/ + static void make_quick_table(uint16 *to_table, uint16 *decode_table, uint *next_free_offset, uint value, uint bits, uint max_bits) { + DBUG_ENTER("make_quick_table"); + + /* + When down the table to the requested maximum, copy the rest of the + Huffman table. + */ if (!bits--) { + /* + Remaining left Huffman tree segment starts behind quick table. + Remaining right Huffman tree segment starts behind left segment. + */ to_table[value]= (uint16) *next_free_offset; - *next_free_offset=copy_decode_table(to_table, *next_free_offset, - decode_table); - return; + /* + Re-construct the remaining Huffman tree segment at + next_free_offset in to_table. + */ + *next_free_offset= copy_decode_table(to_table, *next_free_offset, + decode_table); + DBUG_VOID_RETURN; } + + /* Descent on the left side. Left side bits are clear (0). */ if (!(*decode_table & IS_CHAR)) { - make_quick_table(to_table,decode_table+ *decode_table, - next_free_offset,value,bits,max_bits); + /* Not a leaf. Follow the pointer. */ + make_quick_table(to_table, decode_table + *decode_table, + next_free_offset, value, bits, max_bits); } else - fill_quick_table(to_table+value,bits,max_bits,(uint) *decode_table); + { + /* + A leaf. A Huffman code is complete. Fill the quick_table + array for all possible bit strings starting with this Huffman + code. + */ + fill_quick_table(to_table + value, bits, max_bits, (uint) *decode_table); + } + + /* Descent on the right side. Right side bits are set (1). */ decode_table++; value|= (1 << bits); if (!(*decode_table & IS_CHAR)) { - make_quick_table(to_table,decode_table+ *decode_table, - next_free_offset,value,bits,max_bits); + /* Not a leaf. Follow the pointer. */ + make_quick_table(to_table, decode_table + *decode_table, + next_free_offset, value, bits, max_bits); } else - fill_quick_table(to_table+value,bits,max_bits,(uint) *decode_table); - return; + { + /* + A leaf. A Huffman code is complete. Fill the quick_table + array for all possible bit strings starting with this Huffman + code. + */ + fill_quick_table(to_table + value, bits, max_bits, (uint) *decode_table); + } + + DBUG_VOID_RETURN; } +/* + Fill quick_table for all possible values starting with this Huffman code. + + SYNOPSIS + fill_quick_table() + table Target quick_table position. + bits Unused bits from max_bits. + max_bits Total number of bits to collect (table_bits). + value The byte encoded by the found Huffman code. + + DESCRIPTION + + Fill the segment (all slots) of the quick_table array with the + resulting value for the found Huffman code. There are as many slots + as there are combinations representable by the unused bits. + + In most cases we use 9 table bits. Assume a 3-bit Huffman code. Then + there are 6 unused bits. Hence we fill 2**6 = 64 slots with the + value. + + RETURN + void +*/ + static void fill_quick_table(uint16 *table, uint bits, uint max_bits, uint value) { uint16 *end; - value|=(max_bits-bits) << 8; - for (end=table+ (1 << bits) ; - table < end ; - *table++ = (uint16) value | IS_CHAR) ; + DBUG_ENTER("fill_quick_table"); + + /* + Bits 1..8 of value represent the decoded byte value. + Bits 9..14 become the length of the Huffman code for this byte value. + Bit 16 flags a valid code (IS_CHAR). + */ + value|= (max_bits - bits) << 8 | IS_CHAR; + + for (end= table + (1 << bits); table < end; table++) + { + *table= (uint16) value; + } + DBUG_VOID_RETURN; } +/* + Reconstruct a decode subtree at the target position. + + SYNOPSIS + copy_decode_table() + to_pos Target quick_table and remaining decode table. + offset Next free offset from to_pos. + decode_table Source Huffman subtree within tmp_buff. + + NOTE + Pointers in the decode tree are relative to the pointers position. + + RETURN + next free offset from to_pos. +*/ + static uint copy_decode_table(uint16 *to_pos, uint offset, uint16 *decode_table) { uint prev_offset; prev_offset= offset; + DBUG_ENTER("copy_decode_table"); + /* Descent on the left side. */ if (!(*decode_table & IS_CHAR)) { + /* Set a pointer to the next target node. */ to_pos[offset]=2; + /* Copy the left hand subtree there. */ offset=copy_decode_table(to_pos,offset+2,decode_table+ *decode_table); } else { + /* Copy the byte value. */ to_pos[offset]= *decode_table; + /* Step behind this node. */ offset+=2; } - decode_table++; + /* Descent on the right side. */ + decode_table++; if (!(*decode_table & IS_CHAR)) { + /* Set a pointer to the next free target node. */ to_pos[prev_offset+1]=(uint16) (offset-prev_offset-1); + /* Copy the right hand subtree to the entry of that node. */ offset=copy_decode_table(to_pos,offset,decode_table+ *decode_table); } else + { + /* Copy the byte value. */ to_pos[prev_offset+1]= *decode_table; - return offset; + } + DBUG_RETURN(offset); } +/* + Find the length of the longest Huffman code in this table in bits. + + SYNOPSIS + find_longest_bitstream() + table Code (sub-)table start. + end End of code table. + + IMPLEMENTATION + + Recursively follow the branch(es) of the code pair on every level of + the tree until two byte values (and no branch) are found. Add one to + each level when returning back from each recursion stage. + + 'end' is used for error checking only. A clean tree terminates + before reaching 'end'. Hence the exact value of 'end' is not too + important. However having it higher than necessary could lead to + misbehaviour should 'next' jump into the dirty area. + + RETURN + length Length of longest Huffman code in bits. + >= OFFSET_TABLE_SIZE Error, broken tree. It does not end before 'end'. +*/ + static uint find_longest_bitstream(uint16 *table, uint16 *end) { - uint length=1,length2; + uint length= 1; + uint length2; + if (!(*table & IS_CHAR)) { uint16 *next= table + *table; if (next > end || next == table) - return ~0; - length=find_longest_bitstream(next, end)+1; + { + DBUG_PRINT("error", ("ERROR: illegal pointer in decode tree")); + return OFFSET_TABLE_SIZE; + } + length= find_longest_bitstream(next, end) + 1; } table++; if (!(*table & IS_CHAR)) { uint16 *next= table + *table; if (next > end || next == table) - return ~0; - length2=find_longest_bitstream(table+ *table, end)+1; + { + DBUG_PRINT("error", ("ERROR: illegal pointer in decode tree")); + return OFFSET_TABLE_SIZE; + } + length2= find_longest_bitstream(next, end) + 1; length=max(length,length2); } return length; @@ -825,18 +1083,46 @@ static void decode_bytes(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,uchar *to, bit_buff->pos+=4; bits+=32; } - /* First use info in quick_table */ + /* + First use info in quick_table. + + The quick table is an array of 16-bit values. There exists one + value for each possible code representable by table_bits bits. + In most cases table_bits is 9. So there are 512 16-bit values. + + If the high-order bit (16) is set (IS_CHAR) then the array slot + for this value is a valid Huffman code for a resulting byte value. + + The low-order 8 bits (1..8) are the resulting byte value. + + Bits 9..14 are the length of the Huffman code for this byte value. + This means so many bits from the input stream were needed to + represent this byte value. The remaining bits belong to later + Huffman codes. This also means that for every Huffman code shorter + than table_bits there are multiple entires in the array, which + differ just in the unused bits. + + If the high-order bit (16) is clear (0) then the remaining bits are + the position of the remaining Huffman decode tree segment behind the + quick table. + */ low_byte=(uint) (bit_buff->current_byte >> (bits - table_bits)) & table_and; low_byte=decode_tree->table[low_byte]; if (low_byte & IS_CHAR) { + /* + All Huffman codes of less or equal table_bits length are in the + quick table. This is one of them. + */ *to++ = (low_byte & 255); /* Found char in quick table */ bits-= ((low_byte >> 8) & 31); /* Remove bits used */ } else { /* Map through rest of decode-table */ + /* This means that the Huffman code must be longer than table_bits. */ pos=decode_tree->table+low_byte; bits-=table_bits; + /* NOTE: decode_bytes_test_bit() is a macro wich contains a break !!! */ for (;;) { low_byte=(uint) (bit_buff->current_byte >> (bits-8)); @@ -1062,6 +1348,11 @@ uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BIT_BUFF *bit_buff, { head_length+= read_pack_length((uint) myisam->s->pack.version, header + head_length, &info->blob_len); + /* + Ensure that the record buffer is big enough for the compressed + record plus all expanded blobs. [We do not have an extra buffer + for the resulting blobs. Sigh.] + */ if (!(mi_alloc_rec_buff(myisam,info->rec_len + info->blob_len, rec_buff_p))) return BLOCK_FATAL_ERROR; /* not enough memory */ From 7ce83bc36a0f8914afe6e452250f26b6ac30843c Mon Sep 17 00:00:00 2001 From: "patg@radha.local" <> Date: Wed, 29 Nov 2006 13:56:59 -0500 Subject: [PATCH 09/16] federated.test: fixed test to replace port to SLAVE_PORT in result file federated.result: new result file --- mysql-test/r/federated.result | 4 ++-- mysql-test/t/federated.test | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index f5fc97cda80..cecffbb1471 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -1821,11 +1821,11 @@ insert into federated.t1 values (0x6DC3A56E6164); select hex(a) from federated.t1; hex(a) 6DC3A56E6164 -set names utf8; create table federated.t1 (a varchar(64)) ENGINE=FEDERATED -CONNECTION='mysql://root@127.0.0.1:9308/federated/t1' +connection='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1' DEFAULT CHARSET=utf8; +set names utf8; select hex(a) from federated.t1; hex(a) 6DC3A56E6164 diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index c3912ad0d48..894cd513914 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1555,11 +1555,12 @@ insert into federated.t1 values (0x6DC3A56E6164); select hex(a) from federated.t1; connection master; -set names utf8; +--replace_result $SLAVE_MYPORT SLAVE_PORT eval create table federated.t1 (a varchar(64)) ENGINE=FEDERATED -CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1' +connection='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1' DEFAULT CHARSET=utf8; +set names utf8; select hex(a) from federated.t1; insert into federated.t1 values (0xC3A4C3B6C3BCC39F); insert into federated.t1 values (0xD18DD184D184D0B5D0BAD182D0B8D0B2D0BDD183D18E); From 162f14433b12e6ef7e39aaac319cff9eaaa98f8d Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Tue, 5 Dec 2006 18:05:53 +0400 Subject: [PATCH 10/16] BUG#23255 - Missing version parameter in plugin declaration example Plugin interface version was not raised properly after incompatible changes to plugin structure were introduced. Fixed plugin interface version according to worklog 2761. --- include/mysql/plugin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index 1385bb502bb..d5c4a975602 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -21,7 +21,7 @@ Plugin API. Common for all plugin types. */ -#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0002 +#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0100 /* The allowable types of plugins @@ -98,7 +98,7 @@ struct st_mysql_plugin const char *name; /* plugin name */ const char *author; /* plugin author (for SHOW PLUGINS) */ const char *descr; /* general descriptive text (for SHOW PLUGINS ) */ - int license; /* the plugin type (a MYSQL_XXX_PLUGIN value) */ + int license; /* the plugin license (PLUGIN_LICENSE_XXX) */ int (*init)(void *); /* the function to invoke when plugin is loaded */ int (*deinit)(void *);/* the function to invoke when plugin is unloaded */ unsigned int version; /* plugin version (for SHOW PLUGINS) */ From e17d7bce004635c86f72380d5e08fa4b50d1de48 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Thu, 7 Dec 2006 18:32:40 +0400 Subject: [PATCH 11/16] BUG#23404 - ROW_FORMAT=FIXED option is lost is an index is added to the table ROW_FORMAT option is lost during CREATE/DROP INDEX. This fix forces CREATE/DROP INDEX to retain ROW_FORMAT by instructing mysql_alter_table() that ROW_FORMAT is not used during creating/dropping indexes. --- mysql-test/r/alter_table.result | 15 +++++++++++++++ mysql-test/t/alter_table.test | 11 +++++++++++ sql/sql_parse.cc | 2 ++ 3 files changed, 28 insertions(+) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index e9c9c873750..00795731026 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -543,3 +543,18 @@ ERROR 3D000: No database selected alter table test.t1 rename test.t1; use test; drop table t1; +CREATE TABLE t1(a INT) ROW_FORMAT=FIXED; +CREATE INDEX i1 ON t1(a); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) default NULL, + KEY `i1` (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED +DROP INDEX i1 ON t1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED +DROP TABLE t1; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 9bd34c2a610..fa174191154 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -392,4 +392,15 @@ alter table test.t1 rename test.t1; use test; drop table t1; +# +# BUG#23404 - ROW_FORMAT=FIXED option is lost is an index is added to the +# table +# +CREATE TABLE t1(a INT) ROW_FORMAT=FIXED; +CREATE INDEX i1 ON t1(a); +SHOW CREATE TABLE t1; +DROP INDEX i1 ON t1; +SHOW CREATE TABLE t1; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cb2fa0f7014..19add72c23e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5481,6 +5481,7 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List &keys) bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.default_table_charset= thd->variables.collation_database; + create_info.row_type= ROW_TYPE_NOT_USED; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, fields, keys, 0, (ORDER*)0, @@ -5497,6 +5498,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.default_table_charset= thd->variables.collation_database; + create_info.row_type= ROW_TYPE_NOT_USED; alter_info->clear(); alter_info->flags= ALTER_DROP_INDEX; alter_info->is_simple= 0; From a708a3b5a6f49a27ef4f223d84c874ef91d70b3a Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Wed, 13 Dec 2006 19:20:34 +0400 Subject: [PATCH 12/16] After merge fix. --- mysql-test/r/alter_table.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 4476476f6b4..9a534539523 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -682,14 +682,14 @@ CREATE INDEX i1 ON t1(a); SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` int(11) default NULL, + `a` int(11) DEFAULT NULL, KEY `i1` (`a`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED DROP INDEX i1 ON t1; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` int(11) default NULL + `a` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED DROP TABLE t1; DROP TABLE IF EXISTS bug24219; From 1d69857c4d742f238a57205bd8426bc72a3e7332 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 20 Dec 2006 15:18:09 +0100 Subject: [PATCH 13/16] Bug#25208 - Warnings in mi_packrec.c Compiler warnings due to non-matching conversion specifications in format strings in DBUG_PRINT calls. Fixed DBUG_PRINT format specifiactions. --- myisam/mi_packrec.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index c88b8ab6607..5363a3ecf23 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -177,12 +177,12 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) if (share->min_pack_length > 254) share->base.min_block_length+=2; DBUG_PRINT("info", ("fixed header length: %u", HEAD_LENGTH)); - DBUG_PRINT("info", ("total header length: %u", share->pack.header_length)); + DBUG_PRINT("info", ("total header length: %lu", share->pack.header_length)); DBUG_PRINT("info", ("pack file version: %u", share->pack.version)); - DBUG_PRINT("info", ("min pack length: %u", share->min_pack_length)); - DBUG_PRINT("info", ("max pack length: %u", share->max_pack_length)); - DBUG_PRINT("info", ("elements of all trees: %u", elements)); - DBUG_PRINT("info", ("distinct values bytes: %u", intervall_length)); + DBUG_PRINT("info", ("min pack length: %lu", share->min_pack_length)); + DBUG_PRINT("info", ("max pack length: %lu", share->max_pack_length)); + DBUG_PRINT("info", ("elements of all trees: %lu", elements)); + DBUG_PRINT("info", ("distinct values bytes: %lu", intervall_length)); DBUG_PRINT("info", ("number of code trees: %u", trees)); DBUG_PRINT("info", ("bytes for record lgt: %u", share->pack.ref_length)); DBUG_PRINT("info", ("record pointer length: %u", rec_reflength)); @@ -364,7 +364,8 @@ static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree, } size=elements*2-2; DBUG_PRINT("info", ("tree size in uint16: %u", size)); - DBUG_PRINT("info", ("tree size in bytes: %u", size * sizeof(uint16))); + DBUG_PRINT("info", ("tree size in bytes: %u", + size * (uint) sizeof(uint16))); for (end=ptr+size ; ptr < end ; ptr++) { From d9b9778e532730d8991251776513409f609352cd Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 20 Dec 2006 15:32:02 +0100 Subject: [PATCH 14/16] Bug#25213 - Compiler warnings in MyISAM code Compiler warnings due to non-matching conversion specifications in format strings in DBUG_PRINT calls, due to non-used parameters (in non-debug mode), and due to seemingly uninitialized variables. Initialized variables, declared parameters unused, and casted DBUG_PRINT arguments to get rid of warnings. --- myisam/mi_range.c | 1 + myisam/mi_test1.c | 2 +- myisam/mi_write.c | 18 +++++++++++------- myisam/rt_split.c | 4 ++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/myisam/mi_range.c b/myisam/mi_range.c index de042845d1e..4248fa7f04b 100644 --- a/myisam/mi_range.c +++ b/myisam/mi_range.c @@ -171,6 +171,7 @@ static double _mi_search_pos(register MI_INFO *info, uchar *keypos,*buff; double offset; DBUG_ENTER("_mi_search_pos"); + LINT_INIT(max_keynr); if (pos == HA_OFFSET_ERROR) DBUG_RETURN(0.5); diff --git a/myisam/mi_test1.c b/myisam/mi_test1.c index 77c4d3dfbad..f6e1f7617d0 100644 --- a/myisam/mi_test1.c +++ b/myisam/mi_test1.c @@ -562,7 +562,7 @@ static struct my_option my_long_options[] = static my_bool get_one_option(int optid, const struct my_option *opt __attribute__((unused)), - char *argument) + char *argument __attribute__((unused))) { switch(optid) { case 'a': diff --git a/myisam/mi_write.c b/myisam/mi_write.c index 720c96b2d38..f8a5c6fa652 100644 --- a/myisam/mi_write.c +++ b/myisam/mi_write.c @@ -331,7 +331,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, my_bool was_last_key; my_off_t next_page, dupp_key_pos; DBUG_ENTER("w_search"); - DBUG_PRINT("enter",("page: %ld",page)); + DBUG_PRINT("enter",("page: %ld", (long) page)); search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY; if (!(temp_buff= (uchar*) my_alloca((uint) keyinfo->block_length+ @@ -453,7 +453,7 @@ int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *endpos, *prev_key; MI_KEY_PARAM s_temp; DBUG_ENTER("_mi_insert"); - DBUG_PRINT("enter",("key_pos: %lx",key_pos)); + DBUG_PRINT("enter",("key_pos: %lx", (ulong) key_pos)); DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE,keyinfo->seg,key,USE_WHOLE_KEY);); nod_flag=mi_test_if_nod(anc_buff); @@ -475,7 +475,8 @@ int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo, DBUG_PRINT("test",("t_length: %d ref_len: %d", t_length,s_temp.ref_length)); DBUG_PRINT("test",("n_ref_len: %d n_length: %d key_pos: %lx", - s_temp.n_ref_length,s_temp.n_length,s_temp.key)); + s_temp.n_ref_length, s_temp.n_length, + (ulong) s_temp.key)); } #endif if (t_length > 0) @@ -572,6 +573,7 @@ int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo, my_off_t new_pos; MI_KEY_PARAM s_temp; DBUG_ENTER("mi_split_page"); + LINT_INIT(after_key); DBUG_DUMP("buff",(byte*) buff,mi_getint(buff)); if (info->s->keyinfo+info->lastinx == keyinfo) @@ -664,7 +666,8 @@ uchar *_mi_find_half_pos(uint nod_flag, MI_KEYDEF *keyinfo, uchar *page, } while (page < end); *return_key_length=length; *after_key=page; - DBUG_PRINT("exit",("returns: %lx page: %lx half: %lx",lastpos,page,end)); + DBUG_PRINT("exit",("returns: %lx page: %lx half: %lx", + (ulong) lastpos, (ulong) page, (ulong) end)); DBUG_RETURN(lastpos); } /* _mi_find_half_pos */ @@ -718,7 +721,8 @@ static uchar *_mi_find_last_pos(MI_KEYDEF *keyinfo, uchar *page, } *return_key_length=last_length; *after_key=lastpos; - DBUG_PRINT("exit",("returns: %lx page: %lx end: %lx",prevpos,page,end)); + DBUG_PRINT("exit",("returns: %lx page: %lx end: %lx", + (ulong) prevpos, (ulong) page, (ulong) end)); DBUG_RETURN(prevpos); } /* _mi_find_last_pos */ @@ -754,7 +758,7 @@ static int _mi_balance_page(register MI_INFO *info, MI_KEYDEF *keyinfo, next_page= _mi_kpos(info->s->base.key_reflength, father_key_pos+father_keylength); buff=info->buff; - DBUG_PRINT("test",("use right page: %lu",next_page)); + DBUG_PRINT("test",("use right page: %lu", (ulong) next_page)); } else { @@ -763,7 +767,7 @@ static int _mi_balance_page(register MI_INFO *info, MI_KEYDEF *keyinfo, next_page= _mi_kpos(info->s->base.key_reflength,father_key_pos); /* Fix that curr_buff is to left */ buff=curr_buff; curr_buff=info->buff; - DBUG_PRINT("test",("use left page: %lu",next_page)); + DBUG_PRINT("test",("use left page: %lu", (ulong) next_page)); } /* father_key_pos ptr to parting key */ if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,info->buff,0)) diff --git a/myisam/rt_split.c b/myisam/rt_split.c index 664dd2c75e3..87da22a93c7 100644 --- a/myisam/rt_split.c +++ b/myisam/rt_split.c @@ -188,6 +188,10 @@ static int split_rtree_node(SplitStruct *node, int n_entries, int next_node; int i; SplitStruct *end = node + n_entries; + LINT_INIT(a); + LINT_INIT(b); + LINT_INIT(next); + LINT_INIT(next_node); if (all_size < min_size * 2) { From 591712f53fb0c81bbec4a0b2eb087088280c1468 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/june.mysql.com" <> Date: Tue, 26 Dec 2006 17:47:30 +0400 Subject: [PATCH 15/16] BUG#25048 - ERROR 126 : Incorrect key file for table '.XXXX.MYI'; try to repair it Multi-table delete that is optimized with QUICK_RANGE reports table corruption. DELETE statement must not use KEYREAD optimization, and sets table->no_keyread to 1. This was ignored in QUICK_RANGE optimization. With this fix QUICK_RANGE optimization honors table->no_keyread value and does not enable KEYREAD when it is requested. --- mysql-test/r/index_merge.result | 2 +- mysql-test/r/index_merge_ror.result | 11 +++++++++++ mysql-test/t/index_merge_ror.test | 15 +++++++++++++++ sql/opt_range.cc | 23 +++++++++++++++++------ 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index 3f3360e2da0..9456b4ec978 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -284,7 +284,7 @@ NULL UNION RESULT ALL NULL NULL NULL NULL NULL explain select * from (select * from t1 where key1 = 3 or key2 =3) as Z where key8 >5; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where +2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where; Using index create table t3 like t0; insert into t3 select * from t0; alter table t3 add key9 int not null, add index i9(key9); diff --git a/mysql-test/r/index_merge_ror.result b/mysql-test/r/index_merge_ror.result index 69cd11d1dbf..5d08125be53 100644 --- a/mysql-test/r/index_merge_ror.result +++ b/mysql-test/r/index_merge_ror.result @@ -194,3 +194,14 @@ explain select a from t2 where a='ab'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref a a 6 const 1 Using where drop table t2; +CREATE TABLE t1(c1 INT, c2 INT DEFAULT 0, c3 CHAR(255) DEFAULT '', +KEY(c1), KEY(c2), KEY(c3)); +INSERT INTO t1(c1) VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0); +INSERT INTO t1 VALUES(0,0,0); +CREATE TABLE t2(c1 int); +INSERT INTO t2 VALUES(1); +DELETE t1 FROM t1,t2 WHERE t1.c1=0 AND t1.c2=0; +SELECT * FROM t1; +c1 c2 c3 +DROP TABLE t1,t2; diff --git a/mysql-test/t/index_merge_ror.test b/mysql-test/t/index_merge_ror.test index 48fe5526f11..a9de2f955ee 100644 --- a/mysql-test/t/index_merge_ror.test +++ b/mysql-test/t/index_merge_ror.test @@ -250,3 +250,18 @@ select count(a) from t2 ignore index(a,b) where a='AAAAAAAA' and b='AAAAAAAA'; insert into t2 values ('ab', 'ab', 'uh', 'oh'); explain select a from t2 where a='ab'; drop table t2; + +# +# BUG#25048 - ERROR 126 : Incorrect key file for table '.XXXX.MYI'; try to +# repair it +# +CREATE TABLE t1(c1 INT, c2 INT DEFAULT 0, c3 CHAR(255) DEFAULT '', +KEY(c1), KEY(c2), KEY(c3)); +INSERT INTO t1(c1) VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0); +INSERT INTO t1 VALUES(0,0,0); +CREATE TABLE t2(c1 int); +INSERT INTO t2 VALUES(1); +DELETE t1 FROM t1,t2 WHERE t1.c1=0 AND t1.c2=0; +SELECT * FROM t1; +DROP TABLE t1,t2; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 23a82c6eda7..4659c21f20f 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -875,7 +875,11 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() if (file) { range_end(); - file->extra(HA_EXTRA_NO_KEYREAD); + if (head->key_read) + { + head->key_read= 0; + file->extra(HA_EXTRA_NO_KEYREAD); + } if (free_file) { DBUG_PRINT("info", ("Freeing separate handler 0x%lx (free: %d)", (long) file, @@ -1017,8 +1021,12 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) if (reuse_handler) { DBUG_PRINT("info", ("Reusing handler %p", file)); - if (file->extra(HA_EXTRA_KEYREAD) || - file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) || + if (!head->no_keyread) + { + head->key_read= 1; + file->extra(HA_EXTRA_KEYREAD); + } + if (file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) || init() || reset()) { DBUG_RETURN(1); @@ -1041,9 +1049,12 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) } if (file->external_lock(thd, F_RDLCK)) goto failure; - - if (file->extra(HA_EXTRA_KEYREAD) || - file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) || + if (!head->no_keyread) + { + head->key_read= 1; + file->extra(HA_EXTRA_KEYREAD); + } + if (file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) || init() || reset()) { file->external_lock(thd, F_UNLCK); From 28d3ee656df4d97bee8b3cf1554bef3120cdc8a4 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/june.mysql.com" <> Date: Tue, 26 Dec 2006 22:10:02 +0400 Subject: [PATCH 16/16] After merge fix. --- mysql-test/r/index_merge_myisam.result | 13 ++++++++++++- sql/opt_range.cc | 15 ++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/index_merge_myisam.result b/mysql-test/r/index_merge_myisam.result index 1c60e53a335..45dfe0af505 100644 --- a/mysql-test/r/index_merge_myisam.result +++ b/mysql-test/r/index_merge_myisam.result @@ -286,7 +286,7 @@ NULL UNION RESULT ALL NULL NULL NULL NULL NULL explain select * from (select * from t1 where key1 = 3 or key2 =3) as Z where key8 >5; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where +2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where; Using index create table t3 like t0; insert into t3 select * from t0; alter table t3 add key9 int not null, add index i9(key9); @@ -696,6 +696,17 @@ explain select a from t2 where a='ab'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref a a 6 const 1 Using where drop table t2; +CREATE TABLE t1(c1 INT, c2 INT DEFAULT 0, c3 CHAR(255) DEFAULT '', +KEY(c1), KEY(c2), KEY(c3)); +INSERT INTO t1(c1) VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0); +INSERT INTO t1 VALUES(0,0,0); +CREATE TABLE t2(c1 int); +INSERT INTO t2 VALUES(1); +DELETE t1 FROM t1,t2 WHERE t1.c1=0 AND t1.c2=0; +SELECT * FROM t1; +c1 c2 c3 +DROP TABLE t1,t2; #---------------- Index merge test 2 ------------------------------------------- SET SESSION STORAGE_ENGINE = MyISAM; drop table if exists t1,t2; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fa575e73c39..577fec64e8d 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1002,6 +1002,11 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() if (file) { range_end(); + if (head->key_read) + { + head->key_read= 0; + file->extra(HA_EXTRA_NO_KEYREAD); + } if (free_file) { DBUG_PRINT("info", ("Freeing separate handler 0x%lx (free: %d)", (long) file, @@ -1010,10 +1015,6 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() file->close(); delete file; } - else - { - file->extra(HA_EXTRA_NO_KEYREAD); - } } delete_dynamic(&ranges); /* ranges are allocated in alloc */ free_root(&alloc,MYF(0)); @@ -1195,7 +1196,11 @@ end: org_file= head->file; head->file= file; /* We don't have to set 'head->keyread' here as the 'file' is unique */ - head->mark_columns_used_by_index(index); + if (!head->no_keyread) + { + head->key_read= 1; + head->mark_columns_used_by_index(index); + } head->prepare_for_position(); head->file= org_file; bitmap_copy(&column_bitmap, head->read_set);