| 1 | /* |
| 2 | Copyright (c) 2000, 2011, Oracle and/or its affiliates |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | /* |
| 18 | open a isam-database |
| 19 | |
| 20 | Internal temporary tables |
| 21 | ------------------------- |
| 22 | Since only single instance of internal temporary table is required by |
| 23 | optimizer, such tables are not registered on myisam_open_list. In effect |
| 24 | it means (a) THR_LOCK_myisam is not held while such table is being created, |
| 25 | opened or closed; (b) no iteration through myisam_open_list while opening a |
| 26 | table. This optimization gives nice scalability benefit in concurrent |
| 27 | environment. MEMORY internal temporary tables are optimized similarly. |
| 28 | */ |
| 29 | |
| 30 | #include "fulltext.h" |
| 31 | #include "sp_defs.h" |
| 32 | #include "rt_index.h" |
| 33 | #include <m_ctype.h> |
| 34 | #include <mysql_version.h> |
| 35 | |
| 36 | #ifdef __WIN__ |
| 37 | #include <fcntl.h> |
| 38 | #endif |
| 39 | |
| 40 | static void setup_key_functions(MI_KEYDEF *keyinfo); |
| 41 | #define get_next_element(to,pos,size) { memcpy((char*) to,pos,(size_t) size); \ |
| 42 | pos+=size;} |
| 43 | |
| 44 | |
| 45 | #define disk_pos_assert(pos, end_pos) \ |
| 46 | if (pos > end_pos) \ |
| 47 | { \ |
| 48 | my_errno=HA_ERR_CRASHED; \ |
| 49 | goto err; \ |
| 50 | } |
| 51 | |
| 52 | |
| 53 | /****************************************************************************** |
| 54 | ** Return the shared struct if the table is already open. |
| 55 | ** In MySQL the server will handle version issues. |
| 56 | ******************************************************************************/ |
| 57 | |
| 58 | MI_INFO *test_if_reopen(char *filename) |
| 59 | { |
| 60 | LIST *pos; |
| 61 | |
| 62 | for (pos=myisam_open_list ; pos ; pos=pos->next) |
| 63 | { |
| 64 | MI_INFO *info=(MI_INFO*) pos->data; |
| 65 | MYISAM_SHARE *share=info->s; |
| 66 | DBUG_ASSERT(strcmp(share->unique_file_name,filename) || |
| 67 | share->last_version); |
| 68 | if (!strcmp(share->unique_file_name,filename) && share->last_version) |
| 69 | return info; |
| 70 | } |
| 71 | return 0; |
| 72 | } |
| 73 | |
| 74 | |
| 75 | /****************************************************************************** |
| 76 | open a MyISAM database. |
| 77 | See my_base.h for the handle_locking argument |
| 78 | if handle_locking and HA_OPEN_ABORT_IF_CRASHED then abort if the table |
| 79 | is marked crashed or if we are not using locking and the table doesn't |
| 80 | have an open count of 0. |
| 81 | ******************************************************************************/ |
| 82 | |
| 83 | MI_INFO *mi_open(const char *name, int mode, uint open_flags) |
| 84 | { |
| 85 | int lock_error,kfile,open_mode,save_errno,have_rtree=0, realpath_err; |
| 86 | uint i,j,len,errpos,head_length,base_pos,offset,info_length,keys, |
| 87 | key_parts,unique_key_parts,base_key_parts,fulltext_keys,uniques; |
| 88 | uint internal_table= open_flags & HA_OPEN_INTERNAL_TABLE; |
| 89 | char name_buff[FN_REFLEN], org_name[FN_REFLEN], index_name[FN_REFLEN], |
| 90 | data_name[FN_REFLEN]; |
| 91 | uchar *UNINIT_VAR(disk_cache), *disk_pos, *end_pos; |
| 92 | MI_INFO info,*UNINIT_VAR(m_info),*old_info= NULL; |
| 93 | MYISAM_SHARE share_buff,*share; |
| 94 | ulong *rec_per_key_part= 0; |
| 95 | my_off_t *key_root, *key_del; |
| 96 | ulonglong max_key_file_length, max_data_file_length; |
| 97 | DBUG_ENTER("mi_open" ); |
| 98 | |
| 99 | kfile= -1; |
| 100 | lock_error=1; |
| 101 | errpos=0; |
| 102 | head_length=sizeof(share_buff.state.header); |
| 103 | bzero((uchar*) &info,sizeof(info)); |
| 104 | |
| 105 | realpath_err= my_realpath(name_buff, |
| 106 | fn_format(org_name,name,"" ,MI_NAME_IEXT,4),MYF(0)); |
| 107 | if (realpath_err > 0) /* File not found, no point in looking further. */ |
| 108 | { |
| 109 | DBUG_RETURN(NULL); |
| 110 | } |
| 111 | |
| 112 | if (my_is_symlink(org_name) && |
| 113 | (realpath_err || mysys_test_invalid_symlink(name_buff))) |
| 114 | { |
| 115 | my_errno= HA_WRONG_CREATE_OPTION; |
| 116 | DBUG_RETURN (NULL); |
| 117 | } |
| 118 | |
| 119 | if (!internal_table) |
| 120 | { |
| 121 | mysql_mutex_lock(&THR_LOCK_myisam); |
| 122 | old_info= test_if_reopen(name_buff); |
| 123 | } |
| 124 | |
| 125 | if (!old_info) |
| 126 | { |
| 127 | share= &share_buff; |
| 128 | bzero((uchar*) &share_buff,sizeof(share_buff)); |
| 129 | share_buff.key_cache= multi_key_cache_search((uchar*) name_buff, |
| 130 | (uint)strlen(name_buff), |
| 131 | dflt_key_cache); |
| 132 | |
| 133 | DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_open" , |
| 134 | if (strstr(name, "crashed" )) |
| 135 | { |
| 136 | my_errno= HA_ERR_CRASHED; |
| 137 | goto err; |
| 138 | }); |
| 139 | |
| 140 | DEBUG_SYNC_C("mi_open_kfile" ); |
| 141 | if ((kfile= mysql_file_open(mi_key_file_kfile, name_buff, |
| 142 | (open_mode= O_RDWR) | O_SHARE | O_NOFOLLOW, |
| 143 | MYF(MY_NOSYMLINKS))) < 0) |
| 144 | { |
| 145 | if ((errno != EROFS && errno != EACCES) || |
| 146 | mode != O_RDONLY || |
| 147 | (kfile= mysql_file_open(mi_key_file_kfile, name_buff, |
| 148 | (open_mode= O_RDONLY) | O_SHARE| O_NOFOLLOW, |
| 149 | MYF(MY_NOSYMLINKS))) < 0) |
| 150 | goto err; |
| 151 | } |
| 152 | share->mode=open_mode; |
| 153 | errpos=1; |
| 154 | if (mysql_file_read(kfile, (uchar*)&share->state.header, head_length, |
| 155 | MYF(MY_NABP))) |
| 156 | { |
| 157 | my_errno= HA_ERR_NOT_A_TABLE; |
| 158 | goto err; |
| 159 | } |
| 160 | if (bcmp(share->state.header.file_version, myisam_file_magic, 4)) |
| 161 | { |
| 162 | DBUG_PRINT("error" ,("Wrong header in %s" ,name_buff)); |
| 163 | DBUG_DUMP("error_dump" , share->state.header.file_version, |
| 164 | (size_t) head_length); |
| 165 | my_errno=HA_ERR_NOT_A_TABLE; |
| 166 | goto err; |
| 167 | } |
| 168 | share->options= mi_uint2korr(share->state.header.options); |
| 169 | if (share->options & |
| 170 | ~(HA_OPTION_PACK_RECORD | HA_OPTION_PACK_KEYS | |
| 171 | HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA | |
| 172 | HA_OPTION_TEMP_COMPRESS_RECORD | HA_OPTION_CHECKSUM | |
| 173 | HA_OPTION_TMP_TABLE | HA_OPTION_DELAY_KEY_WRITE | |
| 174 | HA_OPTION_RELIES_ON_SQL_LAYER | HA_OPTION_NULL_FIELDS)) |
| 175 | { |
| 176 | DBUG_PRINT("error" ,("wrong options: 0x%lx" , share->options)); |
| 177 | my_errno=HA_ERR_OLD_FILE; |
| 178 | goto err; |
| 179 | } |
| 180 | if ((share->options & HA_OPTION_RELIES_ON_SQL_LAYER) && |
| 181 | ! (open_flags & HA_OPEN_FROM_SQL_LAYER)) |
| 182 | { |
| 183 | DBUG_PRINT("error" , ("table cannot be openned from non-sql layer" )); |
| 184 | my_errno= HA_ERR_UNSUPPORTED; |
| 185 | goto err; |
| 186 | } |
| 187 | /* Don't call realpath() if the name can't be a link */ |
| 188 | if (!strcmp(name_buff, org_name) || |
| 189 | my_readlink(index_name, org_name, MYF(0)) == -1) |
| 190 | (void) strmov(index_name, org_name); |
| 191 | *strrchr(org_name, '.')= '\0'; |
| 192 | (void) fn_format(data_name,org_name,"" ,MI_NAME_DEXT, |
| 193 | MY_APPEND_EXT|MY_UNPACK_FILENAME); |
| 194 | if (my_is_symlink(data_name)) |
| 195 | { |
| 196 | if (my_realpath(data_name, data_name, MYF(0))) |
| 197 | goto err; |
| 198 | if (mysys_test_invalid_symlink(data_name)) |
| 199 | { |
| 200 | my_errno= HA_WRONG_CREATE_OPTION; |
| 201 | goto err; |
| 202 | } |
| 203 | share->mode|= O_NOFOLLOW; /* all symlinks are resolved by realpath() */ |
| 204 | } |
| 205 | |
| 206 | info_length=mi_uint2korr(share->state.header.header_length); |
| 207 | base_pos=mi_uint2korr(share->state.header.base_pos); |
| 208 | if (!(disk_cache= (uchar*) my_alloca(info_length+128))) |
| 209 | { |
| 210 | my_errno=ENOMEM; |
| 211 | goto err; |
| 212 | } |
| 213 | end_pos=disk_cache+info_length; |
| 214 | errpos=2; |
| 215 | |
| 216 | mysql_file_seek(kfile, 0L, MY_SEEK_SET, MYF(0)); |
| 217 | if (!(open_flags & HA_OPEN_TMP_TABLE)) |
| 218 | { |
| 219 | if ((lock_error=my_lock(kfile,F_RDLCK,0L,F_TO_EOF, |
| 220 | MYF(open_flags & HA_OPEN_WAIT_IF_LOCKED ? |
| 221 | 0 : MY_SHORT_WAIT))) && |
| 222 | !(open_flags & HA_OPEN_IGNORE_IF_LOCKED)) |
| 223 | goto err; |
| 224 | } |
| 225 | errpos=3; |
| 226 | if (mysql_file_read(kfile, disk_cache, info_length, MYF(MY_NABP))) |
| 227 | { |
| 228 | my_errno=HA_ERR_CRASHED; |
| 229 | goto err; |
| 230 | } |
| 231 | len=mi_uint2korr(share->state.header.state_info_length); |
| 232 | keys= (uint) share->state.header.keys; |
| 233 | uniques= (uint) share->state.header.uniques; |
| 234 | fulltext_keys= (uint) share->state.header.fulltext_keys; |
| 235 | base_key_parts= key_parts= mi_uint2korr(share->state.header.key_parts); |
| 236 | unique_key_parts= mi_uint2korr(share->state.header.unique_key_parts); |
| 237 | if (len != MI_STATE_INFO_SIZE) |
| 238 | { |
| 239 | DBUG_PRINT("warning" , |
| 240 | ("saved_state_info_length: %d state_info_length: %d" , |
| 241 | len,MI_STATE_INFO_SIZE)); |
| 242 | } |
| 243 | share->state_diff_length=len-MI_STATE_INFO_SIZE; |
| 244 | |
| 245 | if (!mi_state_info_read(disk_cache, &share->state)) |
| 246 | goto err; |
| 247 | rec_per_key_part= share->state.rec_per_key_part; |
| 248 | key_root= share->state.key_root; |
| 249 | key_del= share->state.key_del; |
| 250 | |
| 251 | len= mi_uint2korr(share->state.header.base_info_length); |
| 252 | if (len != MI_BASE_INFO_SIZE) |
| 253 | { |
| 254 | DBUG_PRINT("warning" ,("saved_base_info_length: %d base_info_length: %d" , |
| 255 | len,MI_BASE_INFO_SIZE)); |
| 256 | } |
| 257 | disk_pos= mi_n_base_info_read(disk_cache + base_pos, &share->base); |
| 258 | share->state.state_length=base_pos; |
| 259 | |
| 260 | if (!(open_flags & HA_OPEN_FOR_REPAIR) && |
| 261 | ((share->state.changed & STATE_CRASHED) || |
| 262 | ((open_flags & HA_OPEN_ABORT_IF_CRASHED) && |
| 263 | (my_disable_locking && share->state.open_count)))) |
| 264 | { |
| 265 | DBUG_PRINT("error" ,("Table is marked as crashed. open_flags: %u " |
| 266 | "changed: %u open_count: %u !locking: %d" , |
| 267 | open_flags, share->state.changed, |
| 268 | share->state.open_count, my_disable_locking)); |
| 269 | my_errno=((share->state.changed & STATE_CRASHED_ON_REPAIR) ? |
| 270 | HA_ERR_CRASHED_ON_REPAIR : HA_ERR_CRASHED_ON_USAGE); |
| 271 | goto err; |
| 272 | } |
| 273 | |
| 274 | /* sanity check */ |
| 275 | if (share->base.keystart > 65535 || |
| 276 | share->base.rec_reflength > 8 || share->base.key_reflength > 7) |
| 277 | { |
| 278 | my_errno=HA_ERR_CRASHED; |
| 279 | goto err; |
| 280 | } |
| 281 | |
| 282 | key_parts+=fulltext_keys*FT_SEGS; |
| 283 | if (share->base.max_key_length > HA_MAX_KEY_BUFF || keys > MI_MAX_KEY || |
| 284 | key_parts > MI_MAX_KEY * HA_MAX_KEY_SEG) |
| 285 | { |
| 286 | DBUG_PRINT("error" ,("Wrong key info: Max_key_length: %d keys: %d key_parts: %d" , share->base.max_key_length, keys, key_parts)); |
| 287 | my_errno=HA_ERR_UNSUPPORTED; |
| 288 | goto err; |
| 289 | } |
| 290 | |
| 291 | /* Correct max_file_length based on length of sizeof(off_t) */ |
| 292 | max_data_file_length= |
| 293 | (share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ? |
| 294 | (((ulonglong) 1 << (share->base.rec_reflength*8))-1) : |
| 295 | (mi_safe_mul(share->base.pack_reclength, |
| 296 | (ulonglong) 1 << (share->base.rec_reflength*8))-1); |
| 297 | max_key_file_length= |
| 298 | mi_safe_mul(MI_MIN_KEY_BLOCK_LENGTH, |
| 299 | ((ulonglong) 1 << (share->base.key_reflength*8))-1); |
| 300 | #if SIZEOF_OFF_T == 4 |
| 301 | set_if_smaller(max_data_file_length, INT_MAX32); |
| 302 | set_if_smaller(max_key_file_length, INT_MAX32); |
| 303 | #endif |
| 304 | share->base.max_data_file_length=(my_off_t) max_data_file_length; |
| 305 | share->base.max_key_file_length=(my_off_t) max_key_file_length; |
| 306 | |
| 307 | if (share->options & HA_OPTION_COMPRESS_RECORD) |
| 308 | share->base.max_key_length+=2; /* For safety */ |
| 309 | |
| 310 | /* Add space for node pointer */ |
| 311 | share->base.max_key_length+= share->base.key_reflength; |
| 312 | |
| 313 | if (!my_multi_malloc(MY_WME, |
| 314 | &share,sizeof(*share), |
| 315 | &share->state.rec_per_key_part, |
| 316 | sizeof(long)*base_key_parts, |
| 317 | &share->keyinfo,keys*sizeof(MI_KEYDEF), |
| 318 | &share->uniqueinfo,uniques*sizeof(MI_UNIQUEDEF), |
| 319 | &share->keyparts, |
| 320 | (key_parts+unique_key_parts+keys+uniques) * |
| 321 | sizeof(HA_KEYSEG), |
| 322 | &share->rec, |
| 323 | (share->base.fields+1)*sizeof(MI_COLUMNDEF), |
| 324 | &share->blobs,sizeof(MI_BLOB)*share->base.blobs, |
| 325 | &share->unique_file_name,strlen(name_buff)+1, |
| 326 | &share->index_file_name,strlen(index_name)+1, |
| 327 | &share->data_file_name,strlen(data_name)+1, |
| 328 | &share->state.key_root,keys*sizeof(my_off_t), |
| 329 | &share->state.key_del, |
| 330 | (share->state.header.max_block_size_index*sizeof(my_off_t)), |
| 331 | &share->key_root_lock, sizeof(mysql_rwlock_t)*keys, |
| 332 | &share->mmap_lock, sizeof(mysql_rwlock_t), |
| 333 | NullS)) |
| 334 | goto err; |
| 335 | errpos=4; |
| 336 | *share=share_buff; |
| 337 | memcpy((char*) share->state.rec_per_key_part, |
| 338 | (char*) rec_per_key_part, sizeof(long)*base_key_parts); |
| 339 | memcpy((char*) share->state.key_root, |
| 340 | (char*) key_root, sizeof(my_off_t)*keys); |
| 341 | memcpy((char*) share->state.key_del, |
| 342 | (char*) key_del, (sizeof(my_off_t) * |
| 343 | share->state.header.max_block_size_index)); |
| 344 | strmov(share->unique_file_name, name_buff); |
| 345 | share->unique_name_length= (uint)strlen(name_buff); |
| 346 | strmov(share->index_file_name, index_name); |
| 347 | strmov(share->data_file_name, data_name); |
| 348 | |
| 349 | share->vreclength= share->base.reclength; |
| 350 | share->blocksize=MY_MIN(IO_SIZE,myisam_block_size); |
| 351 | { |
| 352 | HA_KEYSEG *pos=share->keyparts; |
| 353 | uint32 ftkey_nr= 1; |
| 354 | for (i=0 ; i < keys ; i++) |
| 355 | { |
| 356 | MI_KEYDEF *keyinfo= share->keyinfo + i; |
| 357 | keyinfo->share= share; |
| 358 | disk_pos=mi_keydef_read(disk_pos, keyinfo); |
| 359 | disk_pos_assert(disk_pos + keyinfo->keysegs * HA_KEYSEG_SIZE, end_pos); |
| 360 | if (keyinfo->key_alg == HA_KEY_ALG_RTREE) |
| 361 | have_rtree=1; |
| 362 | set_if_smaller(share->blocksize, keyinfo->block_length); |
| 363 | keyinfo->seg= pos; |
| 364 | for (j=0 ; j < keyinfo->keysegs; j++,pos++) |
| 365 | { |
| 366 | disk_pos=mi_keyseg_read(disk_pos, pos); |
| 367 | if (pos->flag & HA_BLOB_PART && |
| 368 | ! (share->options & (HA_OPTION_COMPRESS_RECORD | |
| 369 | HA_OPTION_PACK_RECORD))) |
| 370 | { |
| 371 | my_errno= HA_ERR_CRASHED; |
| 372 | goto err; |
| 373 | } |
| 374 | if (pos->type == HA_KEYTYPE_TEXT || |
| 375 | pos->type == HA_KEYTYPE_VARTEXT1 || |
| 376 | pos->type == HA_KEYTYPE_VARTEXT2) |
| 377 | { |
| 378 | if (!pos->language) |
| 379 | pos->charset=default_charset_info; |
| 380 | else if (!(pos->charset= get_charset(pos->language, MYF(MY_WME)))) |
| 381 | { |
| 382 | my_errno=HA_ERR_UNKNOWN_CHARSET; |
| 383 | goto err; |
| 384 | } |
| 385 | } |
| 386 | else if (pos->type == HA_KEYTYPE_BINARY) |
| 387 | pos->charset= &my_charset_bin; |
| 388 | } |
| 389 | if (keyinfo->flag & HA_SPATIAL) |
| 390 | { |
| 391 | #ifdef HAVE_SPATIAL |
| 392 | uint sp_segs= SPDIMS*2; |
| 393 | keyinfo->seg= pos - sp_segs; |
| 394 | DBUG_ASSERT(keyinfo->keysegs == sp_segs + 1); |
| 395 | keyinfo->keysegs= sp_segs; |
| 396 | #else |
| 397 | my_errno=HA_ERR_UNSUPPORTED; |
| 398 | goto err; |
| 399 | #endif |
| 400 | } |
| 401 | else if (keyinfo->flag & HA_FULLTEXT) |
| 402 | { |
| 403 | if (!fulltext_keys) |
| 404 | { /* 4.0 compatibility code, to be removed in 5.0 */ |
| 405 | keyinfo->seg= pos - FT_SEGS; |
| 406 | keyinfo->keysegs-= FT_SEGS; |
| 407 | } |
| 408 | else |
| 409 | { |
| 410 | uint k; |
| 411 | keyinfo->seg= pos; |
| 412 | for (k=0; k < FT_SEGS; k++) |
| 413 | { |
| 414 | *pos= ft_keysegs[k]; |
| 415 | pos[0].language= pos[-1].language; |
| 416 | if (!(pos[0].charset= pos[-1].charset)) |
| 417 | { |
| 418 | my_errno=HA_ERR_CRASHED; |
| 419 | goto err; |
| 420 | } |
| 421 | pos++; |
| 422 | } |
| 423 | } |
| 424 | if (!share->ft2_keyinfo.seg) |
| 425 | { |
| 426 | memcpy(& share->ft2_keyinfo, keyinfo, sizeof(MI_KEYDEF)); |
| 427 | share->ft2_keyinfo.keysegs=1; |
| 428 | share->ft2_keyinfo.flag=0; |
| 429 | share->ft2_keyinfo.keylength= |
| 430 | share->ft2_keyinfo.minlength= |
| 431 | share->ft2_keyinfo.maxlength=HA_FT_WLEN+share->base.rec_reflength; |
| 432 | share->ft2_keyinfo.seg=pos-1; |
| 433 | share->ft2_keyinfo.end=pos; |
| 434 | setup_key_functions(& share->ft2_keyinfo); |
| 435 | } |
| 436 | keyinfo->ftkey_nr= ftkey_nr++; |
| 437 | } |
| 438 | setup_key_functions(keyinfo); |
| 439 | keyinfo->end= pos; |
| 440 | pos->type=HA_KEYTYPE_END; /* End */ |
| 441 | pos->length=share->base.rec_reflength; |
| 442 | pos->null_bit=0; |
| 443 | pos->flag=0; /* For purify */ |
| 444 | pos++; |
| 445 | } |
| 446 | |
| 447 | for (i=0 ; i < uniques ; i++) |
| 448 | { |
| 449 | disk_pos=mi_uniquedef_read(disk_pos, &share->uniqueinfo[i]); |
| 450 | disk_pos_assert(disk_pos + share->uniqueinfo[i].keysegs * |
| 451 | HA_KEYSEG_SIZE, end_pos); |
| 452 | share->uniqueinfo[i].seg=pos; |
| 453 | for (j=0 ; j < share->uniqueinfo[i].keysegs; j++,pos++) |
| 454 | { |
| 455 | disk_pos=mi_keyseg_read(disk_pos, pos); |
| 456 | if (pos->type == HA_KEYTYPE_TEXT || |
| 457 | pos->type == HA_KEYTYPE_VARTEXT1 || |
| 458 | pos->type == HA_KEYTYPE_VARTEXT2) |
| 459 | { |
| 460 | if (!pos->language) |
| 461 | pos->charset=default_charset_info; |
| 462 | else if (!(pos->charset= get_charset(pos->language, MYF(MY_WME)))) |
| 463 | { |
| 464 | my_errno=HA_ERR_UNKNOWN_CHARSET; |
| 465 | goto err; |
| 466 | } |
| 467 | } |
| 468 | } |
| 469 | share->uniqueinfo[i].end=pos; |
| 470 | pos->type=HA_KEYTYPE_END; /* End */ |
| 471 | pos->null_bit=0; |
| 472 | pos->flag=0; |
| 473 | pos++; |
| 474 | } |
| 475 | share->ftkeys= ftkey_nr; |
| 476 | } |
| 477 | |
| 478 | disk_pos_assert(disk_pos + share->base.fields *MI_COLUMNDEF_SIZE, end_pos); |
| 479 | for (i=j=offset=0 ; i < share->base.fields ; i++) |
| 480 | { |
| 481 | disk_pos=mi_recinfo_read(disk_pos,&share->rec[i]); |
| 482 | share->rec[i].pack_type=0; |
| 483 | share->rec[i].huff_tree=0; |
| 484 | share->rec[i].offset=offset; |
| 485 | if (share->rec[i].type == FIELD_BLOB) |
| 486 | { |
| 487 | share->blobs[j].pack_length= |
| 488 | share->rec[i].length - portable_sizeof_char_ptr; |
| 489 | share->blobs[j].offset=offset; |
| 490 | j++; |
| 491 | } |
| 492 | /* This is to detect how to calculate checksums */ |
| 493 | if (share->rec[i].null_bit) |
| 494 | share->has_null_fields= 1; |
| 495 | if (share->rec[i].type == FIELD_VARCHAR) |
| 496 | share->has_varchar_fields= 1; |
| 497 | offset+=share->rec[i].length; |
| 498 | } |
| 499 | share->rec[i].type=(int) FIELD_LAST; /* End marker */ |
| 500 | if (offset > share->base.reclength) |
| 501 | { |
| 502 | /* purecov: begin inspected */ |
| 503 | my_errno= HA_ERR_CRASHED; |
| 504 | goto err; |
| 505 | /* purecov: end */ |
| 506 | } |
| 507 | |
| 508 | if (! lock_error) |
| 509 | { |
| 510 | (void) my_lock(kfile,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE)); |
| 511 | lock_error=1; /* Database unlocked */ |
| 512 | } |
| 513 | |
| 514 | if (mi_open_datafile(&info, share)) |
| 515 | goto err; |
| 516 | errpos=5; |
| 517 | |
| 518 | share->kfile=kfile; |
| 519 | share->this_process=(ulong) getpid(); |
| 520 | share->last_process= share->state.process; |
| 521 | share->base.key_parts=key_parts; |
| 522 | share->base.all_key_parts=key_parts+unique_key_parts; |
| 523 | if (!(share->last_version=share->state.version)) |
| 524 | share->last_version=1; /* Safety */ |
| 525 | share->rec_reflength=share->base.rec_reflength; /* May be changed */ |
| 526 | share->base.margin_key_file_length=(share->base.max_key_file_length - |
| 527 | (keys ? MI_INDEX_BLOCK_MARGIN * |
| 528 | share->blocksize * keys : 0)); |
| 529 | share->blocksize=MY_MIN(IO_SIZE,myisam_block_size); |
| 530 | share->data_file_type=STATIC_RECORD; |
| 531 | if (share->options & HA_OPTION_COMPRESS_RECORD) |
| 532 | { |
| 533 | share->data_file_type = COMPRESSED_RECORD; |
| 534 | share->options|= HA_OPTION_READ_ONLY_DATA; |
| 535 | info.s=share; |
| 536 | if (_mi_read_pack_info(&info, |
| 537 | (pbool) |
| 538 | MY_TEST(!(share->options & |
| 539 | (HA_OPTION_PACK_RECORD | |
| 540 | HA_OPTION_TEMP_COMPRESS_RECORD))))) |
| 541 | goto err; |
| 542 | } |
| 543 | else if (share->options & HA_OPTION_PACK_RECORD) |
| 544 | share->data_file_type = DYNAMIC_RECORD; |
| 545 | my_afree(disk_cache); |
| 546 | mi_setup_functions(share); |
| 547 | share->is_log_table= FALSE; |
| 548 | thr_lock_init(&share->lock); |
| 549 | mysql_mutex_init(mi_key_mutex_MYISAM_SHARE_intern_lock, |
| 550 | &share->intern_lock, MY_MUTEX_INIT_FAST); |
| 551 | for (i=0; i<keys; i++) |
| 552 | mysql_rwlock_init(mi_key_rwlock_MYISAM_SHARE_key_root_lock, |
| 553 | &share->key_root_lock[i]); |
| 554 | mysql_rwlock_init(mi_key_rwlock_MYISAM_SHARE_mmap_lock, &share->mmap_lock); |
| 555 | if (!thr_lock_inited) |
| 556 | { |
| 557 | /* Probably a single threaded program; Don't use concurrent inserts */ |
| 558 | myisam_concurrent_insert=0; |
| 559 | } |
| 560 | else if (myisam_concurrent_insert) |
| 561 | { |
| 562 | share->concurrent_insert= |
| 563 | ((share->options & (HA_OPTION_READ_ONLY_DATA | HA_OPTION_TMP_TABLE | |
| 564 | HA_OPTION_COMPRESS_RECORD | |
| 565 | HA_OPTION_TEMP_COMPRESS_RECORD)) || |
| 566 | (open_flags & HA_OPEN_TMP_TABLE) || |
| 567 | have_rtree) ? 0 : 1; |
| 568 | if (share->concurrent_insert) |
| 569 | { |
| 570 | share->lock.get_status=mi_get_status; |
| 571 | share->lock.copy_status=mi_copy_status; |
| 572 | share->lock.update_status=mi_update_status; |
| 573 | share->lock.restore_status= mi_restore_status; |
| 574 | share->lock.check_status=mi_check_status; |
| 575 | share->lock.fix_status= (void (*)(void *, void *)) mi_fix_status; |
| 576 | } |
| 577 | } |
| 578 | /* |
| 579 | Memory mapping can only be requested after initializing intern_lock. |
| 580 | */ |
| 581 | if (open_flags & HA_OPEN_MMAP) |
| 582 | { |
| 583 | info.s= share; |
| 584 | mi_extra(&info, HA_EXTRA_MMAP, 0); |
| 585 | } |
| 586 | } |
| 587 | else |
| 588 | { |
| 589 | share= old_info->s; |
| 590 | if (mode == O_RDWR && share->mode == O_RDONLY) |
| 591 | { |
| 592 | my_errno=EACCES; /* Can't open in write mode */ |
| 593 | goto err; |
| 594 | } |
| 595 | if (mi_open_datafile(&info, share)) |
| 596 | goto err; |
| 597 | errpos=5; |
| 598 | have_rtree= old_info->rtree_recursion_state != NULL; |
| 599 | } |
| 600 | |
| 601 | /* alloc and set up private structure parts */ |
| 602 | if (!my_multi_malloc(MY_WME, |
| 603 | &m_info,sizeof(MI_INFO), |
| 604 | &info.blobs,sizeof(MI_BLOB)*share->base.blobs, |
| 605 | &info.buff,(share->base.max_key_block_length*2+ |
| 606 | share->base.max_key_length), |
| 607 | &info.lastkey,share->base.max_key_length*3+1, |
| 608 | &info.first_mbr_key, share->base.max_key_length, |
| 609 | &info.filename,strlen(name)+1, |
| 610 | &info.rtree_recursion_state,have_rtree ? 1024 : 0, |
| 611 | NullS)) |
| 612 | goto err; |
| 613 | errpos=6; |
| 614 | |
| 615 | if (!have_rtree) |
| 616 | info.rtree_recursion_state= NULL; |
| 617 | |
| 618 | strmov(info.filename,name); |
| 619 | memcpy(info.blobs,share->blobs,sizeof(MI_BLOB)*share->base.blobs); |
| 620 | info.lastkey2=info.lastkey+share->base.max_key_length; |
| 621 | |
| 622 | info.s=share; |
| 623 | info.lastpos= HA_OFFSET_ERROR; |
| 624 | info.update= (short) (HA_STATE_NEXT_FOUND+HA_STATE_PREV_FOUND); |
| 625 | info.open_flag= open_flags; |
| 626 | info.opt_flag=READ_CHECK_USED; |
| 627 | info.this_unique= (ulong) info.dfile; /* Uniq number in process */ |
| 628 | if (share->data_file_type == COMPRESSED_RECORD) |
| 629 | info.this_unique= share->state.unique; |
| 630 | info.this_loop=0; /* Update counter */ |
| 631 | info.last_unique= share->state.unique; |
| 632 | info.last_loop= share->state.update_count; |
| 633 | if (mode == O_RDONLY) |
| 634 | share->options|=HA_OPTION_READ_ONLY_DATA; |
| 635 | info.lock_type=F_UNLCK; |
| 636 | info.quick_mode=0; |
| 637 | info.bulk_insert=0; |
| 638 | info.ft1_to_ft2=0; |
| 639 | info.errkey= -1; |
| 640 | info.page_changed=1; |
| 641 | mysql_mutex_lock(&share->intern_lock); |
| 642 | info.read_record=share->read_record; |
| 643 | share->reopen++; |
| 644 | share->write_flag=MYF(MY_NABP | MY_WAIT_IF_FULL); |
| 645 | if (share->options & HA_OPTION_READ_ONLY_DATA) |
| 646 | { |
| 647 | info.lock_type=F_RDLCK; |
| 648 | share->r_locks++; |
| 649 | share->tot_locks++; |
| 650 | } |
| 651 | if ((open_flags & HA_OPEN_TMP_TABLE) || |
| 652 | (share->options & HA_OPTION_TMP_TABLE)) |
| 653 | { |
| 654 | share->temporary=share->delay_key_write=1; |
| 655 | share->write_flag=MYF(MY_NABP); |
| 656 | share->w_locks++; /* We don't have to update status */ |
| 657 | share->tot_locks++; |
| 658 | info.lock_type=F_WRLCK; |
| 659 | } |
| 660 | if (((open_flags & HA_OPEN_DELAY_KEY_WRITE) || |
| 661 | (share->options & HA_OPTION_DELAY_KEY_WRITE)) && |
| 662 | myisam_delay_key_write) |
| 663 | share->delay_key_write=1; |
| 664 | info.state= &share->state.state; /* Change global values by default */ |
| 665 | mysql_mutex_unlock(&share->intern_lock); |
| 666 | |
| 667 | /* Allocate buffer for one record */ |
| 668 | |
| 669 | /* prerequisites: bzero(info) && info->s=share; are met. */ |
| 670 | if (!mi_alloc_rec_buff(&info, -1, &info.rec_buff)) |
| 671 | goto err; |
| 672 | bzero(info.rec_buff, mi_get_rec_buff_len(&info, info.rec_buff)); |
| 673 | |
| 674 | *m_info=info; |
| 675 | thr_lock_data_init(&share->lock,&m_info->lock,(void*) m_info); |
| 676 | |
| 677 | if (!internal_table) |
| 678 | { |
| 679 | m_info->open_list.data= (void*) m_info; |
| 680 | myisam_open_list= list_add(myisam_open_list, &m_info->open_list); |
| 681 | mysql_mutex_unlock(&THR_LOCK_myisam); |
| 682 | } |
| 683 | |
| 684 | bzero(info.buff, share->base.max_key_block_length * 2); |
| 685 | my_free(rec_per_key_part); |
| 686 | |
| 687 | if (myisam_log_file >= 0) |
| 688 | { |
| 689 | intern_filename(name_buff,share->index_file_name); |
| 690 | _myisam_log(MI_LOG_OPEN, m_info, (uchar*) name_buff, (uint)strlen(name_buff)); |
| 691 | } |
| 692 | DBUG_RETURN(m_info); |
| 693 | |
| 694 | err: |
| 695 | save_errno=my_errno ? my_errno : HA_ERR_END_OF_FILE; |
| 696 | if ((save_errno == HA_ERR_CRASHED) || |
| 697 | (save_errno == HA_ERR_CRASHED_ON_USAGE) || |
| 698 | (save_errno == HA_ERR_CRASHED_ON_REPAIR)) |
| 699 | mi_report_error(save_errno, name); |
| 700 | switch (errpos) { |
| 701 | case 6: |
| 702 | my_free(m_info); |
| 703 | /* fall through */ |
| 704 | case 5: |
| 705 | (void) mysql_file_close(info.dfile, MYF(0)); |
| 706 | if (old_info) |
| 707 | break; /* Don't remove open table */ |
| 708 | /* fall through */ |
| 709 | case 4: |
| 710 | my_free(share); |
| 711 | /* fall through */ |
| 712 | case 3: |
| 713 | if (! lock_error) |
| 714 | (void) my_lock(kfile, F_UNLCK, 0L, F_TO_EOF, MYF(MY_SEEK_NOT_DONE)); |
| 715 | my_free(rec_per_key_part); |
| 716 | /* fall through */ |
| 717 | case 2: |
| 718 | my_afree(disk_cache); |
| 719 | /* fall through */ |
| 720 | case 1: |
| 721 | (void) mysql_file_close(kfile, MYF(0)); |
| 722 | /* fall through */ |
| 723 | case 0: |
| 724 | default: |
| 725 | break; |
| 726 | } |
| 727 | if (!internal_table) |
| 728 | mysql_mutex_unlock(&THR_LOCK_myisam); |
| 729 | my_errno=save_errno; |
| 730 | DBUG_RETURN (NULL); |
| 731 | } /* mi_open */ |
| 732 | |
| 733 | |
| 734 | uchar *mi_alloc_rec_buff(MI_INFO *info, ulong length, uchar **buf) |
| 735 | { |
| 736 | uint ; |
| 737 | uint32 UNINIT_VAR(old_length); |
| 738 | |
| 739 | if (! *buf || length > (old_length=mi_get_rec_buff_len(info, *buf))) |
| 740 | { |
| 741 | uchar *newptr = *buf; |
| 742 | |
| 743 | /* to simplify initial init of info->rec_buf in mi_open and mi_extra */ |
| 744 | if (length == (ulong) -1) |
| 745 | { |
| 746 | if (info->s->options & HA_OPTION_COMPRESS_RECORD) |
| 747 | length= MY_MAX(info->s->base.pack_reclength, info->s->max_pack_length); |
| 748 | else |
| 749 | length= info->s->base.pack_reclength; |
| 750 | length= MY_MAX(length, info->s->base.max_key_length); |
| 751 | length= MY_MAX(length, info->s->vreclength); |
| 752 | /* Avoid unnecessary realloc */ |
| 753 | if (newptr && length == old_length) |
| 754 | return newptr; |
| 755 | } |
| 756 | |
| 757 | extra= ((info->s->options & HA_OPTION_PACK_RECORD) ? |
| 758 | ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+ |
| 759 | MI_REC_BUFF_OFFSET : 0); |
| 760 | if (extra && newptr) |
| 761 | newptr-= MI_REC_BUFF_OFFSET; |
| 762 | if (!(newptr=(uchar*) my_realloc((uchar*)newptr, length+extra+8, |
| 763 | MYF(MY_ALLOW_ZERO_PTR)))) |
| 764 | return NULL; |
| 765 | *((uint32 *) newptr)= (uint32) length; |
| 766 | *buf= newptr+(extra ? MI_REC_BUFF_OFFSET : 0); |
| 767 | } |
| 768 | return *buf; |
| 769 | } |
| 770 | |
| 771 | |
| 772 | ulonglong mi_safe_mul(ulonglong a, ulonglong b) |
| 773 | { |
| 774 | ulonglong max_val= ~ (ulonglong) 0; /* my_off_t is unsigned */ |
| 775 | |
| 776 | if (!a || max_val / a < b) |
| 777 | return max_val; |
| 778 | return a*b; |
| 779 | } |
| 780 | |
| 781 | /* Set up functions in structs */ |
| 782 | |
| 783 | void mi_setup_functions(register MYISAM_SHARE *share) |
| 784 | { |
| 785 | if (share->options & HA_OPTION_COMPRESS_RECORD) |
| 786 | { |
| 787 | share->read_record=_mi_read_pack_record; |
| 788 | share->read_rnd=_mi_read_rnd_pack_record; |
| 789 | if ((share->options & |
| 790 | (HA_OPTION_PACK_RECORD | HA_OPTION_NULL_FIELDS)) || |
| 791 | share->has_varchar_fields) |
| 792 | share->calc_checksum= mi_checksum; |
| 793 | else |
| 794 | share->calc_checksum= mi_static_checksum; |
| 795 | share->calc_check_checksum= share->calc_checksum; |
| 796 | if (!(share->options & HA_OPTION_TEMP_COMPRESS_RECORD)) |
| 797 | share->calc_checksum=0; /* No checksum */ |
| 798 | } |
| 799 | else if (share->options & HA_OPTION_PACK_RECORD) |
| 800 | { |
| 801 | share->read_record=_mi_read_dynamic_record; |
| 802 | share->read_rnd=_mi_read_rnd_dynamic_record; |
| 803 | share->delete_record=_mi_delete_dynamic_record; |
| 804 | share->compare_record=_mi_cmp_dynamic_record; |
| 805 | share->compare_unique=_mi_cmp_dynamic_unique; |
| 806 | share->calc_checksum= mi_checksum; |
| 807 | share->calc_check_checksum= share->calc_checksum; |
| 808 | |
| 809 | /* add bits used to pack data to pack_reclength for faster allocation */ |
| 810 | share->base.pack_reclength+= share->base.pack_bits; |
| 811 | if (share->base.blobs) |
| 812 | { |
| 813 | share->update_record=_mi_update_blob_record; |
| 814 | share->write_record=_mi_write_blob_record; |
| 815 | } |
| 816 | else |
| 817 | { |
| 818 | share->write_record=_mi_write_dynamic_record; |
| 819 | share->update_record=_mi_update_dynamic_record; |
| 820 | } |
| 821 | } |
| 822 | else |
| 823 | { |
| 824 | share->read_record=_mi_read_static_record; |
| 825 | share->read_rnd=_mi_read_rnd_static_record; |
| 826 | share->delete_record=_mi_delete_static_record; |
| 827 | share->compare_record=_mi_cmp_static_record; |
| 828 | share->update_record=_mi_update_static_record; |
| 829 | share->write_record=_mi_write_static_record; |
| 830 | share->compare_unique=_mi_cmp_static_unique; |
| 831 | if (share->options & HA_OPTION_NULL_FIELDS) |
| 832 | share->calc_checksum= mi_checksum; |
| 833 | else |
| 834 | share->calc_checksum= mi_static_checksum; |
| 835 | share->calc_check_checksum= share->calc_checksum; |
| 836 | } |
| 837 | share->file_read= mi_nommap_pread; |
| 838 | share->file_write= mi_nommap_pwrite; |
| 839 | if (!(share->options & HA_OPTION_CHECKSUM)) |
| 840 | share->calc_checksum=0; |
| 841 | return; |
| 842 | } |
| 843 | |
| 844 | |
| 845 | static void setup_key_functions(register MI_KEYDEF *keyinfo) |
| 846 | { |
| 847 | if (keyinfo->key_alg == HA_KEY_ALG_RTREE) |
| 848 | { |
| 849 | #ifdef HAVE_RTREE_KEYS |
| 850 | keyinfo->ck_insert = rtree_insert; |
| 851 | keyinfo->ck_delete = rtree_delete; |
| 852 | #else |
| 853 | DBUG_ASSERT(0); /* mi_open should check it never happens */ |
| 854 | #endif |
| 855 | } |
| 856 | else |
| 857 | { |
| 858 | keyinfo->ck_insert = _mi_ck_write; |
| 859 | keyinfo->ck_delete = _mi_ck_delete; |
| 860 | } |
| 861 | if (keyinfo->flag & HA_BINARY_PACK_KEY) |
| 862 | { /* Simple prefix compression */ |
| 863 | keyinfo->bin_search=_mi_seq_search; |
| 864 | keyinfo->get_key=_mi_get_binary_pack_key; |
| 865 | keyinfo->pack_key=_mi_calc_bin_pack_key_length; |
| 866 | keyinfo->store_key=_mi_store_bin_pack_key; |
| 867 | } |
| 868 | else if (keyinfo->flag & HA_VAR_LENGTH_KEY) |
| 869 | { |
| 870 | keyinfo->get_key= _mi_get_pack_key; |
| 871 | if (keyinfo->seg[0].flag & HA_PACK_KEY) |
| 872 | { /* Prefix compression */ |
| 873 | /* |
| 874 | _mi_prefix_search() compares end-space against ASCII blank (' '). |
| 875 | It cannot be used for character sets, that do not encode the |
| 876 | blank character like ASCII does. UCS2 is an example. All |
| 877 | character sets with a fixed width > 1 or a mimimum width > 1 |
| 878 | cannot represent blank like ASCII does. In these cases we have |
| 879 | to use _mi_seq_search() for the search. |
| 880 | */ |
| 881 | if (!keyinfo->seg->charset || use_strnxfrm(keyinfo->seg->charset) || |
| 882 | (keyinfo->seg->flag & HA_NULL_PART) || |
| 883 | (keyinfo->seg->charset->mbminlen > 1)) |
| 884 | keyinfo->bin_search=_mi_seq_search; |
| 885 | else |
| 886 | keyinfo->bin_search=_mi_prefix_search; |
| 887 | keyinfo->pack_key=_mi_calc_var_pack_key_length; |
| 888 | keyinfo->store_key=_mi_store_var_pack_key; |
| 889 | } |
| 890 | else |
| 891 | { |
| 892 | keyinfo->bin_search=_mi_seq_search; |
| 893 | keyinfo->pack_key=_mi_calc_var_key_length; /* Variable length key */ |
| 894 | keyinfo->store_key=_mi_store_static_key; |
| 895 | } |
| 896 | } |
| 897 | else |
| 898 | { |
| 899 | keyinfo->bin_search=_mi_bin_search; |
| 900 | keyinfo->get_key=_mi_get_static_key; |
| 901 | keyinfo->pack_key=_mi_calc_static_key_length; |
| 902 | keyinfo->store_key=_mi_store_static_key; |
| 903 | } |
| 904 | return; |
| 905 | } |
| 906 | |
| 907 | |
| 908 | /* |
| 909 | Function to save and store the header in the index file (.MYI) |
| 910 | */ |
| 911 | |
| 912 | uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite) |
| 913 | { |
| 914 | uchar buff[MI_STATE_INFO_SIZE + MI_STATE_EXTRA_SIZE]; |
| 915 | uchar *ptr=buff; |
| 916 | uint i, keys= (uint) state->header.keys, |
| 917 | key_blocks=state->header.max_block_size_index; |
| 918 | DBUG_ENTER("mi_state_info_write" ); |
| 919 | |
| 920 | memcpy(ptr, &state->header, sizeof(state->header)); |
| 921 | ptr+=sizeof(state->header); |
| 922 | |
| 923 | /* open_count must be first because of _mi_mark_file_changed ! */ |
| 924 | mi_int2store(ptr,state->open_count); ptr +=2; |
| 925 | *ptr++= (uchar)state->changed; *ptr++= state->sortkey; |
| 926 | mi_rowstore(ptr,state->state.records); ptr +=8; |
| 927 | mi_rowstore(ptr,state->state.del); ptr +=8; |
| 928 | mi_rowstore(ptr,state->split); ptr +=8; |
| 929 | mi_sizestore(ptr,state->dellink); ptr +=8; |
| 930 | mi_sizestore(ptr,state->state.key_file_length); ptr +=8; |
| 931 | mi_sizestore(ptr,state->state.data_file_length); ptr +=8; |
| 932 | mi_sizestore(ptr,state->state.empty); ptr +=8; |
| 933 | mi_sizestore(ptr,state->state.key_empty); ptr +=8; |
| 934 | mi_int8store(ptr,state->auto_increment); ptr +=8; |
| 935 | mi_int8store(ptr,(ulonglong) state->state.checksum);ptr +=8; |
| 936 | mi_int4store(ptr,state->process); ptr +=4; |
| 937 | mi_int4store(ptr,state->unique); ptr +=4; |
| 938 | mi_int4store(ptr,state->status); ptr +=4; |
| 939 | mi_int4store(ptr,state->update_count); ptr +=4; |
| 940 | |
| 941 | ptr+=state->state_diff_length; |
| 942 | |
| 943 | for (i=0; i < keys; i++) |
| 944 | { |
| 945 | mi_sizestore(ptr,state->key_root[i]); ptr +=8; |
| 946 | } |
| 947 | for (i=0; i < key_blocks; i++) |
| 948 | { |
| 949 | mi_sizestore(ptr,state->key_del[i]); ptr +=8; |
| 950 | } |
| 951 | if (pWrite & 2) /* From isamchk */ |
| 952 | { |
| 953 | uint key_parts= mi_uint2korr(state->header.key_parts); |
| 954 | mi_int4store(ptr,state->sec_index_changed); ptr +=4; |
| 955 | mi_int4store(ptr,state->sec_index_used); ptr +=4; |
| 956 | mi_int4store(ptr,state->version); ptr +=4; |
| 957 | mi_int8store(ptr,state->key_map); ptr +=8; |
| 958 | mi_int8store(ptr,(ulonglong) state->create_time); ptr +=8; |
| 959 | mi_int8store(ptr,(ulonglong) state->recover_time); ptr +=8; |
| 960 | mi_int8store(ptr,(ulonglong) state->check_time); ptr +=8; |
| 961 | mi_sizestore(ptr,state->rec_per_key_rows); ptr+=8; |
| 962 | for (i=0 ; i < key_parts ; i++) |
| 963 | { |
| 964 | mi_int4store(ptr,state->rec_per_key_part[i]); ptr+=4; |
| 965 | } |
| 966 | } |
| 967 | |
| 968 | if (pWrite & 1) |
| 969 | DBUG_RETURN(mysql_file_pwrite(file, buff, (size_t) (ptr-buff), 0L, |
| 970 | MYF(MY_NABP | MY_THREADSAFE)) != 0); |
| 971 | DBUG_RETURN(mysql_file_write(file, buff, (size_t) (ptr-buff), |
| 972 | MYF(MY_NABP)) != 0); |
| 973 | } |
| 974 | |
| 975 | |
| 976 | uchar *mi_state_info_read(uchar *ptr, MI_STATE_INFO *state) |
| 977 | { |
| 978 | uint i,keys,key_parts,key_blocks; |
| 979 | memcpy(&state->header, ptr, sizeof(state->header)); |
| 980 | ptr +=sizeof(state->header); |
| 981 | keys=(uint) state->header.keys; |
| 982 | key_parts=mi_uint2korr(state->header.key_parts); |
| 983 | key_blocks=state->header.max_block_size_index; |
| 984 | |
| 985 | state->open_count = mi_uint2korr(ptr); ptr +=2; |
| 986 | state->changed= *ptr++; |
| 987 | state->sortkey = (uint) *ptr++; |
| 988 | state->state.records= mi_rowkorr(ptr); ptr +=8; |
| 989 | state->state.del = mi_rowkorr(ptr); ptr +=8; |
| 990 | state->split = mi_rowkorr(ptr); ptr +=8; |
| 991 | state->dellink= mi_sizekorr(ptr); ptr +=8; |
| 992 | state->state.key_file_length = mi_sizekorr(ptr); ptr +=8; |
| 993 | state->state.data_file_length= mi_sizekorr(ptr); ptr +=8; |
| 994 | state->state.empty = mi_sizekorr(ptr); ptr +=8; |
| 995 | state->state.key_empty= mi_sizekorr(ptr); ptr +=8; |
| 996 | state->auto_increment=mi_uint8korr(ptr); ptr +=8; |
| 997 | state->state.checksum=(ha_checksum) mi_uint8korr(ptr); ptr +=8; |
| 998 | state->process= mi_uint4korr(ptr); ptr +=4; |
| 999 | state->unique = mi_uint4korr(ptr); ptr +=4; |
| 1000 | state->status = mi_uint4korr(ptr); ptr +=4; |
| 1001 | state->update_count=mi_uint4korr(ptr); ptr +=4; |
| 1002 | |
| 1003 | ptr+= state->state_diff_length; |
| 1004 | |
| 1005 | if (!state->rec_per_key_part) |
| 1006 | { |
| 1007 | if (!my_multi_malloc(MY_WME, |
| 1008 | &state->rec_per_key_part,sizeof(long)*key_parts, |
| 1009 | &state->key_root, keys*sizeof(my_off_t), |
| 1010 | &state->key_del, key_blocks*sizeof(my_off_t), |
| 1011 | NullS)) |
| 1012 | return(0); |
| 1013 | } |
| 1014 | |
| 1015 | for (i=0; i < keys; i++) |
| 1016 | { |
| 1017 | state->key_root[i]= mi_sizekorr(ptr); ptr +=8; |
| 1018 | } |
| 1019 | for (i=0; i < key_blocks; i++) |
| 1020 | { |
| 1021 | state->key_del[i] = mi_sizekorr(ptr); ptr +=8; |
| 1022 | } |
| 1023 | state->sec_index_changed = mi_uint4korr(ptr); ptr +=4; |
| 1024 | state->sec_index_used = mi_uint4korr(ptr); ptr +=4; |
| 1025 | state->version = mi_uint4korr(ptr); ptr +=4; |
| 1026 | state->key_map = mi_uint8korr(ptr); ptr +=8; |
| 1027 | state->create_time = (time_t) mi_sizekorr(ptr); ptr +=8; |
| 1028 | state->recover_time =(time_t) mi_sizekorr(ptr); ptr +=8; |
| 1029 | state->check_time = (time_t) mi_sizekorr(ptr); ptr +=8; |
| 1030 | state->rec_per_key_rows=mi_sizekorr(ptr); ptr +=8; |
| 1031 | for (i=0 ; i < key_parts ; i++) |
| 1032 | { |
| 1033 | state->rec_per_key_part[i]= mi_uint4korr(ptr); ptr+=4; |
| 1034 | } |
| 1035 | return ptr; |
| 1036 | } |
| 1037 | |
| 1038 | |
| 1039 | uint mi_state_info_read_dsk(File file, MI_STATE_INFO *state, my_bool pRead) |
| 1040 | { |
| 1041 | uchar buff[MI_STATE_INFO_SIZE + MI_STATE_EXTRA_SIZE]; |
| 1042 | |
| 1043 | if (!myisam_single_user) |
| 1044 | { |
| 1045 | if (pRead) |
| 1046 | { |
| 1047 | if (mysql_file_pread(file, buff, state->state_length, 0L, MYF(MY_NABP))) |
| 1048 | return 1; |
| 1049 | } |
| 1050 | else if (mysql_file_read(file, buff, state->state_length, MYF(MY_NABP))) |
| 1051 | return 1; |
| 1052 | mi_state_info_read(buff, state); |
| 1053 | } |
| 1054 | return 0; |
| 1055 | } |
| 1056 | |
| 1057 | |
| 1058 | /**************************************************************************** |
| 1059 | ** store and read of MI_BASE_INFO |
| 1060 | ****************************************************************************/ |
| 1061 | |
| 1062 | uint mi_base_info_write(File file, MI_BASE_INFO *base) |
| 1063 | { |
| 1064 | uchar buff[MI_BASE_INFO_SIZE], *ptr=buff; |
| 1065 | |
| 1066 | mi_sizestore(ptr,base->keystart); ptr +=8; |
| 1067 | mi_sizestore(ptr,base->max_data_file_length); ptr +=8; |
| 1068 | mi_sizestore(ptr,base->max_key_file_length); ptr +=8; |
| 1069 | mi_rowstore(ptr,base->records); ptr +=8; |
| 1070 | mi_rowstore(ptr,base->reloc); ptr +=8; |
| 1071 | mi_int4store(ptr,base->mean_row_length); ptr +=4; |
| 1072 | mi_int4store(ptr,base->reclength); ptr +=4; |
| 1073 | mi_int4store(ptr,base->pack_reclength); ptr +=4; |
| 1074 | mi_int4store(ptr,base->min_pack_length); ptr +=4; |
| 1075 | mi_int4store(ptr,base->max_pack_length); ptr +=4; |
| 1076 | mi_int4store(ptr,base->min_block_length); ptr +=4; |
| 1077 | mi_int4store(ptr,base->fields); ptr +=4; |
| 1078 | mi_int4store(ptr,base->pack_fields); ptr +=4; |
| 1079 | *ptr++=base->rec_reflength; |
| 1080 | *ptr++=base->key_reflength; |
| 1081 | *ptr++=base->keys; |
| 1082 | *ptr++=base->auto_key; |
| 1083 | mi_int2store(ptr,base->pack_bits); ptr +=2; |
| 1084 | mi_int2store(ptr,base->blobs); ptr +=2; |
| 1085 | mi_int2store(ptr,base->max_key_block_length); ptr +=2; |
| 1086 | mi_int2store(ptr,base->max_key_length); ptr +=2; |
| 1087 | mi_int2store(ptr,base->extra_alloc_bytes); ptr +=2; |
| 1088 | *ptr++= base->extra_alloc_procent; |
| 1089 | bzero(ptr,13); ptr +=13; /* extra */ |
| 1090 | return mysql_file_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0; |
| 1091 | } |
| 1092 | |
| 1093 | |
| 1094 | uchar *mi_n_base_info_read(uchar *ptr, MI_BASE_INFO *base) |
| 1095 | { |
| 1096 | base->keystart = mi_sizekorr(ptr); ptr +=8; |
| 1097 | base->max_data_file_length = mi_sizekorr(ptr); ptr +=8; |
| 1098 | base->max_key_file_length = mi_sizekorr(ptr); ptr +=8; |
| 1099 | base->records = (ha_rows) mi_sizekorr(ptr); ptr +=8; |
| 1100 | base->reloc = (ha_rows) mi_sizekorr(ptr); ptr +=8; |
| 1101 | base->mean_row_length = mi_uint4korr(ptr); ptr +=4; |
| 1102 | base->reclength = mi_uint4korr(ptr); ptr +=4; |
| 1103 | base->pack_reclength = mi_uint4korr(ptr); ptr +=4; |
| 1104 | base->min_pack_length = mi_uint4korr(ptr); ptr +=4; |
| 1105 | base->max_pack_length = mi_uint4korr(ptr); ptr +=4; |
| 1106 | base->min_block_length = mi_uint4korr(ptr); ptr +=4; |
| 1107 | base->fields = mi_uint4korr(ptr); ptr +=4; |
| 1108 | base->pack_fields = mi_uint4korr(ptr); ptr +=4; |
| 1109 | |
| 1110 | base->rec_reflength = *ptr++; |
| 1111 | base->key_reflength = *ptr++; |
| 1112 | base->keys= *ptr++; |
| 1113 | base->auto_key= *ptr++; |
| 1114 | base->pack_bits = mi_uint2korr(ptr); ptr +=2; |
| 1115 | base->blobs = mi_uint2korr(ptr); ptr +=2; |
| 1116 | base->max_key_block_length= mi_uint2korr(ptr); ptr +=2; |
| 1117 | base->max_key_length = mi_uint2korr(ptr); ptr +=2; |
| 1118 | base->extra_alloc_bytes = mi_uint2korr(ptr); ptr +=2; |
| 1119 | base->extra_alloc_procent = *ptr++; |
| 1120 | |
| 1121 | ptr+=13; |
| 1122 | return ptr; |
| 1123 | } |
| 1124 | |
| 1125 | /*-------------------------------------------------------------------------- |
| 1126 | mi_keydef |
| 1127 | ---------------------------------------------------------------------------*/ |
| 1128 | |
| 1129 | uint mi_keydef_write(File file, MI_KEYDEF *keydef) |
| 1130 | { |
| 1131 | uchar buff[MI_KEYDEF_SIZE]; |
| 1132 | uchar *ptr=buff; |
| 1133 | |
| 1134 | *ptr++ = (uchar) keydef->keysegs; |
| 1135 | *ptr++ = keydef->key_alg; /* Rtree or Btree */ |
| 1136 | mi_int2store(ptr,keydef->flag); ptr +=2; |
| 1137 | mi_int2store(ptr,keydef->block_length); ptr +=2; |
| 1138 | mi_int2store(ptr,keydef->keylength); ptr +=2; |
| 1139 | mi_int2store(ptr,keydef->minlength); ptr +=2; |
| 1140 | mi_int2store(ptr,keydef->maxlength); ptr +=2; |
| 1141 | return mysql_file_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0; |
| 1142 | } |
| 1143 | |
| 1144 | uchar *mi_keydef_read(uchar *ptr, MI_KEYDEF *keydef) |
| 1145 | { |
| 1146 | keydef->keysegs = (uint) *ptr++; |
| 1147 | keydef->key_alg = *ptr++; /* Rtree or Btree */ |
| 1148 | |
| 1149 | keydef->flag = mi_uint2korr(ptr); ptr +=2; |
| 1150 | keydef->block_length = mi_uint2korr(ptr); ptr +=2; |
| 1151 | keydef->keylength = mi_uint2korr(ptr); ptr +=2; |
| 1152 | keydef->minlength = mi_uint2korr(ptr); ptr +=2; |
| 1153 | keydef->maxlength = mi_uint2korr(ptr); ptr +=2; |
| 1154 | keydef->block_size_index= keydef->block_length/MI_MIN_KEY_BLOCK_LENGTH-1; |
| 1155 | keydef->underflow_block_length=keydef->block_length/3; |
| 1156 | keydef->version = 0; /* Not saved */ |
| 1157 | keydef->parser = &ft_default_parser; |
| 1158 | keydef->ftkey_nr = 0; |
| 1159 | return ptr; |
| 1160 | } |
| 1161 | |
| 1162 | /*************************************************************************** |
| 1163 | ** mi_keyseg |
| 1164 | ***************************************************************************/ |
| 1165 | |
| 1166 | int mi_keyseg_write(File file, const HA_KEYSEG *keyseg) |
| 1167 | { |
| 1168 | uchar buff[HA_KEYSEG_SIZE]; |
| 1169 | uchar *ptr=buff; |
| 1170 | ulong pos; |
| 1171 | |
| 1172 | *ptr++= keyseg->type; |
| 1173 | *ptr++= keyseg->language & 0xFF; /* Collation ID, low byte */ |
| 1174 | *ptr++= keyseg->null_bit; |
| 1175 | *ptr++= keyseg->bit_start; |
| 1176 | *ptr++= keyseg->language >> 8; /* Collation ID, high byte */ |
| 1177 | *ptr++= keyseg->bit_length; |
| 1178 | mi_int2store(ptr,keyseg->flag); ptr+=2; |
| 1179 | mi_int2store(ptr,keyseg->length); ptr+=2; |
| 1180 | mi_int4store(ptr,keyseg->start); ptr+=4; |
| 1181 | pos= keyseg->null_bit ? keyseg->null_pos : keyseg->bit_pos; |
| 1182 | mi_int4store(ptr, pos); |
| 1183 | ptr+=4; |
| 1184 | |
| 1185 | return mysql_file_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0; |
| 1186 | } |
| 1187 | |
| 1188 | |
| 1189 | uchar *mi_keyseg_read(uchar *ptr, HA_KEYSEG *keyseg) |
| 1190 | { |
| 1191 | keyseg->type = *ptr++; |
| 1192 | keyseg->language = *ptr++; |
| 1193 | keyseg->null_bit = *ptr++; |
| 1194 | keyseg->bit_start = *ptr++; |
| 1195 | keyseg->language += ((uint16) (*ptr++)) << 8; |
| 1196 | keyseg->bit_length = *ptr++; |
| 1197 | keyseg->flag = mi_uint2korr(ptr); ptr +=2; |
| 1198 | keyseg->length = mi_uint2korr(ptr); ptr +=2; |
| 1199 | keyseg->start = mi_uint4korr(ptr); ptr +=4; |
| 1200 | keyseg->null_pos = mi_uint4korr(ptr); ptr +=4; |
| 1201 | keyseg->charset=0; /* Will be filled in later */ |
| 1202 | if (keyseg->null_bit) |
| 1203 | /* We adjust bit_pos if null_bit is last in the byte */ |
| 1204 | keyseg->bit_pos= (uint16)(keyseg->null_pos + (keyseg->null_bit == (1 << 7))); |
| 1205 | else |
| 1206 | { |
| 1207 | keyseg->bit_pos= (uint16)keyseg->null_pos; |
| 1208 | keyseg->null_pos= 0; |
| 1209 | } |
| 1210 | return ptr; |
| 1211 | } |
| 1212 | |
| 1213 | /*-------------------------------------------------------------------------- |
| 1214 | mi_uniquedef |
| 1215 | ---------------------------------------------------------------------------*/ |
| 1216 | |
| 1217 | uint mi_uniquedef_write(File file, MI_UNIQUEDEF *def) |
| 1218 | { |
| 1219 | uchar buff[MI_UNIQUEDEF_SIZE]; |
| 1220 | uchar *ptr=buff; |
| 1221 | |
| 1222 | mi_int2store(ptr,def->keysegs); ptr+=2; |
| 1223 | *ptr++= (uchar) def->key; |
| 1224 | *ptr++ = (uchar) def->null_are_equal; |
| 1225 | |
| 1226 | return mysql_file_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0; |
| 1227 | } |
| 1228 | |
| 1229 | uchar *mi_uniquedef_read(uchar *ptr, MI_UNIQUEDEF *def) |
| 1230 | { |
| 1231 | def->keysegs = mi_uint2korr(ptr); |
| 1232 | def->key = ptr[2]; |
| 1233 | def->null_are_equal=ptr[3]; |
| 1234 | return ptr+4; /* 1 extra byte */ |
| 1235 | } |
| 1236 | |
| 1237 | /*************************************************************************** |
| 1238 | ** MI_COLUMNDEF |
| 1239 | ***************************************************************************/ |
| 1240 | |
| 1241 | uint mi_recinfo_write(File file, MI_COLUMNDEF *recinfo) |
| 1242 | { |
| 1243 | uchar buff[MI_COLUMNDEF_SIZE]; |
| 1244 | uchar *ptr=buff; |
| 1245 | |
| 1246 | mi_int2store(ptr,recinfo->type); ptr +=2; |
| 1247 | mi_int2store(ptr,recinfo->length); ptr +=2; |
| 1248 | *ptr++ = recinfo->null_bit; |
| 1249 | mi_int2store(ptr,recinfo->null_pos); ptr+= 2; |
| 1250 | return mysql_file_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0; |
| 1251 | } |
| 1252 | |
| 1253 | uchar *mi_recinfo_read(uchar *ptr, MI_COLUMNDEF *recinfo) |
| 1254 | { |
| 1255 | recinfo->type= mi_sint2korr(ptr); ptr +=2; |
| 1256 | recinfo->length=mi_uint2korr(ptr); ptr +=2; |
| 1257 | recinfo->null_bit= (uint8) *ptr++; |
| 1258 | recinfo->null_pos=mi_uint2korr(ptr); ptr +=2; |
| 1259 | return ptr; |
| 1260 | } |
| 1261 | |
| 1262 | /************************************************************************** |
| 1263 | Open data file. |
| 1264 | We can't use dup() here as the data file descriptors need to have different |
| 1265 | active seek-positions. |
| 1266 | *************************************************************************/ |
| 1267 | |
| 1268 | int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share) |
| 1269 | { |
| 1270 | myf flags= MY_WME | (share->mode & O_NOFOLLOW ? MY_NOSYMLINKS: 0); |
| 1271 | DEBUG_SYNC_C("mi_open_datafile" ); |
| 1272 | info->dfile= mysql_file_open(mi_key_file_dfile, share->data_file_name, |
| 1273 | share->mode | O_SHARE, MYF(flags)); |
| 1274 | return info->dfile >= 0 ? 0 : 1; |
| 1275 | } |
| 1276 | |
| 1277 | |
| 1278 | int mi_open_keyfile(MYISAM_SHARE *share) |
| 1279 | { |
| 1280 | if ((share->kfile= mysql_file_open(mi_key_file_kfile, |
| 1281 | share->unique_file_name, |
| 1282 | share->mode | O_SHARE | O_NOFOLLOW, |
| 1283 | MYF(MY_NOSYMLINKS | MY_WME))) < 0) |
| 1284 | return 1; |
| 1285 | return 0; |
| 1286 | } |
| 1287 | |
| 1288 | |
| 1289 | /* |
| 1290 | Disable all indexes. |
| 1291 | |
| 1292 | SYNOPSIS |
| 1293 | mi_disable_indexes() |
| 1294 | info A pointer to the MyISAM storage engine MI_INFO struct. |
| 1295 | |
| 1296 | DESCRIPTION |
| 1297 | Disable all indexes. |
| 1298 | |
| 1299 | RETURN |
| 1300 | 0 ok |
| 1301 | */ |
| 1302 | |
| 1303 | int mi_disable_indexes(MI_INFO *info) |
| 1304 | { |
| 1305 | MYISAM_SHARE *share= info->s; |
| 1306 | |
| 1307 | mi_clear_all_keys_active(share->state.key_map); |
| 1308 | return 0; |
| 1309 | } |
| 1310 | |
| 1311 | |
| 1312 | /* |
| 1313 | Enable all indexes |
| 1314 | |
| 1315 | SYNOPSIS |
| 1316 | mi_enable_indexes() |
| 1317 | info A pointer to the MyISAM storage engine MI_INFO struct. |
| 1318 | |
| 1319 | DESCRIPTION |
| 1320 | Enable all indexes. The indexes might have been disabled |
| 1321 | by mi_disable_index() before. |
| 1322 | The function works only if both data and indexes are empty, |
| 1323 | otherwise a repair is required. |
| 1324 | To be sure, call handler::delete_all_rows() before. |
| 1325 | |
| 1326 | RETURN |
| 1327 | 0 ok |
| 1328 | HA_ERR_CRASHED data or index is non-empty. |
| 1329 | */ |
| 1330 | |
| 1331 | int mi_enable_indexes(MI_INFO *info) |
| 1332 | { |
| 1333 | int error= 0; |
| 1334 | MYISAM_SHARE *share= info->s; |
| 1335 | |
| 1336 | if (share->state.state.data_file_length || |
| 1337 | (share->state.state.key_file_length != share->base.keystart)) |
| 1338 | { |
| 1339 | mi_print_error(info->s, HA_ERR_CRASHED); |
| 1340 | error= HA_ERR_CRASHED; |
| 1341 | } |
| 1342 | else |
| 1343 | mi_set_all_keys_active(share->state.key_map, share->base.keys); |
| 1344 | return error; |
| 1345 | } |
| 1346 | |
| 1347 | |
| 1348 | /* |
| 1349 | Test if indexes are disabled. |
| 1350 | |
| 1351 | SYNOPSIS |
| 1352 | mi_indexes_are_disabled() |
| 1353 | info A pointer to the MyISAM storage engine MI_INFO struct. |
| 1354 | |
| 1355 | DESCRIPTION |
| 1356 | Test if indexes are disabled. |
| 1357 | |
| 1358 | RETURN |
| 1359 | 0 indexes are not disabled |
| 1360 | 1 all indexes are disabled |
| 1361 | 2 non-unique indexes are disabled |
| 1362 | */ |
| 1363 | |
| 1364 | int mi_indexes_are_disabled(MI_INFO *info) |
| 1365 | { |
| 1366 | MYISAM_SHARE *share= info->s; |
| 1367 | |
| 1368 | /* |
| 1369 | No keys or all are enabled. keys is the number of keys. Left shifted |
| 1370 | gives us only one bit set. When decreased by one, gives us all all bits |
| 1371 | up to this one set and it gets unset. |
| 1372 | */ |
| 1373 | if (!share->base.keys || |
| 1374 | (mi_is_all_keys_active(share->state.key_map, share->base.keys))) |
| 1375 | return 0; |
| 1376 | |
| 1377 | /* All are disabled */ |
| 1378 | if (mi_is_any_key_active(share->state.key_map)) |
| 1379 | return 1; |
| 1380 | |
| 1381 | /* |
| 1382 | We have keys. Some enabled, some disabled. |
| 1383 | Don't check for any non-unique disabled but return directly 2 |
| 1384 | */ |
| 1385 | return 2; |
| 1386 | } |
| 1387 | |