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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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->s;
38
uint32_t rec_length, chunk_count;
40
if ((share->records >= share->max_records && share->max_records) ||
41
(share->recordspace.total_data_length + share->index_length >= share->max_table_size))
43
return(errno=HA_ERR_RECORD_FILE_FULL);
46
rec_length = hp_get_encoded_data_length(share, record, &chunk_count);
48
if (!(pos=hp_allocate_chunkset(&share->recordspace, chunk_count)))
52
for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
55
if ((*keydef->write_key)(info, keydef, record, pos))
59
hp_copy_record_data_to_chunkset(share, record, pos);
61
if (++share->records == share->blength)
62
share->blength+= share->blength;
64
info->current_ptr=pos;
65
info->current_hash_ptr=0;
66
info->update|=HA_STATE_AKTIV;
68
heap_update_auto_increment(info, record);
72
info->errkey= keydef - share->keydef;
74
We don't need to delete non-inserted key from rb-tree. Also, if
75
we got ENOMEM, the key wasn't inserted, so don't try to delete it
76
either. Otherwise for HASH index on HA_ERR_FOUND_DUPP_KEY the key
77
was inserted and we have to delete it.
79
if (keydef->algorithm == HA_KEY_ALG_BTREE || errno == ENOMEM)
83
while (keydef >= share->keydef)
85
if ((*keydef->delete_key)(info, keydef, record, pos, 0))
90
hp_free_chunks(&share->recordspace, pos);
96
Write a key to rb_tree-index
99
int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, const unsigned char *record,
100
unsigned char *recpos)
102
heap_rb_param custom_arg;
103
uint32_t old_allocated;
105
custom_arg.keyseg= keyinfo->seg;
106
custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
107
if (keyinfo->flag & HA_NOSAME)
109
custom_arg.search_flag= SEARCH_FIND | SEARCH_UPDATE;
110
keyinfo->rb_tree.flag= TREE_NO_DUPS;
114
custom_arg.search_flag= SEARCH_SAME;
115
keyinfo->rb_tree.flag= 0;
117
old_allocated= keyinfo->rb_tree.allocated;
118
if (!tree_insert(&keyinfo->rb_tree, (void*)info->recbuf,
119
custom_arg.key_length, &custom_arg))
121
errno= HA_ERR_FOUND_DUPP_KEY;
124
info->s->index_length+= (keyinfo->rb_tree.allocated-old_allocated);
129
Write a hash-key to the hash-index
133
record Table record to added
134
recpos Memory buffer where the table record will be stored if added
137
Hash index uses HP_BLOCK structure as a 'growable array' of HASH_INFO
138
structs. Array size == number of entries in hash index.
139
hp_mask(hp_rec_hashnr()) maps hash entries values to hash array positions.
140
If there are several hash entries with the same hash array position P,
141
they are connected in a linked list via HASH_INFO::next_key. The first
142
list element is located at position P, next elements are located at
143
positions for which there is no record that should be located at that
144
position. The order of elements in the list is arbitrary.
149
HA_ERR_FOUND_DUPP_KEY - Duplicate record on unique key. The record was
150
still added and the caller must call hp_delete_key for it.
153
int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
154
const unsigned char *record, unsigned char *recpos)
156
HP_SHARE *share = info->s;
158
uint32_t halfbuff,hashnr,first_index;
159
unsigned char *ptr_to_rec= NULL,*ptr_to_rec2= NULL;
160
HASH_INFO *empty, *gpos= NULL, *gpos2= NULL, *pos;
163
if (!(empty= hp_find_free_hash(share,&keyinfo->block,share->records)))
164
return(-1); /* No more memory */
165
halfbuff= (long) share->blength >> 1;
166
pos= hp_find_hash(&keyinfo->block,(first_index=share->records-halfbuff));
169
We're about to add one more hash array position, with hash_mask=#records.
170
The number of hash positions will change and some entries might need to
171
be relocated to the newly added position. Those entries are currently
172
members of the list that starts at #first_index position (this is
173
guaranteed by properties of hp_mask(hp_rec_hashnr(X)) mapping function)
174
At #first_index position currently there may be either:
175
a) An entry with hashnr != first_index. We don't need to move it.
177
b) A list of items with hash_mask=first_index. The list contains entries
179
1) entries that should be relocated to the list that starts at new
180
position we're adding ('uppper' list)
181
2) entries that should be left in the list starting at #first_index
182
position ('lower' list)
184
if (pos != empty) /* If some records */
188
hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
192
First loop, bail out if we're dealing with case a) from above
195
if (hp_mask(hashnr, share->blength, share->records) != first_index)
199
flag & LOWFIND - found a record that should be put into lower position
200
flag & LOWUSED - lower position occupied by the record
201
Same for HIGHFIND and HIGHUSED and 'upper' position
203
gpos - ptr to last element in lower position's list
204
gpos2 - ptr to last element in upper position's list
206
ptr_to_rec - ptr to last entry that should go into lower list.
207
ptr_to_rec2 - same for upper list.
209
if (!(hashnr & halfbuff))
211
/* Key should be put into 'lower' list */
212
if (!(flag & LOWFIND))
214
/* key is the first element to go into lower position */
217
flag=LOWFIND | HIGHFIND;
218
/* key shall be moved to the current empty position */
220
ptr_to_rec=pos->ptr_to_rec;
221
empty=pos; /* This place is now free */
226
We can only get here at first iteration: key is at 'lower'
227
position pos and should be left here.
229
flag=LOWFIND | LOWUSED;
231
ptr_to_rec=pos->ptr_to_rec;
236
/* Already have another key for lower position */
237
if (!(flag & LOWUSED))
239
/* Change link of previous lower-list key */
240
gpos->ptr_to_rec=ptr_to_rec;
242
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
245
ptr_to_rec=pos->ptr_to_rec;
250
/* key will be put into 'higher' list */
251
if (!(flag & HIGHFIND))
253
flag= (flag & LOWFIND) | HIGHFIND;
254
/* key shall be moved to the last (empty) position */
257
ptr_to_rec2=pos->ptr_to_rec;
261
if (!(flag & HIGHUSED))
263
/* Change link of previous upper-list key and save */
264
gpos2->ptr_to_rec=ptr_to_rec2;
266
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
269
ptr_to_rec2=pos->ptr_to_rec;
273
while ((pos=pos->next_key));
275
if ((flag & (LOWFIND | HIGHFIND)) == (LOWFIND | HIGHFIND))
278
If both 'higher' and 'lower' list have at least one element, now
279
there are two hash buckets instead of one.
281
keyinfo->hash_buckets++;
284
if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
286
gpos->ptr_to_rec=ptr_to_rec;
289
if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
291
gpos2->ptr_to_rec=ptr_to_rec2;
295
/* Check if we are at the empty position */
297
pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record),
298
share->blength, share->records + 1));
301
pos->ptr_to_rec=recpos;
303
keyinfo->hash_buckets++;
307
/* Check if more records in same hash-nr family */
309
gpos=hp_find_hash(&keyinfo->block,
310
hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
311
share->blength, share->records + 1));
314
pos->ptr_to_rec=recpos;
319
keyinfo->hash_buckets++;
320
pos->ptr_to_rec=recpos;
322
hp_movelink(pos, gpos, empty);
325
/* Check if duplicated keys */
326
if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
327
(!(keyinfo->flag & HA_NULL_PART_KEY) ||
328
!hp_if_null_in_key(keyinfo, record)))
333
if (! hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec, 1))
335
return(errno=HA_ERR_FOUND_DUPP_KEY);
337
} while ((pos=pos->next_key));
343
/* Returns ptr to block, and allocates block if neaded */
345
static HASH_INFO *hp_find_free_hash(HP_SHARE *info,
346
HP_BLOCK *block, uint32_t records)
351
if (records < block->last_allocated)
352
return hp_find_hash(block,records);
353
if (!(block_pos=(records % block->records_in_block)))
355
if (hp_get_new_block(block,&length))
357
info->index_length+=length;
359
block->last_allocated=records+1;
360
return((HASH_INFO*) ((unsigned char*) block->level_info[0].last_blocks+
361
block_pos*block->recbuffer));