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