MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
hp_write.c
1 /* Copyright (c) 2000-2002, 2004-2007 MySQL AB, 2009 Sun Microsystems, Inc.
2  Use is subject to license terms.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16 
17 /* Write a record to heap-databas */
18 
19 #include "heapdef.h"
20 #ifdef __WIN__
21 #include <fcntl.h>
22 #endif
23 
24 #define LOWFIND 1
25 #define LOWUSED 2
26 #define HIGHFIND 4
27 #define HIGHUSED 8
28 
29 static uchar *next_free_record_pos(HP_SHARE *info);
30 static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
31  ulong records);
32 
33 int heap_write(HP_INFO *info, const uchar *record)
34 {
35  HP_KEYDEF *keydef, *end;
36  uchar *pos;
37  HP_SHARE *share=info->s;
38  DBUG_ENTER("heap_write");
39 #ifndef DBUG_OFF
40  if (info->mode & O_RDONLY)
41  {
42  DBUG_RETURN(my_errno=EACCES);
43  }
44 #endif
45  if (!(pos=next_free_record_pos(share)))
46  DBUG_RETURN(my_errno);
47  share->changed=1;
48 
49  for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
50  keydef++)
51  {
52  if ((*keydef->write_key)(info, keydef, record, pos))
53  goto err;
54  }
55 
56  memcpy(pos,record,(size_t) share->reclength);
57  pos[share->reclength]=1; /* Mark record as not deleted */
58  if (++share->records == share->blength)
59  share->blength+= share->blength;
60  info->current_ptr=pos;
61  info->current_hash_ptr=0;
62  info->update|=HA_STATE_AKTIV;
63 #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
64  DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
65 #endif
66  if (share->auto_key)
67  heap_update_auto_increment(info, record);
68  DBUG_RETURN(0);
69 
70 err:
71  if (my_errno == HA_ERR_FOUND_DUPP_KEY)
72  DBUG_PRINT("info",("Duplicate key: %d", (int) (keydef - share->keydef)));
73  info->errkey= (int) (keydef - share->keydef);
74  /*
75  We don't need to delete non-inserted key from rb-tree. Also, if
76  we got ENOMEM, the key wasn't inserted, so don't try to delete it
77  either. Otherwise for HASH index on HA_ERR_FOUND_DUPP_KEY the key
78  was inserted and we have to delete it.
79  */
80  if (keydef->algorithm == HA_KEY_ALG_BTREE || my_errno == ENOMEM)
81  {
82  keydef--;
83  }
84  while (keydef >= share->keydef)
85  {
86  if ((*keydef->delete_key)(info, keydef, record, pos, 0))
87  break;
88  keydef--;
89  }
90 
91  share->deleted++;
92  *((uchar**) pos)=share->del_link;
93  share->del_link=pos;
94  pos[share->reclength]=0; /* Record deleted */
95 
96  DBUG_RETURN(my_errno);
97 } /* heap_write */
98 
99 /*
100  Write a key to rb_tree-index
101 */
102 
103 int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *record,
104  uchar *recpos)
105 {
106  heap_rb_param custom_arg;
107  uint old_allocated;
108 
109  custom_arg.keyseg= keyinfo->seg;
110  custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
111  if (keyinfo->flag & HA_NOSAME)
112  {
113  custom_arg.search_flag= SEARCH_FIND | SEARCH_UPDATE;
114  keyinfo->rb_tree.flag= TREE_NO_DUPS;
115  }
116  else
117  {
118  custom_arg.search_flag= SEARCH_SAME;
119  keyinfo->rb_tree.flag= 0;
120  }
121  old_allocated= keyinfo->rb_tree.allocated;
122  if (!tree_insert(&keyinfo->rb_tree, (void*)info->recbuf,
123  custom_arg.key_length, &custom_arg))
124  {
125  my_errno= HA_ERR_FOUND_DUPP_KEY;
126  return 1;
127  }
128  info->s->index_length+= (keyinfo->rb_tree.allocated-old_allocated);
129  return 0;
130 }
131 
132  /* Find where to place new record */
133 
134 static uchar *next_free_record_pos(HP_SHARE *info)
135 {
136  int block_pos;
137  uchar *pos;
138  size_t length;
139  DBUG_ENTER("next_free_record_pos");
140 
141  if (info->del_link)
142  {
143  pos=info->del_link;
144  info->del_link= *((uchar**) pos);
145  info->deleted--;
146  DBUG_PRINT("exit",("Used old position: 0x%lx",(long) pos));
147  DBUG_RETURN(pos);
148  }
149  if (!(block_pos=(info->records % info->block.records_in_block)))
150  {
151  if ((info->records > info->max_records && info->max_records) ||
152  (info->data_length + info->index_length >= info->max_table_size))
153  {
154  my_errno=HA_ERR_RECORD_FILE_FULL;
155  DBUG_RETURN(NULL);
156  }
157  if (hp_get_new_block(&info->block,&length))
158  DBUG_RETURN(NULL);
159  info->data_length+=length;
160  }
161  DBUG_PRINT("exit",("Used new position: 0x%lx",
162  (long) ((uchar*) info->block.level_info[0].last_blocks+
163  block_pos * info->block.recbuffer)));
164  DBUG_RETURN((uchar*) info->block.level_info[0].last_blocks+
165  block_pos*info->block.recbuffer);
166 }
167 
168 
169 /*
170  Write a hash-key to the hash-index
171  SYNOPSIS
172  info Heap table info
173  keyinfo Key info
174  record Table record to added
175  recpos Memory buffer where the table record will be stored if added
176  successfully
177  NOTE
178  Hash index uses HP_BLOCK structure as a 'growable array' of HASH_INFO
179  structs. Array size == number of entries in hash index.
180  hp_mask(hp_rec_hashnr()) maps hash entries values to hash array positions.
181  If there are several hash entries with the same hash array position P,
182  they are connected in a linked list via HASH_INFO::next_key. The first
183  list element is located at position P, next elements are located at
184  positions for which there is no record that should be located at that
185  position. The order of elements in the list is arbitrary.
186 
187  RETURN
188  0 - OK
189  -1 - Out of memory
190  HA_ERR_FOUND_DUPP_KEY - Duplicate record on unique key. The record was
191  still added and the caller must call hp_delete_key for it.
192 */
193 
194 int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
195  const uchar *record, uchar *recpos)
196 {
197  HP_SHARE *share = info->s;
198  int flag;
199  ulong halfbuff,hashnr,first_index;
200  uchar *UNINIT_VAR(ptr_to_rec),*UNINIT_VAR(ptr_to_rec2);
201  HASH_INFO *empty,*UNINIT_VAR(gpos),*UNINIT_VAR(gpos2),*pos;
202  DBUG_ENTER("hp_write_key");
203 
204  flag=0;
205  if (!(empty= hp_find_free_hash(share,&keyinfo->block,share->records)))
206  DBUG_RETURN(-1); /* No more memory */
207  halfbuff= (long) share->blength >> 1;
208  pos= hp_find_hash(&keyinfo->block,(first_index=share->records-halfbuff));
209 
210  /*
211  We're about to add one more hash array position, with hash_mask=#records.
212  The number of hash positions will change and some entries might need to
213  be relocated to the newly added position. Those entries are currently
214  members of the list that starts at #first_index position (this is
215  guaranteed by properties of hp_mask(hp_rec_hashnr(X)) mapping function)
216  At #first_index position currently there may be either:
217  a) An entry with hashnr != first_index. We don't need to move it.
218  or
219  b) A list of items with hash_mask=first_index. The list contains entries
220  of 2 types:
221  1) entries that should be relocated to the list that starts at new
222  position we're adding ('uppper' list)
223  2) entries that should be left in the list starting at #first_index
224  position ('lower' list)
225  */
226  if (pos != empty) /* If some records */
227  {
228  do
229  {
230  hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
231  if (flag == 0)
232  {
233  /*
234  First loop, bail out if we're dealing with case a) from above
235  comment
236  */
237  if (hp_mask(hashnr, share->blength, share->records) != first_index)
238  break;
239  }
240  /*
241  flag & LOWFIND - found a record that should be put into lower position
242  flag & LOWUSED - lower position occupied by the record
243  Same for HIGHFIND and HIGHUSED and 'upper' position
244 
245  gpos - ptr to last element in lower position's list
246  gpos2 - ptr to last element in upper position's list
247 
248  ptr_to_rec - ptr to last entry that should go into lower list.
249  ptr_to_rec2 - same for upper list.
250  */
251  if (!(hashnr & halfbuff))
252  {
253  /* Key should be put into 'lower' list */
254  if (!(flag & LOWFIND))
255  {
256  /* key is the first element to go into lower position */
257  if (flag & HIGHFIND)
258  {
259  flag=LOWFIND | HIGHFIND;
260  /* key shall be moved to the current empty position */
261  gpos=empty;
262  ptr_to_rec=pos->ptr_to_rec;
263  empty=pos; /* This place is now free */
264  }
265  else
266  {
267  /*
268  We can only get here at first iteration: key is at 'lower'
269  position pos and should be left here.
270  */
271  flag=LOWFIND | LOWUSED;
272  gpos=pos;
273  ptr_to_rec=pos->ptr_to_rec;
274  }
275  }
276  else
277  {
278  /* Already have another key for lower position */
279  if (!(flag & LOWUSED))
280  {
281  /* Change link of previous lower-list key */
282  gpos->ptr_to_rec=ptr_to_rec;
283  gpos->next_key=pos;
284  flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
285  }
286  gpos=pos;
287  ptr_to_rec=pos->ptr_to_rec;
288  }
289  }
290  else
291  {
292  /* key will be put into 'higher' list */
293  if (!(flag & HIGHFIND))
294  {
295  flag= (flag & LOWFIND) | HIGHFIND;
296  /* key shall be moved to the last (empty) position */
297  gpos2= empty;
298  empty= pos;
299  ptr_to_rec2=pos->ptr_to_rec;
300  }
301  else
302  {
303  if (!(flag & HIGHUSED))
304  {
305  /* Change link of previous upper-list key and save */
306  gpos2->ptr_to_rec=ptr_to_rec2;
307  gpos2->next_key=pos;
308  flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
309  }
310  gpos2=pos;
311  ptr_to_rec2=pos->ptr_to_rec;
312  }
313  }
314  }
315  while ((pos=pos->next_key));
316 
317  if ((flag & (LOWFIND | HIGHFIND)) == (LOWFIND | HIGHFIND))
318  {
319  /*
320  If both 'higher' and 'lower' list have at least one element, now
321  there are two hash buckets instead of one.
322  */
323  keyinfo->hash_buckets++;
324  }
325 
326  if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
327  {
328  gpos->ptr_to_rec=ptr_to_rec;
329  gpos->next_key=0;
330  }
331  if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
332  {
333  gpos2->ptr_to_rec=ptr_to_rec2;
334  gpos2->next_key=0;
335  }
336  }
337  /* Check if we are at the empty position */
338 
339  pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record),
340  share->blength, share->records + 1));
341  if (pos == empty)
342  {
343  pos->ptr_to_rec=recpos;
344  pos->next_key=0;
345  keyinfo->hash_buckets++;
346  }
347  else
348  {
349  /* Check if more records in same hash-nr family */
350  empty[0]=pos[0];
351  gpos=hp_find_hash(&keyinfo->block,
352  hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
353  share->blength, share->records + 1));
354  if (pos == gpos)
355  {
356  pos->ptr_to_rec=recpos;
357  pos->next_key=empty;
358  }
359  else
360  {
361  keyinfo->hash_buckets++;
362  pos->ptr_to_rec=recpos;
363  pos->next_key=0;
364  hp_movelink(pos, gpos, empty);
365  }
366 
367  /* Check if duplicated keys */
368  if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
369  (!(keyinfo->flag & HA_NULL_PART_KEY) ||
370  !hp_if_null_in_key(keyinfo, record)))
371  {
372  pos=empty;
373  do
374  {
375  if (! hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec, 1))
376  {
377  DBUG_RETURN(my_errno=HA_ERR_FOUND_DUPP_KEY);
378  }
379  } while ((pos=pos->next_key));
380  }
381  }
382  DBUG_RETURN(0);
383 }
384 
385  /* Returns ptr to block, and allocates block if neaded */
386 
387 static HASH_INFO *hp_find_free_hash(HP_SHARE *info,
388  HP_BLOCK *block, ulong records)
389 {
390  uint block_pos;
391  size_t length;
392 
393  if (records < block->last_allocated)
394  return hp_find_hash(block,records);
395  if (!(block_pos=(records % block->records_in_block)))
396  {
397  if (hp_get_new_block(block,&length))
398  return(NULL);
399  info->index_length+=length;
400  }
401  block->last_allocated=records+1;
402  return((HASH_INFO*) ((uchar*) block->level_info[0].last_blocks+
403  block_pos*block->recbuffer));
404 }