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