mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Fixed rare bug in MYISAM introduced in 4.0.3 where the index file header was not updated directly after an UPDATE of split dynamic rows.
myisam/mi_locking.c: Added DBUG info myisam/mi_open.c: Added DBUG info myisam/mi_update.c: More comments Fixed rare bug in MYISAM introduced in 4.0.3 where the index file header was not updated directly after an UPDATE of split dynamic rows. mysql-test/r/myisam.result: Added test case for MyISAM UPDATE bug mysql-test/t/myisam.test: Added test case for MyISAM UPDATE bug
This commit is contained in:
@ -76,6 +76,8 @@ int mi_lock_database(MI_INFO *info, int lock_type)
|
|||||||
}
|
}
|
||||||
if (!count)
|
if (!count)
|
||||||
{
|
{
|
||||||
|
DBUG_PRINT("info",("changed: %u w_locks: %u",
|
||||||
|
(uint) share->changed, share->w_locks));
|
||||||
if (share->changed && !share->w_locks)
|
if (share->changed && !share->w_locks)
|
||||||
{
|
{
|
||||||
share->state.process= share->last_process=share->this_process;
|
share->state.process= share->last_process=share->this_process;
|
||||||
@ -352,6 +354,8 @@ int _mi_writeinfo(register MI_INFO *info, uint operation)
|
|||||||
int error,olderror;
|
int error,olderror;
|
||||||
MYISAM_SHARE *share=info->s;
|
MYISAM_SHARE *share=info->s;
|
||||||
DBUG_ENTER("_mi_writeinfo");
|
DBUG_ENTER("_mi_writeinfo");
|
||||||
|
DBUG_PRINT("info",("operation: %u tot_locks: %u", operation,
|
||||||
|
share->tot_locks));
|
||||||
|
|
||||||
error=0;
|
error=0;
|
||||||
if (share->tot_locks == 0)
|
if (share->tot_locks == 0)
|
||||||
@ -379,9 +383,7 @@ int _mi_writeinfo(register MI_INFO *info, uint operation)
|
|||||||
my_errno=olderror;
|
my_errno=olderror;
|
||||||
}
|
}
|
||||||
else if (operation)
|
else if (operation)
|
||||||
{
|
|
||||||
share->changed= 1; /* Mark keyfile changed */
|
share->changed= 1; /* Mark keyfile changed */
|
||||||
}
|
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
} /* _mi_writeinfo */
|
} /* _mi_writeinfo */
|
||||||
|
|
||||||
|
@ -730,6 +730,7 @@ uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite)
|
|||||||
uchar *ptr=buff;
|
uchar *ptr=buff;
|
||||||
uint i, keys= (uint) state->header.keys,
|
uint i, keys= (uint) state->header.keys,
|
||||||
key_blocks=state->header.max_block_size;
|
key_blocks=state->header.max_block_size;
|
||||||
|
DBUG_ENTER("mi_state_info_write");
|
||||||
|
|
||||||
memcpy_fixed(ptr,&state->header,sizeof(state->header));
|
memcpy_fixed(ptr,&state->header,sizeof(state->header));
|
||||||
ptr+=sizeof(state->header);
|
ptr+=sizeof(state->header);
|
||||||
@ -780,10 +781,10 @@ uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pWrite & 1)
|
if (pWrite & 1)
|
||||||
return my_pwrite(file,(char*) buff, (uint) (ptr-buff), 0L,
|
DBUG_RETURN(my_pwrite(file,(char*) buff, (uint) (ptr-buff), 0L,
|
||||||
MYF(MY_NABP | MY_THREADSAFE));
|
MYF(MY_NABP | MY_THREADSAFE)));
|
||||||
else
|
DBUG_RETURN(my_write(file, (char*) buff, (uint) (ptr-buff),
|
||||||
return my_write(file, (char*) buff, (uint) (ptr-buff), MYF(MY_NABP));
|
MYF(MY_NABP)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +94,14 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec)
|
|||||||
if (_mi_ft_cmp(info,i,oldrec, newrec))
|
if (_mi_ft_cmp(info,i,oldrec, newrec))
|
||||||
{
|
{
|
||||||
if ((int) i == info->lastinx)
|
if ((int) i == info->lastinx)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We are changeing the index we are reading on. Mark that
|
||||||
|
the index data has changed and we need to do a full search
|
||||||
|
when doing read-next
|
||||||
|
*/
|
||||||
key_changed|=HA_STATE_WRITTEN;
|
key_changed|=HA_STATE_WRITTEN;
|
||||||
|
}
|
||||||
changed|=((ulonglong) 1 << i);
|
changed|=((ulonglong) 1 << i);
|
||||||
if (_mi_ft_update(info,i,(char*) old_key,oldrec,newrec,pos))
|
if (_mi_ft_update(info,i,(char*) old_key,oldrec,newrec,pos))
|
||||||
goto err;
|
goto err;
|
||||||
@ -121,25 +128,36 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec)
|
|||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
If we are running with external locking, we must update the index file
|
If we are running with external locking, we must update the index file
|
||||||
that something has changed
|
that something has changed.
|
||||||
*/
|
*/
|
||||||
if (changed || !my_disable_locking)
|
if (changed || !my_disable_locking)
|
||||||
key_changed|= HA_STATE_KEY_CHANGED;
|
key_changed|= HA_STATE_CHANGED;
|
||||||
|
|
||||||
if (share->calc_checksum)
|
if (share->calc_checksum)
|
||||||
{
|
{
|
||||||
info->checksum=(*share->calc_checksum)(info,newrec);
|
info->checksum=(*share->calc_checksum)(info,newrec);
|
||||||
key_changed|= HA_STATE_KEY_CHANGED; /* Must update index file */
|
/* Store new checksum in index file header */
|
||||||
|
key_changed|= HA_STATE_CHANGED;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
/* Don't update index file if data file is not extended */
|
/*
|
||||||
|
Don't update index file if data file is not extended and no status
|
||||||
|
information changed
|
||||||
|
*/
|
||||||
MI_STATUS_INFO state;
|
MI_STATUS_INFO state;
|
||||||
|
ha_rows org_split;
|
||||||
|
my_off_t org_delete_link;
|
||||||
|
|
||||||
memcpy((char*) &state, (char*) info->state, sizeof(state));
|
memcpy((char*) &state, (char*) info->state, sizeof(state));
|
||||||
|
org_split= share->state.split;
|
||||||
|
org_delete_link= share->state.dellink;
|
||||||
if ((*share->update_record)(info,pos,newrec))
|
if ((*share->update_record)(info,pos,newrec))
|
||||||
goto err;
|
goto err;
|
||||||
if (!key_changed &&
|
if (!key_changed &&
|
||||||
memcmp((char*) &state, (char*) info->state, sizeof(state)))
|
(memcmp((char*) &state, (char*) info->state, sizeof(state)) ||
|
||||||
key_changed|= HA_STATE_KEY_CHANGED; /* Must update index file */
|
org_split != share->state.split ||
|
||||||
|
org_delete_link != share->state.dellink))
|
||||||
|
key_changed|= HA_STATE_CHANGED; /* Must update index file */
|
||||||
}
|
}
|
||||||
if (auto_key_changed)
|
if (auto_key_changed)
|
||||||
update_auto_increment(info,newrec);
|
update_auto_increment(info,newrec);
|
||||||
@ -163,7 +181,7 @@ err:
|
|||||||
DBUG_PRINT("error",("key: %d errno: %d",i,my_errno));
|
DBUG_PRINT("error",("key: %d errno: %d",i,my_errno));
|
||||||
save_errno=my_errno;
|
save_errno=my_errno;
|
||||||
if (changed)
|
if (changed)
|
||||||
key_changed|= HA_STATE_KEY_CHANGED;
|
key_changed|= HA_STATE_CHANGED;
|
||||||
if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL)
|
if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL)
|
||||||
{
|
{
|
||||||
info->errkey= (int) i;
|
info->errkey= (int) i;
|
||||||
|
@ -364,3 +364,25 @@ explain select * from t1 force index (a) where a=0 or a=2;
|
|||||||
table type possible_keys key key_len ref rows Extra
|
table type possible_keys key key_len ref rows Extra
|
||||||
t1 range a a 4 NULL 4 Using where
|
t1 range a a 4 NULL 4 Using where
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
|
create table t1 (a int not null auto_increment primary key, b varchar(255));
|
||||||
|
insert into t1 (b) values (repeat('a',100)),(repeat('b',100)),(repeat('c',100));
|
||||||
|
update t1 set b=repeat(left(b,1),200) where a=1;
|
||||||
|
delete from t1 where (a & 1)= 0;
|
||||||
|
update t1 set b=repeat('e',200) where a=1;
|
||||||
|
flush tables;
|
||||||
|
check table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 check status OK
|
||||||
|
update t1 set b=repeat(left(b,1),255) where a between 1 and 5;
|
||||||
|
update t1 set b=repeat(left(b,1),10) where a between 32 and 43;
|
||||||
|
update t1 set b=repeat(left(b,1),2) where a between 64 and 66;
|
||||||
|
update t1 set b=repeat(left(b,1),65) where a between 67 and 70;
|
||||||
|
check table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 check status OK
|
||||||
|
insert into t1 (b) values (repeat('z',100));
|
||||||
|
update t1 set b="test" where left(b,1) > 'n';
|
||||||
|
check table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 check status OK
|
||||||
|
drop table t1;
|
||||||
|
@ -352,3 +352,37 @@ explain select * from t1,t2 force index(c) where t1.a=t2.a;
|
|||||||
explain select * from t1 where a=0 or a=2;
|
explain select * from t1 where a=0 or a=2;
|
||||||
explain select * from t1 force index (a) where a=0 or a=2;
|
explain select * from t1 force index (a) where a=0 or a=2;
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test bug when updating a split dynamic row where keys are not changed
|
||||||
|
#
|
||||||
|
|
||||||
|
create table t1 (a int not null auto_increment primary key, b varchar(255));
|
||||||
|
insert into t1 (b) values (repeat('a',100)),(repeat('b',100)),(repeat('c',100));
|
||||||
|
update t1 set b=repeat(left(b,1),200) where a=1;
|
||||||
|
delete from t1 where (a & 1)= 0;
|
||||||
|
update t1 set b=repeat('e',200) where a=1;
|
||||||
|
flush tables;
|
||||||
|
check table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# check updating with keys
|
||||||
|
#
|
||||||
|
|
||||||
|
disable_query_log;
|
||||||
|
let $1 = 100;
|
||||||
|
while ($1)
|
||||||
|
{
|
||||||
|
eval insert into t1 (b) values (repeat(char(($1 & 32)+65), $1));
|
||||||
|
dec $1;
|
||||||
|
}
|
||||||
|
enable_query_log;
|
||||||
|
update t1 set b=repeat(left(b,1),255) where a between 1 and 5;
|
||||||
|
update t1 set b=repeat(left(b,1),10) where a between 32 and 43;
|
||||||
|
update t1 set b=repeat(left(b,1),2) where a between 64 and 66;
|
||||||
|
update t1 set b=repeat(left(b,1),65) where a between 67 and 70;
|
||||||
|
check table t1;
|
||||||
|
insert into t1 (b) values (repeat('z',100));
|
||||||
|
update t1 set b="test" where left(b,1) > 'n';
|
||||||
|
check table t1;
|
||||||
|
drop table t1;
|
||||||
|
Reference in New Issue
Block a user