You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

292 lines
7.4 KiB

/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Write a record to heap-databas */
#include "heapdef.h"
#ifdef __WIN__
#include <fcntl.h>
#endif
#define LOWFIND 1
#define LOWUSED 2
#define HIGHFIND 4
#define HIGHUSED 8
static byte *next_free_record_pos(HP_SHARE *info);
static HASH_INFO *_hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
ulong records);
int heap_write(HP_INFO *info, const byte *record)
{
uint key;
byte *pos;
HP_SHARE *share=info->s;
DBUG_ENTER("heap_write");
#ifndef DBUG_OFF
if (info->mode & O_RDONLY)
{
DBUG_RETURN(my_errno=EACCES);
}
#endif
if (!(pos=next_free_record_pos(share)))
DBUG_RETURN(my_errno);
share->changed=1;
for (key=0 ; key < share->keys ; key++)
{
if (_hp_write_key(share,share->keydef+key,record,pos))
goto err;
}
memcpy(pos,record,(size_t) share->reclength);
pos[share->reclength]=1; /* Mark record as not deleted */
if (++share->records == share->blength)
share->blength+= share->blength;
info->current_ptr=pos;
info->current_hash_ptr=0;
info->update|=HA_STATE_AKTIV;
#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
#endif
DBUG_RETURN(0);
err:
if (my_errno == HA_ERR_FOUND_DUPP_KEY)
DBUG_PRINT("info",("Duplicate key: %d",key));
info->errkey= key;
/*
Because 'key' is unsigned, we increase it before the loop, unless
we have to skip the key that wasn't inserted yet due to OOM. In
the loop we test 'key' before decreasing it as the protection
against value wraparound.
*/
if (my_errno != ENOMEM)
key++;
while (key-- > 0)
{
if (_hp_delete_key(info,share->keydef+key,record,pos,0))
break;
}
share->deleted++;
*((byte**) pos)=share->del_link;
share->del_link=pos;
pos[share->reclength]=0; /* Record deleted */
DBUG_RETURN(my_errno);
} /* heap_write */
/* Find where to place new record */
static byte *next_free_record_pos(HP_SHARE *info)
{
int block_pos;
byte *pos;
ulong length;
DBUG_ENTER("next_free_record_pos");
if (info->del_link)
{
pos=info->del_link;
info->del_link= *((byte**) pos);
info->deleted--;
DBUG_PRINT("exit",("Used old position: %lx",pos));
DBUG_RETURN(pos);
}
if (!(block_pos=(info->records % info->block.records_in_block)))
{
if (info->records > info->max_records && info->max_records)
{
my_errno=HA_ERR_RECORD_FILE_FULL;
DBUG_RETURN(NULL);
}
if (_hp_get_new_block(&info->block,&length))
DBUG_RETURN(NULL);
info->data_length+=length;
}
DBUG_PRINT("exit",("Used new position: %lx",
(byte*) info->block.level_info[0].last_blocks+block_pos*
info->block.recbuffer));
DBUG_RETURN((byte*) info->block.level_info[0].last_blocks+
block_pos*info->block.recbuffer);
}
/* Write a hash-key to the hash-index */
int _hp_write_key(register HP_SHARE *info, HP_KEYDEF *keyinfo,
const byte *record, byte *recpos)
{
int flag;
ulong halfbuff,hashnr,first_index;
byte *ptr_to_rec,*ptr_to_rec2;
HASH_INFO *empty,*gpos,*gpos2,*pos;
DBUG_ENTER("hp_write_key");
LINT_INIT(gpos); LINT_INIT(gpos2);
LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
flag=0;
if (!(empty= _hp_find_free_hash(info,&keyinfo->block,info->records)))
DBUG_RETURN(-1); /* No more memory */
halfbuff= (long) info->blength >> 1;
pos= hp_find_hash(&keyinfo->block,(first_index=info->records-halfbuff));
if (pos != empty) /* If some records */
{
do
{
hashnr=_hp_rec_hashnr(keyinfo,pos->ptr_to_rec);
if (flag == 0) /* First loop; Check if ok */
if (_hp_mask(hashnr,info->blength,info->records) != first_index)
break;
if (!(hashnr & halfbuff))
{ /* Key will not move */
if (!(flag & LOWFIND))
{
if (flag & HIGHFIND)
{
flag=LOWFIND | HIGHFIND;
/* key shall be moved to the current empty position */
gpos=empty;
ptr_to_rec=pos->ptr_to_rec;
empty=pos; /* This place is now free */
}
else
{
flag=LOWFIND | LOWUSED; /* key isn't changed */
gpos=pos;
ptr_to_rec=pos->ptr_to_rec;
}
}
else
{
if (!(flag & LOWUSED))
{
/* Change link of previous LOW-key */
gpos->ptr_to_rec=ptr_to_rec;
gpos->next_key=pos;
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
}
gpos=pos;
ptr_to_rec=pos->ptr_to_rec;
}
}
else
{ /* key will be moved */
if (!(flag & HIGHFIND))
{
flag= (flag & LOWFIND) | HIGHFIND;
/* key shall be moved to the last (empty) position */
gpos2 = empty; empty=pos;
ptr_to_rec2=pos->ptr_to_rec;
}
else
{
if (!(flag & HIGHUSED))
{
/* Change link of previous hash-key and save */
gpos2->ptr_to_rec=ptr_to_rec2;
gpos2->next_key=pos;
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
}
gpos2=pos;
ptr_to_rec2=pos->ptr_to_rec;
}
}
}
while ((pos=pos->next_key));
if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
{
gpos->ptr_to_rec=ptr_to_rec;
gpos->next_key=0;
}
if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
{
gpos2->ptr_to_rec=ptr_to_rec2;
gpos2->next_key=0;
}
}
/* Check if we are at the empty position */
pos=hp_find_hash(&keyinfo->block,_hp_mask(_hp_rec_hashnr(keyinfo,record),
info->blength,info->records+1));
if (pos == empty)
{
pos->ptr_to_rec=recpos;
pos->next_key=0;
}
else
{
/* Check if more records in same hash-nr family */
empty[0]=pos[0];
gpos=hp_find_hash(&keyinfo->block,
_hp_mask(_hp_rec_hashnr(keyinfo,pos->ptr_to_rec),
info->blength,info->records+1));
if (pos == gpos)
{
pos->ptr_to_rec=recpos;
pos->next_key=empty;
}
else
{
pos->ptr_to_rec=recpos;
pos->next_key=0;
_hp_movelink(pos,gpos,empty);
}
/* Check if duplicated keys */
if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
(!(keyinfo->flag & HA_NULL_PART_KEY) ||
!hp_if_null_in_key(keyinfo, record)))
{
pos=empty;
do
{
if (! _hp_rec_key_cmp(keyinfo,record,pos->ptr_to_rec))
{
DBUG_RETURN(my_errno=HA_ERR_FOUND_DUPP_KEY);
}
} while ((pos=pos->next_key));
}
}
DBUG_RETURN(0);
}
/* Returns ptr to block, and allocates block if neaded */
static HASH_INFO *_hp_find_free_hash(HP_SHARE *info,
HP_BLOCK *block, ulong records)
{
uint block_pos;
ulong length;
if (records < block->last_allocated)
return hp_find_hash(block,records);
if (!(block_pos=(records % block->records_in_block)))
{
if (_hp_get_new_block(block,&length))
return(NULL);
info->index_length+=length;
}
block->last_allocated=records+1;
return((HASH_INFO*) ((byte*) block->level_info[0].last_blocks+
block_pos*block->recbuffer));
}