1
/* Copyright (C) 2000-2002, 2004-2006 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
/* Write a record to heap-databas */
18
#include "heap_priv.h"
28
using namespace drizzled;
30
static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
33
int heap_write(HP_INFO *info, const unsigned char *record)
35
HP_KEYDEF *keydef, *end;
37
HP_SHARE *share=info->getShare();
39
if ((share->records >= share->max_records && share->max_records) ||
40
(share->recordspace.total_data_length + share->index_length >= share->max_table_size))
42
return(errno=HA_ERR_RECORD_FILE_FULL);
45
if (!(pos=hp_allocate_chunkset(&share->recordspace, 1)))
49
for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
52
if (hp_write_key(info, keydef, record, pos))
56
hp_copy_record_data_to_chunkset(share, record, pos);
58
if (++share->records == share->blength)
59
share->blength+= share->blength;
61
info->current_ptr=pos;
62
info->current_hash_ptr=0;
63
info->update|=HA_STATE_AKTIV;
65
heap_update_auto_increment(info, record);
69
info->errkey= keydef - share->keydef;
70
while (keydef >= share->keydef)
72
if (hp_delete_key(info, keydef, record, pos, 0))
77
hp_free_chunks(&share->recordspace, pos);
83
Write a hash-key to the hash-index
87
record Table record to added
88
recpos Memory buffer where the table record will be stored if added
91
Hash index uses HP_BLOCK structure as a 'growable array' of HASH_INFO
92
structs. Array size == number of entries in hash index.
93
hp_mask(hp_rec_hashnr()) maps hash entries values to hash array positions.
94
If there are several hash entries with the same hash array position P,
95
they are connected in a linked list via HASH_INFO::next_key. The first
96
list element is located at position P, next elements are located at
97
positions for which there is no record that should be located at that
98
position. The order of elements in the list is arbitrary.
103
HA_ERR_FOUND_DUPP_KEY - Duplicate record on unique key. The record was
104
still added and the caller must call hp_delete_key for it.
107
int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
108
const unsigned char *record, unsigned char *recpos)
110
HP_SHARE *share = info->getShare();
112
uint32_t halfbuff,hashnr,first_index;
113
unsigned char *ptr_to_rec= NULL,*ptr_to_rec2= NULL;
114
HASH_INFO *empty, *gpos= NULL, *gpos2= NULL, *pos;
117
if (!(empty= hp_find_free_hash(share,&keyinfo->block,share->records)))
118
return(-1); /* No more memory */
119
halfbuff= (long) share->blength >> 1;
120
pos= hp_find_hash(&keyinfo->block,(first_index=share->records-halfbuff));
123
We're about to add one more hash array position, with hash_mask=#records.
124
The number of hash positions will change and some entries might need to
125
be relocated to the newly added position. Those entries are currently
126
members of the list that starts at #first_index position (this is
127
guaranteed by properties of hp_mask(hp_rec_hashnr(X)) mapping function)
128
At #first_index position currently there may be either:
129
a) An entry with hashnr != first_index. We don't need to move it.
131
b) A list of items with hash_mask=first_index. The list contains entries
133
1) entries that should be relocated to the list that starts at new
134
position we're adding ('uppper' list)
135
2) entries that should be left in the list starting at #first_index
136
position ('lower' list)
138
if (pos != empty) /* If some records */
142
hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
146
First loop, bail out if we're dealing with case a) from above
149
if (hp_mask(hashnr, share->blength, share->records) != first_index)
153
flag & LOWFIND - found a record that should be put into lower position
154
flag & LOWUSED - lower position occupied by the record
155
Same for HIGHFIND and HIGHUSED and 'upper' position
157
gpos - ptr to last element in lower position's list
158
gpos2 - ptr to last element in upper position's list
160
ptr_to_rec - ptr to last entry that should go into lower list.
161
ptr_to_rec2 - same for upper list.
163
if (!(hashnr & halfbuff))
165
/* Key should be put into 'lower' list */
166
if (!(flag & LOWFIND))
168
/* key is the first element to go into lower position */
171
flag=LOWFIND | HIGHFIND;
172
/* key shall be moved to the current empty position */
174
ptr_to_rec=pos->ptr_to_rec;
175
empty=pos; /* This place is now free */
180
We can only get here at first iteration: key is at 'lower'
181
position pos and should be left here.
183
flag=LOWFIND | LOWUSED;
185
ptr_to_rec=pos->ptr_to_rec;
190
/* Already have another key for lower position */
191
if (!(flag & LOWUSED))
193
/* Change link of previous lower-list key */
194
gpos->ptr_to_rec=ptr_to_rec;
196
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
199
ptr_to_rec=pos->ptr_to_rec;
204
/* key will be put into 'higher' list */
205
if (!(flag & HIGHFIND))
207
flag= (flag & LOWFIND) | HIGHFIND;
208
/* key shall be moved to the last (empty) position */
211
ptr_to_rec2=pos->ptr_to_rec;
215
if (!(flag & HIGHUSED))
217
/* Change link of previous upper-list key and save */
218
gpos2->ptr_to_rec=ptr_to_rec2;
220
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
223
ptr_to_rec2=pos->ptr_to_rec;
227
while ((pos=pos->next_key));
229
if ((flag & (LOWFIND | HIGHFIND)) == (LOWFIND | HIGHFIND))
232
If both 'higher' and 'lower' list have at least one element, now
233
there are two hash buckets instead of one.
235
keyinfo->hash_buckets++;
238
if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
240
gpos->ptr_to_rec=ptr_to_rec;
243
if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
245
gpos2->ptr_to_rec=ptr_to_rec2;
249
/* Check if we are at the empty position */
251
pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record),
252
share->blength, share->records + 1));
255
pos->ptr_to_rec=recpos;
257
keyinfo->hash_buckets++;
261
/* Check if more records in same hash-nr family */
263
gpos=hp_find_hash(&keyinfo->block,
264
hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
265
share->blength, share->records + 1));
268
pos->ptr_to_rec=recpos;
273
keyinfo->hash_buckets++;
274
pos->ptr_to_rec=recpos;
276
hp_movelink(pos, gpos, empty);
279
/* Check if duplicated keys */
280
if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
281
(!(keyinfo->flag & HA_NULL_PART_KEY) ||
282
!hp_if_null_in_key(keyinfo, record)))
287
if (! hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec, 1))
289
return(errno=HA_ERR_FOUND_DUPP_KEY);
291
} while ((pos=pos->next_key));
297
/* Returns ptr to block, and allocates block if neaded */
299
static HASH_INFO *hp_find_free_hash(HP_SHARE *info,
300
HP_BLOCK *block, uint32_t records)
305
if (records < block->last_allocated)
306
return hp_find_hash(block,records);
307
if (!(block_pos=(records % block->records_in_block)))
309
if (hp_get_new_block(block,&length))
311
info->index_length+=length;
313
block->last_allocated=records+1;
314
return((HASH_INFO*) ((unsigned char*) block->level_info[0].last_blocks+
315
block_pos*block->recbuffer));