Drizzled Public API Documentation

hp_write.cc
00001 /* Copyright (C) 2000-2002, 2004-2006 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 /* Write a record to heap-databas */
00017 
00018 #include "heap_priv.h"
00019 #ifdef __WIN__
00020 #include <fcntl.h>
00021 #endif
00022 
00023 #include <drizzled/error_t.h>
00024 
00025 #define LOWFIND 1
00026 #define LOWUSED 2
00027 #define HIGHFIND 4
00028 #define HIGHUSED 8
00029 
00030 using namespace drizzled;
00031 
00032 static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
00033              uint32_t records);
00034 
00035 int heap_write(HP_INFO *info, const unsigned char *record)
00036 {
00037   HP_KEYDEF *keydef, *end;
00038   unsigned char *pos;
00039   HP_SHARE *share=info->getShare();
00040 
00041   if ((share->records >= share->max_records && share->max_records) ||
00042     (share->recordspace.total_data_length + share->index_length >= share->max_table_size))
00043   {
00044     return(errno=HA_ERR_RECORD_FILE_FULL);
00045   }
00046 
00047   if (!(pos=hp_allocate_chunkset(&share->recordspace, 1)))
00048     return(errno);
00049   share->changed=1;
00050 
00051   for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
00052        keydef++)
00053   {
00054     if (hp_write_key(info, keydef, record, pos))
00055       goto err;
00056   }
00057 
00058   hp_copy_record_data_to_chunkset(share, record, pos);
00059 
00060   if (++share->records == share->blength)
00061     share->blength+= share->blength;
00062 
00063   info->current_ptr=pos;
00064   info->current_hash_ptr=0;
00065   info->update|=HA_STATE_AKTIV;
00066   if (share->auto_key)
00067     heap_update_auto_increment(info, record);
00068   return(0);
00069 
00070 err:
00071   info->errkey= keydef - share->keydef;
00072   while (keydef >= share->keydef)
00073   {
00074     if (hp_delete_key(info, keydef, record, pos, 0))
00075       break;
00076     keydef--;
00077   }
00078 
00079   hp_free_chunks(&share->recordspace, pos);
00080 
00081   return(errno);
00082 } /* heap_write */
00083 
00084 /*
00085   Write a hash-key to the hash-index
00086   SYNOPSIS
00087     info     Heap table info
00088     keyinfo  Key info
00089     record   Table record to added
00090     recpos   Memory buffer where the table record will be stored if added
00091              successfully
00092   NOTE
00093     Hash index uses HP_BLOCK structure as a 'growable array' of HASH_INFO
00094     structs. Array size == number of entries in hash index.
00095     hp_mask(hp_rec_hashnr()) maps hash entries values to hash array positions.
00096     If there are several hash entries with the same hash array position P,
00097     they are connected in a linked list via HASH_INFO::next_key. The first
00098     list element is located at position P, next elements are located at
00099     positions for which there is no record that should be located at that
00100     position. The order of elements in the list is arbitrary.
00101 
00102   RETURN
00103     0  - OK
00104     -1 - Out of memory
00105     HA_ERR_FOUND_DUPP_KEY - Duplicate record on unique key. The record was
00106     still added and the caller must call hp_delete_key for it.
00107 */
00108 
00109 int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
00110      const unsigned char *record, unsigned char *recpos)
00111 {
00112   HP_SHARE *share = info->getShare();
00113   int flag;
00114   uint32_t halfbuff,hashnr,first_index;
00115   unsigned char *ptr_to_rec= NULL,*ptr_to_rec2= NULL;
00116   HASH_INFO *empty, *gpos= NULL, *gpos2= NULL, *pos;
00117 
00118   flag=0;
00119   if (!(empty= hp_find_free_hash(share,&keyinfo->block,share->records)))
00120     return(-1);       /* No more memory */
00121   halfbuff= (long) share->blength >> 1;
00122   pos= hp_find_hash(&keyinfo->block,(first_index=share->records-halfbuff));
00123 
00124   /*
00125     We're about to add one more hash array position, with hash_mask=#records.
00126     The number of hash positions will change and some entries might need to
00127     be relocated to the newly added position. Those entries are currently
00128     members of the list that starts at #first_index position (this is
00129     guaranteed by properties of hp_mask(hp_rec_hashnr(X)) mapping function)
00130     At #first_index position currently there may be either:
00131     a) An entry with hashnr != first_index. We don't need to move it.
00132     or
00133     b) A list of items with hash_mask=first_index. The list contains entries
00134        of 2 types:
00135        1) entries that should be relocated to the list that starts at new
00136           position we're adding ('uppper' list)
00137        2) entries that should be left in the list starting at #first_index
00138           position ('lower' list)
00139   */
00140   if (pos != empty)       /* If some records */
00141   {
00142     do
00143     {
00144       hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
00145       if (flag == 0)
00146       {
00147         /*
00148           First loop, bail out if we're dealing with case a) from above
00149           comment
00150         */
00151   if (hp_mask(hashnr, share->blength, share->records) != first_index)
00152     break;
00153       }
00154       /*
00155         flag & LOWFIND - found a record that should be put into lower position
00156         flag & LOWUSED - lower position occupied by the record
00157         Same for HIGHFIND and HIGHUSED and 'upper' position
00158 
00159         gpos  - ptr to last element in lower position's list
00160         gpos2 - ptr to last element in upper position's list
00161 
00162         ptr_to_rec - ptr to last entry that should go into lower list.
00163         ptr_to_rec2 - same for upper list.
00164       */
00165       if (!(hashnr & halfbuff))
00166       {
00167         /* Key should be put into 'lower' list */
00168   if (!(flag & LOWFIND))
00169   {
00170           /* key is the first element to go into lower position */
00171     if (flag & HIGHFIND)
00172     {
00173       flag=LOWFIND | HIGHFIND;
00174       /* key shall be moved to the current empty position */
00175       gpos=empty;
00176       ptr_to_rec=pos->ptr_to_rec;
00177       empty=pos;        /* This place is now free */
00178     }
00179     else
00180     {
00181             /*
00182               We can only get here at first iteration: key is at 'lower'
00183               position pos and should be left here.
00184             */
00185       flag=LOWFIND | LOWUSED;
00186       gpos=pos;
00187       ptr_to_rec=pos->ptr_to_rec;
00188     }
00189   }
00190   else
00191         {
00192           /* Already have another key for lower position */
00193     if (!(flag & LOWUSED))
00194     {
00195       /* Change link of previous lower-list key */
00196       gpos->ptr_to_rec=ptr_to_rec;
00197       gpos->next_key=pos;
00198       flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
00199     }
00200     gpos=pos;
00201     ptr_to_rec=pos->ptr_to_rec;
00202   }
00203       }
00204       else
00205       {
00206         /* key will be put into 'higher' list */
00207   if (!(flag & HIGHFIND))
00208   {
00209     flag= (flag & LOWFIND) | HIGHFIND;
00210     /* key shall be moved to the last (empty) position */
00211     gpos2= empty;
00212           empty= pos;
00213     ptr_to_rec2=pos->ptr_to_rec;
00214   }
00215   else
00216   {
00217     if (!(flag & HIGHUSED))
00218     {
00219       /* Change link of previous upper-list key and save */
00220       gpos2->ptr_to_rec=ptr_to_rec2;
00221       gpos2->next_key=pos;
00222       flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
00223     }
00224     gpos2=pos;
00225     ptr_to_rec2=pos->ptr_to_rec;
00226   }
00227       }
00228     }
00229     while ((pos=pos->next_key));
00230 
00231     if ((flag & (LOWFIND | HIGHFIND)) == (LOWFIND | HIGHFIND))
00232     {
00233       /*
00234         If both 'higher' and 'lower' list have at least one element, now
00235         there are two hash buckets instead of one.
00236       */
00237       keyinfo->hash_buckets++;
00238     }
00239 
00240     if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
00241     {
00242       gpos->ptr_to_rec=ptr_to_rec;
00243       gpos->next_key=0;
00244     }
00245     if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
00246     {
00247       gpos2->ptr_to_rec=ptr_to_rec2;
00248       gpos2->next_key=0;
00249     }
00250   }
00251   /* Check if we are at the empty position */
00252 
00253   pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record),
00254            share->blength, share->records + 1));
00255   if (pos == empty)
00256   {
00257     pos->ptr_to_rec=recpos;
00258     pos->next_key=0;
00259     keyinfo->hash_buckets++;
00260   }
00261   else
00262   {
00263     /* Check if more records in same hash-nr family */
00264     empty[0]=pos[0];
00265     gpos=hp_find_hash(&keyinfo->block,
00266           hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
00267             share->blength, share->records + 1));
00268     if (pos == gpos)
00269     {
00270       pos->ptr_to_rec=recpos;
00271       pos->next_key=empty;
00272     }
00273     else
00274     {
00275       keyinfo->hash_buckets++;
00276       pos->ptr_to_rec=recpos;
00277       pos->next_key=0;
00278       hp_movelink(pos, gpos, empty);
00279     }
00280 
00281     /* Check if duplicated keys */
00282     if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
00283   (!(keyinfo->flag & HA_NULL_PART_KEY) ||
00284    !hp_if_null_in_key(keyinfo, record)))
00285     {
00286       pos=empty;
00287       do
00288       {
00289   if (! hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec, 1))
00290   {
00291     return(errno=HA_ERR_FOUND_DUPP_KEY);
00292   }
00293       } while ((pos=pos->next_key));
00294     }
00295   }
00296   return(0);
00297 }
00298 
00299   /* Returns ptr to block, and allocates block if neaded */
00300 
00301 static HASH_INFO *hp_find_free_hash(HP_SHARE *info,
00302              HP_BLOCK *block, uint32_t records)
00303 {
00304   uint32_t block_pos;
00305   size_t length;
00306 
00307   if (records < block->last_allocated)
00308     return hp_find_hash(block,records);
00309   if (!(block_pos=(records % block->records_in_block)))
00310   {
00311     if (hp_get_new_block(block,&length))
00312       return(NULL);
00313     info->index_length+=length;
00314   }
00315   block->last_allocated=records+1;
00316   return((HASH_INFO*) ((unsigned char*) block->level_info[0].last_blocks+
00317            block_pos*block->recbuffer));
00318 }