~drizzle-trunk/drizzle/development

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