Drizzled Public API Documentation

mi_write.cc
00001 /* Copyright (C) 2000-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 row to a MyISAM table */
00017 
00018 #include "myisam_priv.h"
00019 
00020 #include <drizzled/internal/m_string.h>
00021 #include <drizzled/util/test.h>
00022 #include <drizzled/tree.h>
00023 
00024 using namespace drizzled;
00025 
00026 #define MAX_POINTER_LENGTH 8
00027 
00028   /* Functions declared in this file */
00029 
00030 static int w_search(MI_INFO *info,MI_KEYDEF *keyinfo,
00031         uint32_t comp_flag, unsigned char *key,
00032         uint32_t key_length, internal::my_off_t pos, unsigned char *father_buff,
00033         unsigned char *father_keypos, internal::my_off_t father_page,
00034         bool insert_last);
00035 static int _mi_balance_page(MI_INFO *info,MI_KEYDEF *keyinfo,unsigned char *key,
00036           unsigned char *curr_buff,unsigned char *father_buff,
00037           unsigned char *father_keypos,internal::my_off_t father_page);
00038 static unsigned char *_mi_find_last_pos(MI_KEYDEF *keyinfo, unsigned char *page,
00039         unsigned char *key, uint32_t *return_key_length,
00040         unsigned char **after_key);
00041 int _mi_ck_write_tree(register MI_INFO *info, uint32_t keynr,unsigned char *key,
00042           uint32_t key_length);
00043 int _mi_ck_write_btree(register MI_INFO *info, uint32_t keynr,unsigned char *key,
00044            uint32_t key_length);
00045 
00046   /* Write new record to database */
00047 
00048 int mi_write(MI_INFO *info, unsigned char *record)
00049 {
00050   MYISAM_SHARE *share=info->s;
00051   uint32_t i;
00052   int save_errno;
00053   internal::my_off_t filepos;
00054   unsigned char *buff;
00055   bool lock_tree= share->concurrent_insert;
00056 
00057   if (share->options & HA_OPTION_READ_ONLY_DATA)
00058   {
00059     return(errno=EACCES);
00060   }
00061   if (_mi_readinfo(info,F_WRLCK,1))
00062     return(errno);
00063   filepos= ((share->state.dellink != HA_OFFSET_ERROR &&
00064              !info->append_insert_at_end) ?
00065       share->state.dellink :
00066       info->state->data_file_length);
00067 
00068   if (share->base.reloc == (ha_rows) 1 &&
00069       share->base.records == (ha_rows) 1 &&
00070       info->state->records == (ha_rows) 1)
00071   {           /* System file */
00072     errno=HA_ERR_RECORD_FILE_FULL;
00073     goto err2;
00074   }
00075   if (info->state->key_file_length >= share->base.margin_key_file_length)
00076   {
00077     errno=HA_ERR_INDEX_FILE_FULL;
00078     goto err2;
00079   }
00080   if (_mi_mark_file_changed(info))
00081     goto err2;
00082 
00083   /* Calculate and check all unique constraints */
00084   for (i=0 ; i < share->state.header.uniques ; i++)
00085   {
00086     if (mi_check_unique(info,share->uniqueinfo+i,record,
00087          mi_unique_hash(share->uniqueinfo+i,record),
00088          HA_OFFSET_ERROR))
00089       goto err2;
00090   }
00091 
00092   /* Write all keys to indextree */
00093 
00094   buff=info->lastkey2;
00095   for (i=0 ; i < share->base.keys ; i++)
00096   {
00097     if (mi_is_key_active(share->state.key_map, i))
00098     {
00099       bool local_lock_tree= (lock_tree &&
00100                                 !(info->bulk_insert &&
00101                                   info->bulk_insert[i].is_inited()));
00102       if (local_lock_tree)
00103       {
00104   share->keyinfo[i].version++;
00105       }
00106       {
00107         if (share->keyinfo[i].ck_insert(info,i,buff,
00108       _mi_make_key(info,i,buff,record,filepos)))
00109         {
00110           goto err;
00111         }
00112       }
00113 
00114       /* The above changed info->lastkey2. Inform mi_rnext_same(). */
00115       info->update&= ~HA_STATE_RNEXT_SAME;
00116     }
00117   }
00118   if (share->calc_checksum)
00119     info->checksum=(*share->calc_checksum)(info,record);
00120   if (!(info->opt_flag & OPT_NO_ROWS))
00121   {
00122     if ((*share->write_record)(info,record))
00123       goto err;
00124     info->state->checksum+=info->checksum;
00125   }
00126   if (share->base.auto_key)
00127     set_if_bigger(info->s->state.auto_increment,
00128                   retrieve_auto_increment(info, record));
00129   info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_WRITTEN |
00130      HA_STATE_ROW_CHANGED);
00131   info->state->records++;
00132   info->lastpos=filepos;
00133   _mi_writeinfo(info, WRITEINFO_UPDATE_KEYFILE);
00134 
00135   /*
00136     Update status of the table. We need to do so after each row write
00137     for the log tables, as we want the new row to become visible to
00138     other threads as soon as possible. We don't lock mutex here
00139     (as it is required by pthread memory visibility rules) as (1) it's
00140     not critical to use outdated share->is_log_table value (2) locking
00141     mutex here for every write is too expensive.
00142   */
00143   if (share->is_log_table) // Log table do not exist in Drizzle
00144     assert(0);
00145 
00146   return(0);
00147 
00148 err:
00149   save_errno=errno;
00150   if (errno == HA_ERR_FOUND_DUPP_KEY || errno == HA_ERR_RECORD_FILE_FULL ||
00151       errno == HA_ERR_NULL_IN_SPATIAL || errno == HA_ERR_OUT_OF_MEM)
00152   {
00153     if (info->bulk_insert)
00154     {
00155       uint32_t j;
00156       for (j=0 ; j < share->base.keys ; j++)
00157         mi_flush_bulk_insert(info, j);
00158     }
00159     info->errkey= (int) i;
00160     while ( i-- > 0)
00161     {
00162       if (mi_is_key_active(share->state.key_map, i))
00163       {
00164   {
00165     uint32_t key_length=_mi_make_key(info,i,buff,record,filepos);
00166     if (_mi_ck_delete(info,i,buff,key_length))
00167     {
00168       break;
00169     }
00170   }
00171       }
00172     }
00173   }
00174   else
00175   {
00176     mi_print_error(info->s, HA_ERR_CRASHED);
00177     mi_mark_crashed(info);
00178   }
00179   info->update= (HA_STATE_CHANGED | HA_STATE_WRITTEN | HA_STATE_ROW_CHANGED);
00180   errno=save_errno;
00181 err2:
00182   save_errno=errno;
00183   _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
00184   return(errno=save_errno);
00185 } /* mi_write */
00186 
00187 
00188   /* Write one key to btree */
00189 
00190 int _mi_ck_write(MI_INFO *info, uint32_t keynr, unsigned char *key, uint32_t key_length)
00191 {
00192   if (info->bulk_insert && info->bulk_insert[keynr].is_inited())
00193   {
00194     return(_mi_ck_write_tree(info, keynr, key, key_length));
00195   }
00196   else
00197   {
00198     return(_mi_ck_write_btree(info, keynr, key, key_length));
00199   }
00200 } /* _mi_ck_write */
00201 
00202 
00203 /**********************************************************************
00204  *                Normal insert code                                  *
00205  **********************************************************************/
00206 
00207 int _mi_ck_write_btree(register MI_INFO *info, uint32_t keynr, unsigned char *key,
00208            uint32_t key_length)
00209 {
00210   uint32_t error;
00211   uint32_t comp_flag;
00212   MI_KEYDEF *keyinfo=info->s->keyinfo+keynr;
00213   internal::my_off_t  *root=&info->s->state.key_root[keynr];
00214 
00215   if (keyinfo->flag & HA_SORT_ALLOWS_SAME)
00216     comp_flag=SEARCH_BIGGER;      /* Put after same key */
00217   else if (keyinfo->flag & (HA_NOSAME))
00218   {
00219     comp_flag=SEARCH_FIND | SEARCH_UPDATE;  /* No duplicates */
00220     if (keyinfo->flag & HA_NULL_ARE_EQUAL)
00221       comp_flag|= SEARCH_NULL_ARE_EQUAL;
00222   }
00223   else
00224     comp_flag=SEARCH_SAME;      /* Keys in rec-pos order */
00225 
00226   error=_mi_ck_real_write_btree(info, keyinfo, key, key_length,
00227                                 root, comp_flag);
00228   return(error);
00229 } /* _mi_ck_write_btree */
00230 
00231 int _mi_ck_real_write_btree(MI_INFO *info, MI_KEYDEF *keyinfo,
00232     unsigned char *key, uint32_t key_length, internal::my_off_t *root, uint32_t comp_flag)
00233 {
00234   int error;
00235   /* key_length parameter is used only if comp_flag is SEARCH_FIND */
00236   if (*root == HA_OFFSET_ERROR ||
00237       (error=w_search(info, keyinfo, comp_flag, key, key_length,
00238           *root, (unsigned char *) 0, (unsigned char*) 0,
00239           (internal::my_off_t) 0, 1)) > 0)
00240     error=_mi_enlarge_root(info,keyinfo,key,root);
00241   return(error);
00242 } /* _mi_ck_real_write_btree */
00243 
00244 
00245   /* Make a new root with key as only pointer */
00246 
00247 int _mi_enlarge_root(MI_INFO *info, MI_KEYDEF *keyinfo, unsigned char *key,
00248                      internal::my_off_t *root)
00249 {
00250   uint32_t t_length,nod_flag;
00251   MI_KEY_PARAM s_temp;
00252   MYISAM_SHARE *share=info->s;
00253 
00254   nod_flag= (*root != HA_OFFSET_ERROR) ?  share->base.key_reflength : 0;
00255   _mi_kpointer(info,info->buff+2,*root); /* if nod */
00256   t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,(unsigned char*) 0,
00257         (unsigned char*) 0, (unsigned char*) 0, key,&s_temp);
00258   mi_putint(info->buff,t_length+2+nod_flag,nod_flag);
00259   (*keyinfo->store_key)(keyinfo,info->buff+2+nod_flag,&s_temp);
00260   info->buff_used=info->page_changed=1;   /* info->buff is used */
00261   if ((*root= _mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR ||
00262       _mi_write_keypage(info,keyinfo,*root,DFLT_INIT_HITS,info->buff))
00263     return(-1);
00264   return(0);
00265 } /* _mi_enlarge_root */
00266 
00267 
00268   /*
00269     Search after a position for a key and store it there
00270     Returns -1 = error
00271        0  = ok
00272        1  = key should be stored in higher tree
00273   */
00274 
00275 static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
00276         uint32_t comp_flag, unsigned char *key, uint32_t key_length, internal::my_off_t page,
00277         unsigned char *father_buff, unsigned char *father_keypos,
00278         internal::my_off_t father_page, bool insert_last)
00279 {
00280   int error,flag;
00281   uint32_t nod_flag, search_key_length;
00282   unsigned char *temp_buff,*keypos;
00283   unsigned char keybuff[MI_MAX_KEY_BUFF];
00284   bool was_last_key;
00285   internal::my_off_t next_page, dupp_key_pos;
00286 
00287   search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY;
00288   if (!(temp_buff= (unsigned char*) malloc(keyinfo->block_length+
00289                    MI_MAX_KEY_BUFF*2)))
00290     return(-1);
00291   if (!_mi_fetch_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff,0))
00292     goto err;
00293 
00294   flag=(*keyinfo->bin_search)(info,keyinfo,temp_buff,key,search_key_length,
00295             comp_flag, &keypos, keybuff, &was_last_key);
00296   nod_flag=mi_test_if_nod(temp_buff);
00297   if (flag == 0)
00298   {
00299     uint32_t tmp_key_length;
00300   /* get position to record with duplicated key */
00301     tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,keybuff);
00302     if (tmp_key_length)
00303       dupp_key_pos=_mi_dpos(info,0,keybuff+tmp_key_length);
00304     else
00305       dupp_key_pos= HA_OFFSET_ERROR;
00306 
00307     {
00308       info->dupp_key_pos= dupp_key_pos;
00309       free(temp_buff);
00310       errno=HA_ERR_FOUND_DUPP_KEY;
00311       return(-1);
00312     }
00313   }
00314   if (flag == MI_FOUND_WRONG_KEY)
00315     return(-1);
00316   if (!was_last_key)
00317     insert_last=0;
00318   next_page=_mi_kpos(nod_flag,keypos);
00319   if (next_page == HA_OFFSET_ERROR ||
00320       (error=w_search(info, keyinfo, comp_flag, key, key_length, next_page,
00321           temp_buff, keypos, page, insert_last)) >0)
00322   {
00323     error=_mi_insert(info,keyinfo,key,temp_buff,keypos,keybuff,father_buff,
00324          father_keypos,father_page, insert_last);
00325     if (_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff))
00326       goto err;
00327   }
00328   free(temp_buff);
00329   return(error);
00330 err:
00331   free(temp_buff);
00332   return (-1);
00333 } /* w_search */
00334 
00335 
00336 /*
00337   Insert new key.
00338 
00339   SYNOPSIS
00340     _mi_insert()
00341     info                        Open table information.
00342     keyinfo                     Key definition information.
00343     key                         New key.
00344     anc_buff                    Key page (beginning).
00345     key_pos                     Position in key page where to insert.
00346     key_buff                    Copy of previous key.
00347     father_buff                 parent key page for balancing.
00348     father_key_pos              position in parent key page for balancing.
00349     father_page                 position of parent key page in file.
00350     insert_last                 If to append at end of page.
00351 
00352   DESCRIPTION
00353     Insert new key at right of key_pos.
00354 
00355   RETURN
00356     2           if key contains key to upper level.
00357     0           OK.
00358     < 0         Error.
00359 */
00360 
00361 int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo,
00362          unsigned char *key, unsigned char *anc_buff, unsigned char *key_pos, unsigned char *key_buff,
00363                unsigned char *father_buff, unsigned char *father_key_pos, internal::my_off_t father_page,
00364          bool insert_last)
00365 {
00366   uint32_t a_length,nod_flag;
00367   int t_length;
00368   unsigned char *endpos, *prev_key;
00369   MI_KEY_PARAM s_temp;
00370 
00371   nod_flag=mi_test_if_nod(anc_buff);
00372   a_length=mi_getint(anc_buff);
00373   endpos= anc_buff+ a_length;
00374   prev_key=(key_pos == anc_buff+2+nod_flag ? (unsigned char*) 0 : key_buff);
00375   t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
00376         (key_pos == endpos ? (unsigned char*) 0 : key_pos),
00377         prev_key, prev_key,
00378         key,&s_temp);
00379 
00380   if (t_length > 0)
00381   {
00382     if (t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH)
00383     {
00384       mi_print_error(info->s, HA_ERR_CRASHED);
00385       errno=HA_ERR_CRASHED;
00386       return(-1);
00387     }
00388     internal::bmove_upp((unsigned char*) endpos+t_length,(unsigned char*) endpos,(uint) (endpos-key_pos));
00389   }
00390   else
00391   {
00392     if (-t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH)
00393     {
00394       mi_print_error(info->s, HA_ERR_CRASHED);
00395       errno=HA_ERR_CRASHED;
00396       return(-1);
00397     }
00398     memmove(key_pos, key_pos - t_length, endpos - key_pos + t_length);
00399   }
00400   (*keyinfo->store_key)(keyinfo,key_pos,&s_temp);
00401   a_length+=t_length;
00402   mi_putint(anc_buff,a_length,nod_flag);
00403   if (a_length <= keyinfo->block_length)
00404   {
00405     return(0);        /* There is room on page */
00406   }
00407   /* Page is full */
00408   if (nod_flag)
00409     insert_last=0;
00410   if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)) &&
00411       father_buff && !insert_last)
00412     return(_mi_balance_page(info,keyinfo,key,anc_buff,father_buff,
00413          father_key_pos,father_page));
00414   return(_mi_split_page(info,keyinfo,key,anc_buff,key_buff, insert_last));
00415 } /* _mi_insert */
00416 
00417 
00418   /* split a full page in two and assign emerging item to key */
00419 
00420 int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo,
00421        unsigned char *key, unsigned char *buff, unsigned char *key_buff,
00422        bool insert_last_key)
00423 {
00424   uint32_t length,a_length,key_ref_length,t_length,nod_flag,key_length;
00425   unsigned char *key_pos,*pos, *after_key= NULL;
00426   internal::my_off_t new_pos;
00427   MI_KEY_PARAM s_temp;
00428 
00429   if (info->s->keyinfo+info->lastinx == keyinfo)
00430     info->page_changed=1;     /* Info->buff is used */
00431   info->buff_used=1;
00432   nod_flag=mi_test_if_nod(buff);
00433   key_ref_length=2+nod_flag;
00434   if (insert_last_key)
00435     key_pos=_mi_find_last_pos(keyinfo,buff,key_buff, &key_length, &after_key);
00436   else
00437     key_pos=_mi_find_half_pos(nod_flag,keyinfo,buff,key_buff, &key_length,
00438             &after_key);
00439   if (!key_pos)
00440     return(-1);
00441 
00442   length=(uint) (key_pos-buff);
00443   a_length=mi_getint(buff);
00444   mi_putint(buff,length,nod_flag);
00445 
00446   key_pos=after_key;
00447   if (nod_flag)
00448   {
00449     pos=key_pos-nod_flag;
00450     memcpy(info->buff + 2, pos, nod_flag);
00451   }
00452 
00453   /* Move middle item to key and pointer to new page */
00454   if ((new_pos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
00455     return(-1);
00456   _mi_kpointer(info,_mi_move_key(keyinfo,key,key_buff),new_pos);
00457 
00458   /* Store new page */
00459   if (!(*keyinfo->get_key)(keyinfo,nod_flag,&key_pos,key_buff))
00460     return(-1);
00461 
00462   t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,(unsigned char *) 0,
00463         (unsigned char*) 0, (unsigned char*) 0,
00464         key_buff, &s_temp);
00465   length=(uint) ((buff+a_length)-key_pos);
00466   memcpy(info->buff+key_ref_length+t_length, key_pos, length);
00467   (*keyinfo->store_key)(keyinfo,info->buff+key_ref_length,&s_temp);
00468   mi_putint(info->buff,length+t_length+key_ref_length,nod_flag);
00469 
00470   if (_mi_write_keypage(info,keyinfo,new_pos,DFLT_INIT_HITS,info->buff))
00471     return(-1);
00472   return(2);        /* Middle key up */
00473 } /* _mi_split_page */
00474 
00475 
00476   /*
00477     Calculate how to much to move to split a page in two
00478     Returns pointer to start of key.
00479     key will contain the key.
00480     return_key_length will contain the length of key
00481     after_key will contain the position to where the next key starts
00482   */
00483 
00484 unsigned char *_mi_find_half_pos(uint32_t nod_flag, MI_KEYDEF *keyinfo, unsigned char *page,
00485        unsigned char *key, uint32_t *return_key_length,
00486        unsigned char **after_key)
00487 {
00488   uint32_t keys,length,key_ref_length;
00489   unsigned char *end,*lastpos;
00490 
00491   key_ref_length=2+nod_flag;
00492   length=mi_getint(page)-key_ref_length;
00493   page+=key_ref_length;
00494   if (!(keyinfo->flag &
00495   (HA_PACK_KEY | HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY |
00496    HA_BINARY_PACK_KEY)))
00497   {
00498     key_ref_length=keyinfo->keylength+nod_flag;
00499     keys=length/(key_ref_length*2);
00500     *return_key_length=keyinfo->keylength;
00501     end=page+keys*key_ref_length;
00502     *after_key=end+key_ref_length;
00503     memcpy(key,end,key_ref_length);
00504     return(end);
00505   }
00506 
00507   end=page+length/2-key_ref_length;   /* This is aprox. half */
00508   *key='\0';
00509   do
00510   {
00511     lastpos=page;
00512     if (!(length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key)))
00513       return(0);
00514   } while (page < end);
00515   *return_key_length=length;
00516   *after_key=page;
00517   return(lastpos);
00518 } /* _mi_find_half_pos */
00519 
00520 
00521   /*
00522     Split buffer at last key
00523     Returns pointer to the start of the key before the last key
00524     key will contain the last key
00525   */
00526 
00527 static unsigned char *_mi_find_last_pos(MI_KEYDEF *keyinfo, unsigned char *page,
00528         unsigned char *key, uint32_t *return_key_length,
00529         unsigned char **after_key)
00530 {
00531   uint32_t keys;
00532   uint32_t length;
00533   uint32_t last_length= 0;
00534   uint32_t key_ref_length;
00535   unsigned char *end, *lastpos, *prevpos= NULL;
00536   unsigned char key_buff[MI_MAX_KEY_BUFF];
00537 
00538   key_ref_length=2;
00539   length=mi_getint(page)-key_ref_length;
00540   page+=key_ref_length;
00541   if (!(keyinfo->flag &
00542   (HA_PACK_KEY | HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY |
00543    HA_BINARY_PACK_KEY)))
00544   {
00545     keys=length/keyinfo->keylength-2;
00546     *return_key_length=length=keyinfo->keylength;
00547     end=page+keys*length;
00548     *after_key=end+length;
00549     memcpy(key,end,length);
00550     return(end);
00551   }
00552 
00553   end= page + length - key_ref_length;
00554   *key='\0';
00555   length=0;
00556   lastpos=page;
00557   while (page < end)
00558   {
00559     prevpos=lastpos; lastpos=page;
00560     last_length=length;
00561     memcpy(key, key_buff, length);    /* previous key */
00562     if (!(length=(*keyinfo->get_key)(keyinfo,0,&page,key_buff)))
00563     {
00564       mi_print_error(keyinfo->share, HA_ERR_CRASHED);
00565       errno=HA_ERR_CRASHED;
00566       return(0);
00567     }
00568   }
00569   *return_key_length=last_length;
00570   *after_key=lastpos;
00571   return(prevpos);
00572 } /* _mi_find_last_pos */
00573 
00574 
00575   /* Balance page with not packed keys with page on right/left */
00576   /* returns 0 if balance was done */
00577 
00578 static int _mi_balance_page(register MI_INFO *info, MI_KEYDEF *keyinfo,
00579           unsigned char *key, unsigned char *curr_buff, unsigned char *father_buff,
00580           unsigned char *father_key_pos, internal::my_off_t father_page)
00581 {
00582   bool right;
00583   uint32_t k_length,father_length,father_keylength,nod_flag,curr_keylength,
00584        right_length,left_length,new_right_length,new_left_length,extra_length,
00585        length,keys;
00586   unsigned char *pos,*buff,*extra_buff;
00587   internal::my_off_t next_page,new_pos;
00588   unsigned char tmp_part_key[MI_MAX_KEY_BUFF];
00589 
00590   k_length=keyinfo->keylength;
00591   father_length=mi_getint(father_buff);
00592   father_keylength=k_length+info->s->base.key_reflength;
00593   nod_flag=mi_test_if_nod(curr_buff);
00594   curr_keylength=k_length+nod_flag;
00595   info->page_changed=1;
00596 
00597   if ((father_key_pos != father_buff+father_length &&
00598        (info->state->records & 1)) ||
00599       father_key_pos == father_buff+2+info->s->base.key_reflength)
00600   {
00601     right=1;
00602     next_page= _mi_kpos(info->s->base.key_reflength,
00603       father_key_pos+father_keylength);
00604     buff=info->buff;
00605   }
00606   else
00607   {
00608     right=0;
00609     father_key_pos-=father_keylength;
00610     next_page= _mi_kpos(info->s->base.key_reflength,father_key_pos);
00611           /* Fix that curr_buff is to left */
00612     buff=curr_buff; curr_buff=info->buff;
00613   }         /* father_key_pos ptr to parting key */
00614 
00615   if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,info->buff,0))
00616     goto err;
00617 
00618   /* Test if there is room to share keys */
00619 
00620   left_length=mi_getint(curr_buff);
00621   right_length=mi_getint(buff);
00622   keys=(left_length+right_length-4-nod_flag*2)/curr_keylength;
00623 
00624   if ((right ? right_length : left_length) + curr_keylength <=
00625       keyinfo->block_length)
00626   {           /* Merge buffs */
00627     new_left_length=2+nod_flag+(keys/2)*curr_keylength;
00628     new_right_length=2+nod_flag+((keys+1)/2)*curr_keylength;
00629     mi_putint(curr_buff,new_left_length,nod_flag);
00630     mi_putint(buff,new_right_length,nod_flag);
00631 
00632     if (left_length < new_left_length)
00633     {           /* Move keys buff -> leaf */
00634       pos=curr_buff+left_length;
00635       memcpy(pos, father_key_pos, k_length);
00636       length= new_left_length - left_length - k_length;
00637       memcpy(pos+k_length, buff+2, length);
00638       pos=buff+2+length;
00639       memcpy(father_key_pos, pos, k_length);
00640       memmove(buff+2, pos+k_length, new_right_length);
00641     }
00642     else
00643     {           /* Move keys -> buff */
00644 
00645       internal::bmove_upp((unsigned char*) buff+new_right_length,(unsigned char*) buff+right_length,
00646     right_length-2);
00647       length=new_right_length-right_length-k_length;
00648       memcpy(buff+2+length,father_key_pos, k_length);
00649       pos=curr_buff+new_left_length;
00650       memcpy(father_key_pos, pos, k_length);
00651       memcpy(buff+2, pos+k_length, length);
00652     }
00653 
00654     if (_mi_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,info->buff) ||
00655   _mi_write_keypage(info,keyinfo,father_page,DFLT_INIT_HITS,father_buff))
00656       goto err;
00657     return(0);
00658   }
00659 
00660   /* curr_buff[] and buff[] are full, lets split and make new nod */
00661 
00662   extra_buff=info->buff+info->s->base.max_key_block_length;
00663   new_left_length=new_right_length=2+nod_flag+(keys+1)/3*curr_keylength;
00664   if (keys == 5)        /* Too few keys to balance */
00665     new_left_length-=curr_keylength;
00666   extra_length=nod_flag+left_length+right_length-
00667     new_left_length-new_right_length-curr_keylength;
00668   mi_putint(curr_buff,new_left_length,nod_flag);
00669   mi_putint(buff,new_right_length,nod_flag);
00670   mi_putint(extra_buff,extra_length+2,nod_flag);
00671 
00672   /* move first largest keys to new page  */
00673   pos=buff+right_length-extra_length;
00674   memcpy(extra_buff+2, pos, extra_length);
00675   /* Save new parting key */
00676   memcpy(tmp_part_key, pos-k_length,k_length);
00677   /* Make place for new keys */
00678   internal::bmove_upp((unsigned char*) buff+new_right_length,(unsigned char*) pos-k_length,
00679       right_length-extra_length-k_length-2);
00680   /* Copy keys from left page */
00681   pos= curr_buff+new_left_length;
00682   length= left_length - new_left_length - k_length;
00683   memcpy(buff+2, pos+k_length, length);
00684   /* Copy old parting key */
00685   memcpy(buff+2+length, father_key_pos, k_length);
00686 
00687   /* Move new parting keys up to caller */
00688   memcpy((right ? key : father_key_pos), pos, k_length);
00689   memcpy((right ? father_key_pos : key), tmp_part_key, k_length);
00690 
00691   if ((new_pos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
00692     goto err;
00693   _mi_kpointer(info,key+k_length,new_pos);
00694   if (_mi_write_keypage(info,keyinfo,(right ? new_pos : next_page),
00695       DFLT_INIT_HITS,info->buff) ||
00696       _mi_write_keypage(info,keyinfo,(right ? next_page : new_pos),
00697                         DFLT_INIT_HITS,extra_buff))
00698     goto err;
00699 
00700   return(1);        /* Middle key up */
00701 
00702 err:
00703   return(-1);
00704 } /* _mi_balance_page */
00705 
00706 /**********************************************************************
00707  *                Bulk insert code                                    *
00708  **********************************************************************/
00709 
00710 typedef struct {
00711   MI_INFO *info;
00712   uint32_t keynr;
00713 } bulk_insert_param;
00714 
00715 int _mi_ck_write_tree(register MI_INFO *info, uint32_t keynr, unsigned char *key,
00716           uint32_t key_length)
00717 {
00718   int error;
00719 
00720   error= info->bulk_insert[keynr].tree_insert(key,
00721          key_length + info->s->rec_reflength,
00722          info->bulk_insert[keynr].getCustomArg()) ? 0 : HA_ERR_OUT_OF_MEM ;
00723 
00724   return(error);
00725 } /* _mi_ck_write_tree */
00726 
00727 
00728 /* typeof(_mi_keys_compare)=qsort_cmp2 */
00729 
00730 static int keys_compare(bulk_insert_param *param, unsigned char *key1, unsigned char *key2)
00731 {
00732   uint32_t not_used[2];
00733   return ha_key_cmp(param->info->s->keyinfo[param->keynr].seg,
00734                     key1, key2, USE_WHOLE_KEY, SEARCH_SAME,
00735                     not_used);
00736 }
00737 
00738 
00739 static int keys_free(unsigned char *key, TREE_FREE mode, bulk_insert_param *param)
00740 {
00741   /*
00742     Probably I can use info->lastkey here, but I'm not sure,
00743     and to be safe I'd better use local lastkey.
00744   */
00745   unsigned char lastkey[MI_MAX_KEY_BUFF];
00746   uint32_t keylen;
00747   MI_KEYDEF *keyinfo;
00748 
00749   switch (mode) {
00750   case free_init:
00751     if (param->info->s->concurrent_insert)
00752     {
00753       param->info->s->keyinfo[param->keynr].version++;
00754     }
00755     return 0;
00756   case free_free:
00757     keyinfo=param->info->s->keyinfo+param->keynr;
00758     keylen=_mi_keylength(keyinfo, key);
00759     memcpy(lastkey, key, keylen);
00760     return _mi_ck_write_btree(param->info,param->keynr,lastkey,
00761             keylen - param->info->s->rec_reflength);
00762   case free_end:
00763     return 0;
00764   }
00765   return -1;
00766 }
00767 
00768 
00769 int mi_init_bulk_insert(MI_INFO *info, uint32_t cache_size, ha_rows rows)
00770 {
00771   MYISAM_SHARE *share=info->s;
00772   MI_KEYDEF *key=share->keyinfo;
00773   bulk_insert_param *params;
00774   uint32_t i, num_keys, total_keylength;
00775   uint64_t key_map;
00776 
00777   assert(!info->bulk_insert &&
00778         (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT));
00779 
00780   mi_clear_all_keys_active(key_map);
00781   for (i=total_keylength=num_keys=0 ; i < share->base.keys ; i++)
00782   {
00783     if (! (key[i].flag & HA_NOSAME) && (share->base.auto_key != i + 1) &&
00784         mi_is_key_active(share->state.key_map, i))
00785     {
00786       num_keys++;
00787       mi_set_key_active(key_map, i);
00788       total_keylength+=key[i].maxlength+TREE_ELEMENT_EXTRA_SIZE;
00789     }
00790   }
00791 
00792   if (num_keys==0 ||
00793       num_keys * MI_MIN_SIZE_BULK_INSERT_TREE > cache_size)
00794     return(0);
00795 
00796   if (rows && rows*total_keylength < cache_size)
00797     cache_size= (uint32_t)rows;
00798   else
00799     cache_size/=total_keylength*16;
00800 
00801   info->bulk_insert=(Tree *) malloc((sizeof(Tree)*share->base.keys+
00802            sizeof(bulk_insert_param)*num_keys));
00803 
00804   if (!info->bulk_insert)
00805     return(HA_ERR_OUT_OF_MEM);
00806 
00807   params=(bulk_insert_param *)(info->bulk_insert+share->base.keys);
00808   for (i=0 ; i < share->base.keys ; i++)
00809   {
00810     if (mi_is_key_active(key_map, i))
00811     {
00812       params->info=info;
00813       params->keynr=i;
00814       /* Only allocate a 16'th of the buffer at a time */
00815       info->bulk_insert[i].init_tree(cache_size * key[i].maxlength,
00816                 cache_size * key[i].maxlength, 0,
00817     (qsort_cmp2)keys_compare, false,
00818     (tree_element_free) keys_free, (void *)params++);
00819     }
00820     else
00821      info->bulk_insert[i].setRoot(0);
00822   }
00823 
00824   return(0);
00825 }
00826 
00827 void mi_flush_bulk_insert(MI_INFO *info, uint32_t inx)
00828 {
00829   if (info->bulk_insert)
00830   {
00831     if (info->bulk_insert[inx].is_inited())
00832       info->bulk_insert[inx].reset_tree();
00833   }
00834 }
00835 
00836 void mi_end_bulk_insert(MI_INFO *info)
00837 {
00838   if (info->bulk_insert)
00839   {
00840     uint32_t i;
00841     for (i=0 ; i < info->s->base.keys ; i++)
00842     {
00843       if (info->bulk_insert[i].is_inited())
00844       {
00845         info->bulk_insert[i].delete_tree();
00846       }
00847     }
00848     free((void *)info->bulk_insert);
00849     info->bulk_insert=0;
00850   }
00851 }