~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/memory/hp_write.cc

  • Committer: patrick crews
  • Date: 2010-09-29 15:15:19 UTC
  • mfrom: (1099.4.188 drizzle)
  • Revision ID: gleebix@gmail.com-20100929151519-6mrmzd1ciw2p9nws
Tags: 2010.09.1802
Update translations

Show diffs side-by-side

added added

removed removed

Lines of Context:
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
15
 
 
16
 
/* Write a record to heap-databas */
17
 
 
18
 
#include "heap_priv.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
 
using namespace drizzled;
29
 
 
30
 
static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
31
 
                                     uint32_t records);
32
 
 
33
 
int heap_write(HP_INFO *info, const unsigned char *record)
34
 
{
35
 
  HP_KEYDEF *keydef, *end;
36
 
  unsigned char *pos;
37
 
  HP_SHARE *share=info->getShare();
38
 
 
39
 
  if ((share->records >= share->max_records && share->max_records) ||
40
 
    (share->recordspace.total_data_length + share->index_length >= share->max_table_size))
41
 
  {
42
 
    return(errno=HA_ERR_RECORD_FILE_FULL);
43
 
  }
44
 
 
45
 
  if (!(pos=hp_allocate_chunkset(&share->recordspace, 1)))
46
 
    return(errno);
47
 
  share->changed=1;
48
 
 
49
 
  for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
50
 
       keydef++)
51
 
  {
52
 
    if (hp_write_key(info, keydef, record, pos))
53
 
      goto err;
54
 
  }
55
 
 
56
 
  hp_copy_record_data_to_chunkset(share, record, pos);
57
 
 
58
 
  if (++share->records == share->blength)
59
 
    share->blength+= share->blength;
60
 
 
61
 
  info->current_ptr=pos;
62
 
  info->current_hash_ptr=0;
63
 
  info->update|=HA_STATE_AKTIV;
64
 
  if (share->auto_key)
65
 
    heap_update_auto_increment(info, record);
66
 
  return(0);
67
 
 
68
 
err:
69
 
  info->errkey= keydef - share->keydef;
70
 
  while (keydef >= share->keydef)
71
 
  {
72
 
    if (hp_delete_key(info, keydef, record, pos, 0))
73
 
      break;
74
 
    keydef--;
75
 
  }
76
 
 
77
 
  hp_free_chunks(&share->recordspace, pos);
78
 
 
79
 
  return(errno);
80
 
} /* heap_write */
81
 
 
82
 
/*
83
 
  Write a hash-key to the hash-index
84
 
  SYNOPSIS
85
 
    info     Heap table info
86
 
    keyinfo  Key info
87
 
    record   Table record to added
88
 
    recpos   Memory buffer where the table record will be stored if added
89
 
             successfully
90
 
  NOTE
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.
99
 
 
100
 
  RETURN
101
 
    0  - OK
102
 
    -1 - Out of memory
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.
105
 
*/
106
 
 
107
 
int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
108
 
                 const unsigned char *record, unsigned char *recpos)
109
 
{
110
 
  HP_SHARE *share = info->getShare();
111
 
  int flag;
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;
115
 
 
116
 
  flag=0;
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));
121
 
 
122
 
  /*
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.
130
 
    or
131
 
    b) A list of items with hash_mask=first_index. The list contains entries
132
 
       of 2 types:
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)
137
 
  */
138
 
  if (pos != empty)                             /* If some records */
139
 
  {
140
 
    do
141
 
    {
142
 
      hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
143
 
      if (flag == 0)
144
 
      {
145
 
        /*
146
 
          First loop, bail out if we're dealing with case a) from above
147
 
          comment
148
 
        */
149
 
        if (hp_mask(hashnr, share->blength, share->records) != first_index)
150
 
          break;
151
 
      }
152
 
      /*
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
156
 
 
157
 
        gpos  - ptr to last element in lower position's list
158
 
        gpos2 - ptr to last element in upper position's list
159
 
 
160
 
        ptr_to_rec - ptr to last entry that should go into lower list.
161
 
        ptr_to_rec2 - same for upper list.
162
 
      */
163
 
      if (!(hashnr & halfbuff))
164
 
      {
165
 
        /* Key should be put into 'lower' list */
166
 
        if (!(flag & LOWFIND))
167
 
        {
168
 
          /* key is the first element to go into lower position */
169
 
          if (flag & HIGHFIND)
170
 
          {
171
 
            flag=LOWFIND | HIGHFIND;
172
 
            /* key shall be moved to the current empty position */
173
 
            gpos=empty;
174
 
            ptr_to_rec=pos->ptr_to_rec;
175
 
            empty=pos;                          /* This place is now free */
176
 
          }
177
 
          else
178
 
          {
179
 
            /*
180
 
              We can only get here at first iteration: key is at 'lower'
181
 
              position pos and should be left here.
182
 
            */
183
 
            flag=LOWFIND | LOWUSED;
184
 
            gpos=pos;
185
 
            ptr_to_rec=pos->ptr_to_rec;
186
 
          }
187
 
        }
188
 
        else
189
 
        {
190
 
          /* Already have another key for lower position */
191
 
          if (!(flag & LOWUSED))
192
 
          {
193
 
            /* Change link of previous lower-list key */
194
 
            gpos->ptr_to_rec=ptr_to_rec;
195
 
            gpos->next_key=pos;
196
 
            flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
197
 
          }
198
 
          gpos=pos;
199
 
          ptr_to_rec=pos->ptr_to_rec;
200
 
        }
201
 
      }
202
 
      else
203
 
      {
204
 
        /* key will be put into 'higher' list */
205
 
        if (!(flag & HIGHFIND))
206
 
        {
207
 
          flag= (flag & LOWFIND) | HIGHFIND;
208
 
          /* key shall be moved to the last (empty) position */
209
 
          gpos2= empty;
210
 
          empty= pos;
211
 
          ptr_to_rec2=pos->ptr_to_rec;
212
 
        }
213
 
        else
214
 
        {
215
 
          if (!(flag & HIGHUSED))
216
 
          {
217
 
            /* Change link of previous upper-list key and save */
218
 
            gpos2->ptr_to_rec=ptr_to_rec2;
219
 
            gpos2->next_key=pos;
220
 
            flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
221
 
          }
222
 
          gpos2=pos;
223
 
          ptr_to_rec2=pos->ptr_to_rec;
224
 
        }
225
 
      }
226
 
    }
227
 
    while ((pos=pos->next_key));
228
 
 
229
 
    if ((flag & (LOWFIND | HIGHFIND)) == (LOWFIND | HIGHFIND))
230
 
    {
231
 
      /*
232
 
        If both 'higher' and 'lower' list have at least one element, now
233
 
        there are two hash buckets instead of one.
234
 
      */
235
 
      keyinfo->hash_buckets++;
236
 
    }
237
 
 
238
 
    if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
239
 
    {
240
 
      gpos->ptr_to_rec=ptr_to_rec;
241
 
      gpos->next_key=0;
242
 
    }
243
 
    if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
244
 
    {
245
 
      gpos2->ptr_to_rec=ptr_to_rec2;
246
 
      gpos2->next_key=0;
247
 
    }
248
 
  }
249
 
  /* Check if we are at the empty position */
250
 
 
251
 
  pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record),
252
 
                                         share->blength, share->records + 1));
253
 
  if (pos == empty)
254
 
  {
255
 
    pos->ptr_to_rec=recpos;
256
 
    pos->next_key=0;
257
 
    keyinfo->hash_buckets++;
258
 
  }
259
 
  else
260
 
  {
261
 
    /* Check if more records in same hash-nr family */
262
 
    empty[0]=pos[0];
263
 
    gpos=hp_find_hash(&keyinfo->block,
264
 
                      hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
265
 
                              share->blength, share->records + 1));
266
 
    if (pos == gpos)
267
 
    {
268
 
      pos->ptr_to_rec=recpos;
269
 
      pos->next_key=empty;
270
 
    }
271
 
    else
272
 
    {
273
 
      keyinfo->hash_buckets++;
274
 
      pos->ptr_to_rec=recpos;
275
 
      pos->next_key=0;
276
 
      hp_movelink(pos, gpos, empty);
277
 
    }
278
 
 
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)))
283
 
    {
284
 
      pos=empty;
285
 
      do
286
 
      {
287
 
        if (! hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec, 1))
288
 
        {
289
 
          return(errno=HA_ERR_FOUND_DUPP_KEY);
290
 
        }
291
 
      } while ((pos=pos->next_key));
292
 
    }
293
 
  }
294
 
  return(0);
295
 
}
296
 
 
297
 
        /* Returns ptr to block, and allocates block if neaded */
298
 
 
299
 
static HASH_INFO *hp_find_free_hash(HP_SHARE *info,
300
 
                                     HP_BLOCK *block, uint32_t records)
301
 
{
302
 
  uint32_t block_pos;
303
 
  size_t length;
304
 
 
305
 
  if (records < block->last_allocated)
306
 
    return hp_find_hash(block,records);
307
 
  if (!(block_pos=(records % block->records_in_block)))
308
 
  {
309
 
    if (hp_get_new_block(block,&length))
310
 
      return(NULL);
311
 
    info->index_length+=length;
312
 
  }
313
 
  block->last_allocated=records+1;
314
 
  return((HASH_INFO*) ((unsigned char*) block->level_info[0].last_blocks+
315
 
                       block_pos*block->recbuffer));
316
 
}