diff options
Diffstat (limited to 'myisam/mi_dynrec.c')
-rw-r--r-- | myisam/mi_dynrec.c | 144 |
1 files changed, 103 insertions, 41 deletions
diff --git a/myisam/mi_dynrec.c b/myisam/mi_dynrec.c index 079779bd0ef..30a4762abe0 100644 --- a/myisam/mi_dynrec.c +++ b/myisam/mi_dynrec.c @@ -14,7 +14,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - /* Functions to handle space-packed-records and blobs */ +/* + Functions to handle space-packed-records and blobs + + A row may be stored in one or more linked blocks. + The block size is between MI_MIN_BLOCK_LENGTH and MI_MAX_BLOCK_LENGTH. + Each block is aligned on MI_DYN_ALIGN_SIZE. + The reson for the max block size is to not have too many different types + of blocks. For the differnet block types, look at _mi_get_block_info() +*/ #include "myisamdef.h" #include <assert.h> @@ -148,7 +156,7 @@ static int write_dynamic_record(MI_INFO *info, const byte *record, } while (reclength); DBUG_RETURN(0); - err: +err: DBUG_RETURN(1); } @@ -264,37 +272,62 @@ static bool unlink_deleted_block(MI_INFO *info, MI_BLOCK_INFO *block_info) DBUG_RETURN(0); } - /* Delete datarecord from database */ - /* info->rec_cache.seek_not_done is updated in cmp_record */ -static int delete_dynamic_record(MI_INFO *info, my_off_t filepos, - uint second_read) +/* + Add a backward link to delete block + + SYNOPSIS + update_backward_delete_link() + info MyISAM handler + delete_block Position to delete block to update. + If this is 'HA_OFFSET_ERROR', nothing will be done + filepos Position to block that 'delete_block' should point to + + RETURN + 0 ok + 1 error. In this case my_error is set. +*/ + +static int update_backward_delete_link(MI_INFO *info, my_off_t delete_block, + my_off_t filepos) { - uint length,b_type; - MI_BLOCK_INFO block_info,del_block; - int error=0; - my_bool remove_next_block; - DBUG_ENTER("delete_dynamic_record"); + MI_BLOCK_INFO block_info; + DBUG_ENTER("update_backward_delete_link"); - /* First add a link from the last block to the new one */ - if (info->s->state.dellink != HA_OFFSET_ERROR) + if (delete_block != HA_OFFSET_ERROR) { block_info.second_read=0; - if (_mi_get_block_info(&block_info,info->dfile,info->s->state.dellink) + if (_mi_get_block_info(&block_info,info->dfile,delete_block) & BLOCK_DELETED) { char buff[8]; mi_sizestore(buff,filepos); - if (my_pwrite(info->dfile,buff,8,info->s->state.dellink+12, - MYF(MY_NABP))) - error=1; /* Error on write */ + if (my_pwrite(info->dfile,buff, 8, delete_block+12, MYF(MY_NABP))) + DBUG_RETURN(1); /* Error on write */ } else { - error=1; /* Wrong delete link */ my_errno=HA_ERR_WRONG_IN_RECORD; + DBUG_RETURN(1); /* Wrong delete link */ } } + return 0; +} + + /* Delete datarecord from database */ + /* info->rec_cache.seek_not_done is updated in cmp_record */ + +static int delete_dynamic_record(MI_INFO *info, my_off_t filepos, + uint second_read) +{ + uint length,b_type; + MI_BLOCK_INFO block_info,del_block; + int error; + my_bool remove_next_block; + DBUG_ENTER("delete_dynamic_record"); + + /* First add a link from the last block to the new one */ + error= update_backward_delete_link(info, info->s->state.dellink, filepos); block_info.second_read=second_read; do @@ -518,21 +551,11 @@ int _mi_write_part_record(MI_INFO *info, *reclength-=(length-head_length); *flag=6; - if (del_length && next_delete_block != HA_OFFSET_ERROR) + if (del_length) { /* link the next delete block to this */ - MI_BLOCK_INFO del_block; - del_block.second_read=0; - if (!(_mi_get_block_info(&del_block,info->dfile,next_delete_block) - & BLOCK_DELETED)) - { - my_errno=HA_ERR_WRONG_IN_RECORD; - goto err; - } - mi_sizestore(del_block.header+12,info->s->state.dellink); - if (my_pwrite(info->dfile,(char*) del_block.header+12,8, - next_delete_block+12, - MYF(MY_NABP))) + if (update_backward_delete_link(info, next_delete_block, + info->s->state.dellink)) goto err; } @@ -574,6 +597,8 @@ static int update_dynamic_record(MI_INFO *info, my_off_t filepos, byte *record, { uint tmp=MY_ALIGN(reclength - length + 3 + test(reclength >= 65520L),MI_DYN_ALIGN_SIZE); + /* Don't create a block bigger than MI_MAX_BLOCK_LENGTH */ + tmp= min(length+tmp, MI_MAX_BLOCK_LENGTH)-length; /* Check if we can extend this block */ if (block_info.filepos + block_info.block_len == info->state->data_file_length && @@ -588,9 +613,15 @@ static int update_dynamic_record(MI_INFO *info, my_off_t filepos, byte *record, info->update|= HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK; length+=tmp; } - else + else if (length < MI_MAX_BLOCK_LENGTH - MI_MIN_BLOCK_LENGTH) { - /* Check if next block is a deleted block */ + /* + Check if next block is a deleted block + Above we have MI_MIN_BLOCK_LENGTH to avoid the problem where + the next block is so small it can't be splited which could + casue problems + */ + MI_BLOCK_INFO del_block; del_block.second_read=0; if (_mi_get_block_info(&del_block,info->dfile, @@ -601,7 +632,35 @@ static int update_dynamic_record(MI_INFO *info, my_off_t filepos, byte *record, DBUG_PRINT("info",("Extending current block")); if (unlink_deleted_block(info,&del_block)) goto err; - length+=del_block.block_len; + if ((length+=del_block.block_len) > MI_MAX_BLOCK_LENGTH) + { + /* + New block was too big, link overflow part back to + delete list + */ + my_off_t next_pos; + ulong rest_length= length-MI_MAX_BLOCK_LENGTH; + set_if_bigger(rest_length, MI_MIN_BLOCK_LENGTH); + next_pos= del_block.filepos+ del_block.block_len - rest_length; + + if (update_backward_delete_link(info, info->s->state.dellink, + next_pos)) + DBUG_RETURN(1); + + /* create delete link for data that didn't fit into the page */ + del_block.header[0]=0; + mi_int3store(del_block.header+1, rest_length); + mi_sizestore(del_block.header+4,info->s->state.dellink); + bfill(del_block.header+12,8,255); + if (my_pwrite(info->dfile,(byte*) del_block.header,20, next_pos, + MYF(MY_NABP))) + DBUG_RETURN(1); + info->s->state.dellink= next_pos; + info->s->state.split++; + info->state->del++; + info->state->empty+= rest_length; + length-= rest_length; + } } } } @@ -615,7 +674,10 @@ static int update_dynamic_record(MI_INFO *info, my_off_t filepos, byte *record, &record,&reclength,&flag)) goto err; if ((filepos=block_info.next_filepos) == HA_OFFSET_ERROR) + { + /* Start writing data on deleted blocks */ filepos=info->s->state.dellink; + } } if (block_info.next_filepos != HA_OFFSET_ERROR) @@ -744,7 +806,8 @@ uint _mi_rec_pack(MI_INFO *info, register byte *to, register const byte *from) Returns 0 if record is ok. */ -my_bool _mi_rec_check(MI_INFO *info,const char *record, byte *rec_buff) +my_bool _mi_rec_check(MI_INFO *info,const char *record, byte *rec_buff, + ulong packed_length) { uint length,new_length,flag,bit,i; char *pos,*end,*packpos,*to; @@ -836,8 +899,7 @@ my_bool _mi_rec_check(MI_INFO *info,const char *record, byte *rec_buff) to+=length; } } - if (info->packed_length != (uint) (to - rec_buff) - + test(info->s->calc_checksum) || + if (packed_length != (uint) (to - rec_buff) + test(info->s->calc_checksum) || (bit != 1 && (flag & ~(bit - 1)))) goto err; if (info->s->calc_checksum) @@ -850,7 +912,7 @@ my_bool _mi_rec_check(MI_INFO *info,const char *record, byte *rec_buff) } DBUG_RETURN(0); - err: +err: DBUG_RETURN(1); } @@ -966,8 +1028,8 @@ ulong _mi_rec_unpack(register MI_INFO *info, register byte *to, byte *from, if (info->s->calc_checksum) from++; if (to == to_end && from == from_end && (bit == 1 || !(flag & ~(bit-1)))) - DBUG_RETURN((info->packed_length=found_length)); - err: + DBUG_RETURN(found_length); +err: my_errno=HA_ERR_RECORD_DELETED; DBUG_PRINT("error",("to_end: %lx -> %lx from_end: %lx -> %lx", to,to_end,from,from_end)); @@ -1210,7 +1272,7 @@ int _mi_cmp_dynamic_record(register MI_INFO *info, register const byte *record) } } my_errno=0; - err: +err: if (buffer != info->rec_buff) my_afree((gptr) buffer); DBUG_RETURN(my_errno); |