Drizzled Public API Documentation

mi_create.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 /* Create a MyISAM table */
00017 
00018 #include "myisam_priv.h"
00019 #include <drizzled/internal/my_bit.h>
00020 #include <drizzled/internal/my_sys.h>
00021 
00022 #include <drizzled/util/test.h>
00023 #include <drizzled/charset.h>
00024 #include <drizzled/error.h>
00025 
00026 #include <cassert>
00027 #include <algorithm>
00028 
00029 using namespace std;
00030 using namespace drizzled;
00031 
00032 /*
00033   Next highest power of two
00034 
00035   SYNOPSIS
00036     my_round_up_to_next_power()
00037     v   Value to check
00038 
00039   RETURN
00040     Next or equal power of 2
00041     Note: 0 will return 0
00042 
00043   NOTES
00044     Algorithm by Sean Anderson, according to:
00045     http://graphics.stanford.edu/~seander/bithacks.html
00046     (Orignal code public domain)
00047 
00048     Comments shows how this works with 01100000000000000000000000001011
00049 */
00050 
00051 static inline uint32_t my_round_up_to_next_power(uint32_t v)
00052 {
00053   v--;      /* 01100000000000000000000000001010 */
00054   v|= v >> 1;   /* 01110000000000000000000000001111 */
00055   v|= v >> 2;   /* 01111100000000000000000000001111 */
00056   v|= v >> 4;   /* 01111111110000000000000000001111 */
00057   v|= v >> 8;   /* 01111111111111111100000000001111 */
00058   v|= v >> 16;    /* 01111111111111111111111111111111 */
00059   return v+1;   /* 10000000000000000000000000000000 */
00060 }
00061 
00062 /*
00063   Old options is used when recreating database, from myisamchk
00064 */
00065 
00066 int mi_create(const char *name,uint32_t keys,MI_KEYDEF *keydefs,
00067         uint32_t columns, MI_COLUMNDEF *recinfo,
00068         uint32_t uniques, MI_UNIQUEDEF *uniquedefs,
00069         MI_CREATE_INFO *ci,uint32_t flags)
00070 {
00071   register uint32_t i, j;
00072   int dfile= 0, file= 0;
00073   int errpos,save_errno, create_mode= O_RDWR | O_TRUNC;
00074   myf create_flag;
00075   uint32_t fields,length,max_key_length,packed,pointer,real_length_diff,
00076        key_length,info_length,key_segs,options,min_key_length_skip,
00077        base_pos,long_varchar_count,varchar_length,
00078        max_key_block_length,unique_key_parts,fulltext_keys,offset;
00079   uint32_t aligned_key_start, block_length;
00080   ulong reclength, real_reclength,min_pack_length;
00081   char filename[FN_REFLEN],linkname[FN_REFLEN], *linkname_ptr;
00082   ulong pack_reclength;
00083   uint64_t tot_length,max_rows, tmp;
00084   enum en_fieldtype type;
00085   MYISAM_SHARE share;
00086   MI_KEYDEF *keydef,tmp_keydef;
00087   MI_UNIQUEDEF *uniquedef;
00088   HA_KEYSEG *keyseg,tmp_keyseg;
00089   MI_COLUMNDEF *rec;
00090   ulong *rec_per_key_part;
00091   internal::my_off_t key_root[HA_MAX_POSSIBLE_KEY],key_del[MI_MAX_KEY_BLOCK_SIZE];
00092   MI_CREATE_INFO tmp_create_info;
00093 
00094   if (!ci)
00095   {
00096     memset(&tmp_create_info, 0, sizeof(tmp_create_info));
00097     ci=&tmp_create_info;
00098   }
00099 
00100   if (keys + uniques > MI_MAX_KEY || columns == 0)
00101   {
00102     return(errno=HA_WRONG_CREATE_OPTION);
00103   }
00104   errpos= 0;
00105   options= 0;
00106   memset(&share, 0, sizeof(share));
00107 
00108   if (flags & HA_DONT_TOUCH_DATA)
00109   {
00110     if (!(ci->old_options & HA_OPTION_TEMP_COMPRESS_RECORD))
00111       options=ci->old_options &
00112   (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD |
00113    HA_OPTION_READ_ONLY_DATA |
00114    HA_OPTION_TMP_TABLE );
00115     else
00116       options=ci->old_options &
00117   (HA_OPTION_TMP_TABLE );
00118   }
00119 
00120   if (ci->reloc_rows > ci->max_rows)
00121     ci->reloc_rows=ci->max_rows;    /* Check if wrong parameter */
00122 
00123   if (!(rec_per_key_part=
00124   (ulong*) malloc((keys + uniques)*MI_MAX_KEY_SEG*sizeof(long))))
00125     return(errno);
00126   memset(rec_per_key_part, 0, (keys + uniques)*MI_MAX_KEY_SEG*sizeof(long));
00127 
00128   /* Start by checking fields and field-types used */
00129 
00130   reclength=varchar_length=long_varchar_count=packed=
00131     min_pack_length=pack_reclength=0;
00132   for (rec=recinfo, fields=0 ;
00133        fields != columns ;
00134        rec++,fields++)
00135   {
00136     reclength+=rec->length;
00137     if ((type=(enum en_fieldtype) rec->type) != FIELD_NORMAL &&
00138   type != FIELD_CHECK)
00139     {
00140       packed++;
00141       if (type == FIELD_BLOB)
00142       {
00143   share.base.blobs++;
00144   if (pack_reclength != INT32_MAX)
00145   {
00146     if (rec->length == 4+portable_sizeof_char_ptr)
00147       pack_reclength= INT32_MAX;
00148     else
00149       pack_reclength+=(1 << ((rec->length-portable_sizeof_char_ptr)*8)); /* Max blob length */
00150   }
00151       }
00152       else if (type == FIELD_SKIP_PRESPACE ||
00153          type == FIELD_SKIP_ENDSPACE)
00154       {
00155   if (pack_reclength != INT32_MAX)
00156     pack_reclength+= rec->length > 255 ? 2 : 1;
00157   min_pack_length++;
00158       }
00159       else if (type == FIELD_VARCHAR)
00160       {
00161   varchar_length+= rec->length-1;          /* Used for min_pack_length */
00162   packed--;
00163   pack_reclength++;
00164         min_pack_length++;
00165         /* We must test for 257 as length includes pack-length */
00166         if (test(rec->length >= 257))
00167   {
00168     long_varchar_count++;
00169     pack_reclength+= 2;     /* May be packed on 3 bytes */
00170   }
00171       }
00172       else if (type != FIELD_SKIP_ZERO)
00173       {
00174   min_pack_length+=rec->length;
00175   packed--;       /* Not a pack record type */
00176       }
00177     }
00178     else          /* FIELD_NORMAL */
00179       min_pack_length+=rec->length;
00180   }
00181   if ((packed & 7) == 1)
00182   {       /* Bad packing, try to remove a zero-field */
00183     while (rec != recinfo)
00184     {
00185       rec--;
00186       if (rec->type == (int) FIELD_SKIP_ZERO && rec->length == 1)
00187       {
00188         /*
00189           NOTE1: here we change a field type FIELD_SKIP_ZERO ->
00190           FIELD_NORMAL
00191         */
00192   rec->type=(int) FIELD_NORMAL;
00193   packed--;
00194   min_pack_length++;
00195   break;
00196       }
00197     }
00198   }
00199 
00200   if (packed || (flags & HA_PACK_RECORD))
00201     options|=HA_OPTION_PACK_RECORD; /* Must use packed records */
00202   if (!(options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
00203     min_pack_length+= varchar_length;
00204   if (flags & HA_CREATE_TMP_TABLE)
00205   {
00206     options|= HA_OPTION_TMP_TABLE;
00207     create_mode|= O_EXCL;
00208   }
00209 
00210   packed=(packed+7)/8;
00211   if (pack_reclength != INT32_MAX)
00212     pack_reclength+= reclength+packed +
00213       test(test_all_bits(options,
00214                          uint32_t(HA_PACK_RECORD)));
00215   min_pack_length+=packed;
00216 
00217   if (!ci->data_file_length && ci->max_rows)
00218   {
00219     if (pack_reclength == INT32_MAX ||
00220              (~(uint64_t) 0)/ci->max_rows < (uint64_t) pack_reclength)
00221       ci->data_file_length= ~(uint64_t) 0;
00222     else
00223       ci->data_file_length=(uint64_t) ci->max_rows*pack_reclength;
00224   }
00225   else if (!ci->max_rows)
00226     ci->max_rows=(ha_rows) (ci->data_file_length/(min_pack_length +
00227            ((options & HA_OPTION_PACK_RECORD) ?
00228             3 : 0)));
00229 
00230   if (options & (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD))
00231     pointer=mi_get_pointer_length(ci->data_file_length, data_pointer_size);
00232   else
00233     pointer=mi_get_pointer_length(ci->max_rows, data_pointer_size);
00234   if (!(max_rows=(uint64_t) ci->max_rows))
00235     max_rows= ((((uint64_t) 1 << (pointer*8)) -1) / min_pack_length);
00236 
00237 
00238   real_reclength=reclength;
00239   if (!(options & (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD)))
00240   {
00241     if (reclength <= pointer)
00242       reclength=pointer+1;    /* reserve place for delete link */
00243   }
00244   else
00245     reclength+= long_varchar_count; /* We need space for varchar! */
00246 
00247   max_key_length=0; tot_length=0 ; key_segs=0;
00248   fulltext_keys=0;
00249   max_key_block_length=0;
00250   share.state.rec_per_key_part=rec_per_key_part;
00251   share.state.key_root=key_root;
00252   share.state.key_del=key_del;
00253   if (uniques)
00254   {
00255     max_key_block_length= myisam_block_size;
00256     max_key_length=   MI_UNIQUE_HASH_LENGTH + pointer;
00257   }
00258 
00259   for (i=0, keydef=keydefs ; i < keys ; i++ , keydef++)
00260   {
00261 
00262     share.state.key_root[i]= HA_OFFSET_ERROR;
00263     min_key_length_skip=length=real_length_diff=0;
00264     key_length=pointer;
00265     {
00266       /* Test if prefix compression */
00267       if (keydef->flag & HA_PACK_KEY)
00268       {
00269   /* Only use HA_PACK_KEY when first segment is a variable length key */
00270   if (!(keydef->seg[0].flag & (HA_SPACE_PACK | HA_BLOB_PART |
00271              HA_VAR_LENGTH_PART)))
00272   {
00273     /* pack relative to previous key */
00274     keydef->flag&= ~HA_PACK_KEY;
00275     keydef->flag|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
00276   }
00277   else
00278   {
00279     keydef->seg[0].flag|=HA_PACK_KEY; /* for easyer intern test */
00280     keydef->flag|=HA_VAR_LENGTH_KEY;
00281     options|=HA_OPTION_PACK_KEYS;   /* Using packed keys */
00282   }
00283       }
00284       if (keydef->flag & HA_BINARY_PACK_KEY)
00285   options|=HA_OPTION_PACK_KEYS;   /* Using packed keys */
00286 
00287       if (keydef->flag & HA_AUTO_KEY && ci->with_auto_increment)
00288   share.base.auto_key=i+1;
00289       for (j=0, keyseg=keydef->seg ; j < keydef->keysegs ; j++, keyseg++)
00290       {
00291   /* numbers are stored with high by first to make compression easier */
00292   switch (keyseg->type) {
00293   case HA_KEYTYPE_LONG_INT:
00294   case HA_KEYTYPE_DOUBLE:
00295   case HA_KEYTYPE_ULONG_INT:
00296   case HA_KEYTYPE_LONGLONG:
00297   case HA_KEYTYPE_ULONGLONG:
00298     keyseg->flag|= HA_SWAP_KEY;
00299           break;
00300         case HA_KEYTYPE_VARTEXT1:
00301         case HA_KEYTYPE_VARTEXT2:
00302         case HA_KEYTYPE_VARBINARY1:
00303         case HA_KEYTYPE_VARBINARY2:
00304           if (!(keyseg->flag & HA_BLOB_PART))
00305           {
00306             /* Make a flag that this is a VARCHAR */
00307             keyseg->flag|= HA_VAR_LENGTH_PART;
00308             /* Store in bit_start number of bytes used to pack the length */
00309             keyseg->bit_start= ((keyseg->type == HA_KEYTYPE_VARTEXT1 ||
00310                                  keyseg->type == HA_KEYTYPE_VARBINARY1) ?
00311                                 1 : 2);
00312           }
00313           break;
00314   default:
00315     break;
00316   }
00317   if (keyseg->flag & HA_SPACE_PACK)
00318   {
00319           assert(!(keyseg->flag & HA_VAR_LENGTH_PART));
00320     keydef->flag |= HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY;
00321     options|=HA_OPTION_PACK_KEYS;   /* Using packed keys */
00322     length++;       /* At least one length byte */
00323     min_key_length_skip+=keyseg->length;
00324     if (keyseg->length >= 255)
00325     {         /* prefix may be 3 bytes */
00326       min_key_length_skip+=2;
00327       length+=2;
00328     }
00329   }
00330   if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
00331   {
00332           assert(!test_all_bits(keyseg->flag,
00333                                     (HA_VAR_LENGTH_PART | HA_BLOB_PART)));
00334     keydef->flag|=HA_VAR_LENGTH_KEY;
00335     length++;       /* At least one length byte */
00336     options|=HA_OPTION_PACK_KEYS;   /* Using packed keys */
00337     min_key_length_skip+=keyseg->length;
00338     if (keyseg->length >= 255)
00339     {         /* prefix may be 3 bytes */
00340       min_key_length_skip+=2;
00341       length+=2;
00342     }
00343   }
00344   key_length+= keyseg->length;
00345   if (keyseg->null_bit)
00346   {
00347     key_length++;
00348     options|=HA_OPTION_PACK_KEYS;
00349     keyseg->flag|=HA_NULL_PART;
00350     keydef->flag|=HA_VAR_LENGTH_KEY | HA_NULL_PART_KEY;
00351   }
00352       }
00353     } /* if HA_FULLTEXT */
00354     key_segs+=keydef->keysegs;
00355     if (keydef->keysegs > MI_MAX_KEY_SEG)
00356     {
00357       errno=HA_WRONG_CREATE_OPTION;
00358       goto err;
00359     }
00360     /*
00361       key_segs may be 0 in the case when we only want to be able to
00362       add on row into the table. This can happen with some DISTINCT queries
00363       in MySQL
00364     */
00365     if ((keydef->flag & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME &&
00366   key_segs)
00367       share.state.rec_per_key_part[key_segs-1]=1L;
00368     length+=key_length;
00369     /* Get block length for key, if defined by user */
00370     block_length= (keydef->block_length ?
00371                    my_round_up_to_next_power(keydef->block_length) :
00372                    myisam_block_size);
00373     block_length= max(block_length, (uint32_t)MI_MIN_KEY_BLOCK_LENGTH);
00374     block_length= min(block_length, (uint32_t)MI_MAX_KEY_BLOCK_LENGTH);
00375 
00376     keydef->block_length= (uint16_t) MI_BLOCK_SIZE(length-real_length_diff,
00377                                                  pointer,MI_MAX_KEYPTR_SIZE,
00378                                                  block_length);
00379     if (keydef->block_length > MI_MAX_KEY_BLOCK_LENGTH ||
00380         length >= MI_MAX_KEY_BUFF)
00381     {
00382       errno=HA_WRONG_CREATE_OPTION;
00383       goto err;
00384     }
00385     set_if_bigger(max_key_block_length,(uint32_t)keydef->block_length);
00386     keydef->keylength= (uint16_t) key_length;
00387     keydef->minlength= (uint16_t) (length-min_key_length_skip);
00388     keydef->maxlength= (uint16_t) length;
00389 
00390     if (length > max_key_length)
00391       max_key_length= length;
00392     tot_length+= (max_rows/(ulong) (((uint) keydef->block_length-5)/
00393             (length*2)))*
00394       (ulong) keydef->block_length;
00395   }
00396   for (i=max_key_block_length/MI_MIN_KEY_BLOCK_LENGTH ; i-- ; )
00397     key_del[i]=HA_OFFSET_ERROR;
00398 
00399   unique_key_parts=0;
00400   offset=reclength-uniques*MI_UNIQUE_HASH_LENGTH;
00401   for (i=0, uniquedef=uniquedefs ; i < uniques ; i++ , uniquedef++)
00402   {
00403     uniquedef->key=keys+i;
00404     unique_key_parts+=uniquedef->keysegs;
00405     share.state.key_root[keys+i]= HA_OFFSET_ERROR;
00406     tot_length+= (max_rows/(ulong) (((uint) myisam_block_size-5)/
00407                          ((MI_UNIQUE_HASH_LENGTH + pointer)*2)))*
00408                          (ulong) myisam_block_size;
00409   }
00410   keys+=uniques;        /* Each unique has 1 key */
00411   key_segs+=uniques;        /* Each unique has 1 key seg */
00412 
00413   base_pos=(MI_STATE_INFO_SIZE + keys * MI_STATE_KEY_SIZE +
00414       max_key_block_length/MI_MIN_KEY_BLOCK_LENGTH*
00415       MI_STATE_KEYBLOCK_SIZE+
00416       key_segs*MI_STATE_KEYSEG_SIZE);
00417   info_length=base_pos+(uint) (MI_BASE_INFO_SIZE+
00418              keys * MI_KEYDEF_SIZE+
00419              uniques * MI_UNIQUEDEF_SIZE +
00420              (key_segs + unique_key_parts)*HA_KEYSEG_SIZE+
00421              columns*MI_COLUMNDEF_SIZE);
00422   /* There are only 16 bits for the total header length. */
00423   if (info_length > 65535)
00424   {
00425     my_printf_error(EE_OK, "MyISAM table '%s' has too many columns and/or "
00426                     "indexes and/or unique constraints.",
00427                     MYF(0), name + internal::dirname_length(name));
00428     errno= HA_WRONG_CREATE_OPTION;
00429     goto err;
00430   }
00431 
00432   memmove(share.state.header.file_version,myisam_file_magic,4);
00433   ci->old_options=options| (ci->old_options & HA_OPTION_TEMP_COMPRESS_RECORD ?
00434       HA_OPTION_COMPRESS_RECORD |
00435       HA_OPTION_TEMP_COMPRESS_RECORD: 0);
00436   mi_int2store(share.state.header.options,ci->old_options);
00437   mi_int2store(share.state.header.header_length,info_length);
00438   mi_int2store(share.state.header.state_info_length,MI_STATE_INFO_SIZE);
00439   mi_int2store(share.state.header.base_info_length,MI_BASE_INFO_SIZE);
00440   mi_int2store(share.state.header.base_pos,base_pos);
00441   share.state.header.language= (ci->language ?
00442         ci->language : default_charset_info->number);
00443   share.state.header.max_block_size_index= max_key_block_length/MI_MIN_KEY_BLOCK_LENGTH;
00444 
00445   share.state.dellink = HA_OFFSET_ERROR;
00446   share.state.process=  (ulong) getpid();
00447   share.state.unique= (ulong) 0;
00448   share.state.update_count=(ulong) 0;
00449   share.state.version=  (ulong) time((time_t*) 0);
00450   share.state.sortkey=  UINT16_MAX;
00451   share.state.auto_increment=ci->auto_increment;
00452   share.options=options;
00453   share.base.rec_reflength=pointer;
00454   /* Get estimate for index file length (this may be wrong for FT keys) */
00455   tmp= (tot_length + max_key_block_length * keys *
00456   MI_INDEX_BLOCK_MARGIN) / MI_MIN_KEY_BLOCK_LENGTH;
00457   /*
00458     use maximum of key_file_length we calculated and key_file_length value we
00459     got from MYI file header (see also myisampack.c:save_state)
00460   */
00461   share.base.key_reflength=
00462     mi_get_pointer_length(max(ci->key_file_length,tmp),3);
00463   share.base.keys= share.state.header.keys= keys;
00464   share.state.header.uniques= uniques;
00465   share.state.header.fulltext_keys= fulltext_keys;
00466   mi_int2store(share.state.header.key_parts,key_segs);
00467   mi_int2store(share.state.header.unique_key_parts,unique_key_parts);
00468 
00469   mi_set_all_keys_active(share.state.key_map, keys);
00470   aligned_key_start= my_round_up_to_next_power(max_key_block_length ?
00471                                                max_key_block_length :
00472                                                myisam_block_size);
00473 
00474   share.base.keystart= share.state.state.key_file_length=
00475     MY_ALIGN(info_length, aligned_key_start);
00476   share.base.max_key_block_length=max_key_block_length;
00477   share.base.max_key_length=ALIGN_SIZE(max_key_length+4);
00478   share.base.records=ci->max_rows;
00479   share.base.reloc=  ci->reloc_rows;
00480   share.base.reclength=real_reclength;
00481   share.base.pack_reclength=reclength;
00482   share.base.max_pack_length=pack_reclength;
00483   share.base.min_pack_length=min_pack_length;
00484   share.base.pack_bits=packed;
00485   share.base.fields=fields;
00486   share.base.pack_fields=packed;
00487 
00488   /* max_data_file_length and max_key_file_length are recalculated on open */
00489   if (options & HA_OPTION_TMP_TABLE)
00490     share.base.max_data_file_length=(internal::my_off_t) ci->data_file_length;
00491 
00492   share.base.min_block_length=
00493     (share.base.pack_reclength+3 < MI_EXTEND_BLOCK_LENGTH &&
00494      ! share.base.blobs) ?
00495     max(share.base.pack_reclength,(ulong)MI_MIN_BLOCK_LENGTH) :
00496     MI_EXTEND_BLOCK_LENGTH;
00497   if (! (flags & HA_DONT_TOUCH_DATA))
00498     share.state.create_time= (long) time((time_t*) 0);
00499 
00500   THR_LOCK_myisam.lock();
00501 
00502   /*
00503     NOTE: For test_if_reopen() we need a real path name. Hence we need
00504     MY_RETURN_REAL_PATH for every internal::fn_format(filename, ...).
00505   */
00506   if (ci->index_file_name)
00507   {
00508     char *iext= strrchr((char *)ci->index_file_name, '.');
00509     int have_iext= iext && !strcmp(iext, MI_NAME_IEXT);
00510     if (options & HA_OPTION_TMP_TABLE)
00511     {
00512       char *path;
00513       /* chop off the table name, tempory tables use generated name */
00514       if ((path= strrchr((char *)ci->index_file_name, FN_LIBCHAR)))
00515         *path= '\0';
00516       internal::fn_format(filename, name, ci->index_file_name, MI_NAME_IEXT,
00517                 MY_REPLACE_DIR | MY_UNPACK_FILENAME |
00518                 MY_RETURN_REAL_PATH | MY_APPEND_EXT);
00519     }
00520     else
00521     {
00522       internal::fn_format(filename, ci->index_file_name, "", MI_NAME_IEXT,
00523                 MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
00524                 (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
00525     }
00526     internal::fn_format(linkname, name, "", MI_NAME_IEXT,
00527               MY_UNPACK_FILENAME|MY_APPEND_EXT);
00528     linkname_ptr=linkname;
00529     /*
00530       Don't create the table if the link or file exists to ensure that one
00531       doesn't accidently destroy another table.
00532     */
00533     create_flag=0;
00534   }
00535   else
00536   {
00537     char *iext= strrchr((char *)name, '.');
00538     int have_iext= iext && !strcmp(iext, MI_NAME_IEXT);
00539     internal::fn_format(filename, name, "", MI_NAME_IEXT,
00540               MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
00541               (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
00542     linkname_ptr=0;
00543     /* Replace the current file */
00544     create_flag=(flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
00545   }
00546 
00547   /*
00548     If a MRG_MyISAM table is in use, the mapped MyISAM tables are open,
00549     but no entry is made in the table cache for them.
00550     A TRUNCATE command checks for the table in the cache only and could
00551     be fooled to believe, the table is not open.
00552     Pull the emergency brake in this situation. (Bug #8306)
00553 
00554     NOTE: The filename is compared against unique_file_name of every
00555     open table. Hence we need a real path here.
00556   */
00557   if (test_if_reopen(filename))
00558   {
00559     my_printf_error(EE_OK, "MyISAM table '%s' is in use "
00560                     "(most likely by a MERGE table). Try FLUSH TABLES.",
00561                     MYF(0), name + internal::dirname_length(name));
00562     errno= HA_ERR_TABLE_EXIST;
00563     goto err;
00564   }
00565 
00566   if ((file= internal::my_create_with_symlink(linkname_ptr,
00567                                               filename,
00568                                               0,
00569                                               create_mode,
00570                       MYF(MY_WME | create_flag))) < 0)
00571     goto err;
00572   errpos=1;
00573 
00574   if (!(flags & HA_DONT_TOUCH_DATA))
00575   {
00576     {
00577       if (ci->data_file_name)
00578       {
00579         char *dext= strrchr((char *)ci->data_file_name, '.');
00580         int have_dext= dext && !strcmp(dext, MI_NAME_DEXT);
00581 
00582         if (options & HA_OPTION_TMP_TABLE)
00583         {
00584           char *path;
00585           /* chop off the table name, tempory tables use generated name */
00586           if ((path= strrchr((char *)ci->data_file_name, FN_LIBCHAR)))
00587             *path= '\0';
00588           internal::fn_format(filename, name, ci->data_file_name, MI_NAME_DEXT,
00589                     MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT);
00590         }
00591         else
00592         {
00593           internal::fn_format(filename, ci->data_file_name, "", MI_NAME_DEXT,
00594                     MY_UNPACK_FILENAME |
00595                     (have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT));
00596         }
00597 
00598   internal::fn_format(linkname, name, "",MI_NAME_DEXT,
00599             MY_UNPACK_FILENAME | MY_APPEND_EXT);
00600   linkname_ptr=linkname;
00601   create_flag=0;
00602       }
00603       else
00604       {
00605   internal::fn_format(filename,name,"", MI_NAME_DEXT,
00606             MY_UNPACK_FILENAME | MY_APPEND_EXT);
00607   linkname_ptr=0;
00608         create_flag=(flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
00609       }
00610       if ((dfile= internal::my_create_with_symlink(linkname_ptr,
00611                                                    filename, 0, create_mode,
00612                                                    MYF(MY_WME | create_flag))) < 0)
00613   goto err;
00614     }
00615     errpos=3;
00616   }
00617 
00618   if (mi_state_info_write(file, &share.state, 2) ||
00619       mi_base_info_write(file, &share.base))
00620     goto err;
00621 
00622   /* Write key and keyseg definitions */
00623   for (i=0 ; i < share.base.keys - uniques; i++)
00624   {
00625     uint32_t sp_segs= 0;
00626 
00627     if (mi_keydef_write(file, &keydefs[i]))
00628       goto err;
00629     for (j=0 ; j < keydefs[i].keysegs-sp_segs ; j++)
00630       if (mi_keyseg_write(file, &keydefs[i].seg[j]))
00631        goto err;
00632   }
00633   /* Create extra keys for unique definitions */
00634   offset=reclength-uniques*MI_UNIQUE_HASH_LENGTH;
00635   memset(&tmp_keydef, 0, sizeof(tmp_keydef));
00636   memset(&tmp_keyseg, 0, sizeof(tmp_keyseg));
00637   for (i=0; i < uniques ; i++)
00638   {
00639     tmp_keydef.keysegs=1;
00640     tmp_keydef.flag=    HA_UNIQUE_CHECK;
00641     tmp_keydef.block_length=  (uint16_t)myisam_block_size;
00642     tmp_keydef.keylength= MI_UNIQUE_HASH_LENGTH + pointer;
00643     tmp_keydef.minlength=tmp_keydef.maxlength=tmp_keydef.keylength;
00644     tmp_keyseg.type=    MI_UNIQUE_HASH_TYPE;
00645     tmp_keyseg.length=    MI_UNIQUE_HASH_LENGTH;
00646     tmp_keyseg.start=   offset;
00647     offset+=      MI_UNIQUE_HASH_LENGTH;
00648     if (mi_keydef_write(file,&tmp_keydef) ||
00649   mi_keyseg_write(file,(&tmp_keyseg)))
00650       goto err;
00651   }
00652 
00653   /* Save unique definition */
00654   for (i=0 ; i < share.state.header.uniques ; i++)
00655   {
00656     HA_KEYSEG *keyseg_end;
00657     keyseg= uniquedefs[i].seg;
00658     if (mi_uniquedef_write(file, &uniquedefs[i]))
00659       goto err;
00660     for (keyseg= uniquedefs[i].seg, keyseg_end= keyseg+ uniquedefs[i].keysegs;
00661          keyseg < keyseg_end;
00662          keyseg++)
00663     {
00664       switch (keyseg->type) {
00665       case HA_KEYTYPE_VARTEXT1:
00666       case HA_KEYTYPE_VARTEXT2:
00667       case HA_KEYTYPE_VARBINARY1:
00668       case HA_KEYTYPE_VARBINARY2:
00669         if (!(keyseg->flag & HA_BLOB_PART))
00670         {
00671           keyseg->flag|= HA_VAR_LENGTH_PART;
00672           keyseg->bit_start= ((keyseg->type == HA_KEYTYPE_VARTEXT1 ||
00673                                keyseg->type == HA_KEYTYPE_VARBINARY1) ?
00674                               1 : 2);
00675         }
00676         break;
00677       default:
00678         break;
00679       }
00680       if (mi_keyseg_write(file, keyseg))
00681   goto err;
00682     }
00683   }
00684   for (i=0 ; i < share.base.fields ; i++)
00685     if (mi_recinfo_write(file, &recinfo[i]))
00686       goto err;
00687 
00688   /* Enlarge files */
00689   if (ftruncate(file, (off_t) share.base.keystart))
00690     goto err;
00691 
00692   if (! (flags & HA_DONT_TOUCH_DATA))
00693   {
00694 #ifdef USE_RELOC
00695     if (ftruncate(dfile,share.base.min_pack_length*ci->reloc_rows,))
00696       goto err;
00697 #endif
00698     errpos=2;
00699     if (internal::my_close(dfile,MYF(0)))
00700       goto err;
00701   }
00702   errpos=0;
00703   THR_LOCK_myisam.unlock();
00704   if (internal::my_close(file,MYF(0)))
00705     goto err;
00706   free((char*) rec_per_key_part);
00707   return(0);
00708 
00709 err:
00710   THR_LOCK_myisam.unlock();
00711   save_errno=errno;
00712   switch (errpos) {
00713   case 3:
00714     internal::my_close(dfile,MYF(0));
00715     /* fall through */
00716   case 2:
00717   if (! (flags & HA_DONT_TOUCH_DATA))
00718     internal::my_delete_with_symlink(internal::fn_format(filename,name,"",MI_NAME_DEXT,
00719                                      MY_UNPACK_FILENAME | MY_APPEND_EXT),
00720          MYF(0));
00721     /* fall through */
00722   case 1:
00723     internal::my_close(file,MYF(0));
00724     if (! (flags & HA_DONT_TOUCH_DATA))
00725       internal::my_delete_with_symlink(internal::fn_format(filename,name,"",MI_NAME_IEXT,
00726                                        MY_UNPACK_FILENAME | MY_APPEND_EXT),
00727            MYF(0));
00728   }
00729   free((char*) rec_per_key_part);
00730   return(errno=save_errno);   /* return the fatal errno */
00731 }
00732 
00733 
00734 uint32_t mi_get_pointer_length(uint64_t file_length, uint32_t def)
00735 {
00736   assert(def >= 2 && def <= 7);
00737   if (file_length)        /* If not default */
00738   {
00739 #ifdef NOT_YET_READY_FOR_8_BYTE_POINTERS
00740     if (file_length >= 1ULL << 56)
00741       def=8;
00742     else
00743 #endif
00744     if (file_length >= 1ULL << 48)
00745       def=7;
00746     else if (file_length >= 1ULL << 40)
00747       def=6;
00748     else if (file_length >= 1ULL << 32)
00749       def=5;
00750     else if (file_length >= 1ULL << 24)
00751       def=4;
00752     else if (file_length >= 1ULL << 16)
00753       def=3;
00754     else
00755       def=2;
00756   }
00757   return def;
00758 }