00001 /* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult 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; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 00018 #ifdef USE_PRAGMA_IMPLEMENTATION 00019 #pragma implementation // gcc: Class implementation 00020 #endif 00021 00022 #include "mysql_priv.h" 00023 #include <m_ctype.h> 00024 #include <myisampack.h> 00025 #include "ha_myisam.h" 00026 #include <stdarg.h> 00027 #ifndef MASTER 00028 #include "../srclib/myisam/myisamdef.h" 00029 #else 00030 #include "../storage/myisam/myisamdef.h" 00031 #include "../storage/myisam/rt_index.h" 00032 #endif 00033 00034 #include <mysql/plugin.h> 00035 00036 ulong myisam_recover_options= HA_RECOVER_NONE; 00037 00038 /* bits in myisam_recover_options */ 00039 const char *myisam_recover_names[] = 00040 { "DEFAULT", "BACKUP", "FORCE", "QUICK", NullS}; 00041 TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", 00042 myisam_recover_names, NULL}; 00043 00044 const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal", 00045 "nulls_ignored", NullS}; 00046 TYPELIB myisam_stats_method_typelib= { 00047 array_elements(myisam_stats_method_names) - 1, "", 00048 myisam_stats_method_names, NULL}; 00049 00050 00051 /***************************************************************************** 00052 ** MyISAM tables 00053 *****************************************************************************/ 00054 00055 static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) 00056 { 00057 return new (mem_root) ha_myisam(table); 00058 } 00059 00060 // collect errors printed by mi_check routines 00061 00062 static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, 00063 const char *fmt, va_list args) 00064 { 00065 THD* thd = (THD*)param->thd; 00066 Protocol *protocol= thd->protocol; 00067 uint length, msg_length; 00068 char msgbuf[MI_MAX_MSG_BUF]; 00069 char name[NAME_LEN*2+2]; 00070 00071 msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); 00072 msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia 00073 00074 DBUG_PRINT(msg_type,("message: %s",msgbuf)); 00075 00076 if (!thd->vio_ok()) 00077 { 00078 sql_print_error(msgbuf); 00079 return; 00080 } 00081 00082 if (param->testflag & (T_CREATE_MISSING_KEYS | T_SAFE_REPAIR | 00083 T_AUTO_REPAIR)) 00084 { 00085 my_message(ER_NOT_KEYFILE,msgbuf,MYF(MY_WME)); 00086 return; 00087 } 00088 length=(uint) (strxmov(name, param->db_name,".",param->table_name,NullS) - 00089 name); 00090 protocol->prepare_for_resend(); 00091 protocol->store(name, length, system_charset_info); 00092 protocol->store(param->op_name, system_charset_info); 00093 protocol->store(msg_type, system_charset_info); 00094 protocol->store(msgbuf, msg_length, system_charset_info); 00095 if (protocol->write()) 00096 sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n", 00097 msgbuf); 00098 return; 00099 } 00100 00101 extern "C" { 00102 00103 volatile int *killed_ptr(MI_CHECK *param) 00104 { 00105 /* In theory Unsafe conversion, but should be ok for now */ 00106 return (int*) &(((THD *)(param->thd))->killed); 00107 } 00108 00109 void mi_check_print_error(MI_CHECK *param, const char *fmt,...) 00110 { 00111 param->error_printed|=1; 00112 param->out_flag|= O_DATA_LOST; 00113 va_list args; 00114 va_start(args, fmt); 00115 mi_check_print_msg(param, "error", fmt, args); 00116 va_end(args); 00117 } 00118 00119 void mi_check_print_info(MI_CHECK *param, const char *fmt,...) 00120 { 00121 va_list args; 00122 va_start(args, fmt); 00123 mi_check_print_msg(param, "info", fmt, args); 00124 va_end(args); 00125 } 00126 00127 void mi_check_print_warning(MI_CHECK *param, const char *fmt,...) 00128 { 00129 param->warning_printed=1; 00130 param->out_flag|= O_DATA_LOST; 00131 va_list args; 00132 va_start(args, fmt); 00133 mi_check_print_msg(param, "warning", fmt, args); 00134 va_end(args); 00135 } 00136 00137 } 00138 00139 00140 ha_myisam::ha_myisam(TABLE_SHARE *table_arg) 00141 :handler(&myisam_hton, table_arg), file(0), 00142 int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | 00143 HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | 00144 HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS | 00145 HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS | 00146 HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT), 00147 can_enable_indexes(1) 00148 {} 00149 00150 00151 static const char *ha_myisam_exts[] = { 00152 ".MYI", 00153 ".MYD", 00154 NullS 00155 }; 00156 00157 const char **ha_myisam::bas_ext() const 00158 { 00159 return ha_myisam_exts; 00160 } 00161 00162 00163 const char *ha_myisam::index_type(uint key_number) 00164 { 00165 return ((table->key_info[key_number].flags & HA_FULLTEXT) ? 00166 "FULLTEXT" : 00167 (table->key_info[key_number].flags & HA_SPATIAL) ? 00168 "SPATIAL" : 00169 (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ? 00170 "RTREE" : 00171 "BTREE"); 00172 } 00173 00174 #ifdef HAVE_REPLICATION 00175 int ha_myisam::net_read_dump(NET* net) 00176 { 00177 int data_fd = file->dfile; 00178 int error = 0; 00179 00180 my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME)); 00181 for (;;) 00182 { 00183 ulong packet_len = my_net_read(net); 00184 if (!packet_len) 00185 break ; // end of file 00186 if (packet_len == packet_error) 00187 { 00188 sql_print_error("ha_myisam::net_read_dump - read error "); 00189 error= -1; 00190 goto err; 00191 } 00192 if (my_write(data_fd, (byte*)net->read_pos, (uint) packet_len, 00193 MYF(MY_WME|MY_FNABP))) 00194 { 00195 error = errno; 00196 goto err; 00197 } 00198 } 00199 err: 00200 return error; 00201 } 00202 00203 00204 int ha_myisam::dump(THD* thd, int fd) 00205 { 00206 MYISAM_SHARE* share = file->s; 00207 NET* net = &thd->net; 00208 uint blocksize = share->blocksize; 00209 my_off_t bytes_to_read = share->state.state.data_file_length; 00210 int data_fd = file->dfile; 00211 byte * buf = (byte*) my_malloc(blocksize, MYF(MY_WME)); 00212 if (!buf) 00213 return ENOMEM; 00214 00215 int error = 0; 00216 my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME)); 00217 for (; bytes_to_read > 0;) 00218 { 00219 uint bytes = my_read(data_fd, buf, blocksize, MYF(MY_WME)); 00220 if (bytes == MY_FILE_ERROR) 00221 { 00222 error = errno; 00223 goto err; 00224 } 00225 00226 if (fd >= 0) 00227 { 00228 if (my_write(fd, buf, bytes, MYF(MY_WME | MY_FNABP))) 00229 { 00230 error = errno ? errno : EPIPE; 00231 goto err; 00232 } 00233 } 00234 else 00235 { 00236 if (my_net_write(net, (char*) buf, bytes)) 00237 { 00238 error = errno ? errno : EPIPE; 00239 goto err; 00240 } 00241 } 00242 bytes_to_read -= bytes; 00243 } 00244 00245 if (fd < 0) 00246 { 00247 if (my_net_write(net, "", 0)) 00248 error = errno ? errno : EPIPE; 00249 net_flush(net); 00250 } 00251 00252 err: 00253 my_free((gptr) buf, MYF(0)); 00254 return error; 00255 } 00256 #endif /* HAVE_REPLICATION */ 00257 00258 00259 bool ha_myisam::check_if_locking_is_allowed(uint sql_command, 00260 ulong type, TABLE *table, 00261 uint count, 00262 bool called_by_logger_thread) 00263 { 00264 /* 00265 To be able to open and lock for reading system tables like 'mysql.proc', 00266 when we already have some tables opened and locked, and avoid deadlocks 00267 we have to disallow write-locking of these tables with any other tables. 00268 */ 00269 if (table->s->system_table && 00270 table->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE && 00271 count != 1) 00272 { 00273 my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0), table->s->db.str, 00274 table->s->table_name.str); 00275 return FALSE; 00276 } 00277 00278 /* 00279 Deny locking of the log tables, which is incompatible with 00280 concurrent insert. Unless called from a logger THD: 00281 general_log_thd or slow_log_thd. 00282 */ 00283 if (!called_by_logger_thread) 00284 return check_if_log_table_locking_is_allowed(sql_command, type, table); 00285 00286 return TRUE; 00287 } 00288 00289 /* Name is here without an extension */ 00290 00291 int ha_myisam::open(const char *name, int mode, uint test_if_locked) 00292 { 00293 uint i; 00294 if (!(file=mi_open(name, mode, test_if_locked | HA_OPEN_FROM_SQL_LAYER))) 00295 return (my_errno ? my_errno : -1); 00296 00297 if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE)) 00298 VOID(mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0)); 00299 00300 if (!(test_if_locked & HA_OPEN_TMP_TABLE) && opt_myisam_use_mmap) 00301 VOID(mi_extra(file, HA_EXTRA_MMAP, 0)); 00302 00303 info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); 00304 if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) 00305 VOID(mi_extra(file, HA_EXTRA_WAIT_LOCK, 0)); 00306 if (!table->s->db_record_offset) 00307 int_table_flags|=HA_REC_NOT_IN_SEQ; 00308 if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) 00309 int_table_flags|=HA_HAS_CHECKSUM; 00310 00311 for (i= 0; i < table->s->keys; i++) 00312 { 00313 struct st_plugin_int *parser= table->key_info[i].parser; 00314 if (table->key_info[i].flags & HA_USES_PARSER) 00315 file->s->keyinfo[i].parser= 00316 (struct st_mysql_ftparser *)parser->plugin->info; 00317 table->key_info[i].block_size= file->s->keyinfo[i].block_length; 00318 } 00319 return (0); 00320 } 00321 00322 int ha_myisam::close(void) 00323 { 00324 MI_INFO *tmp=file; 00325 file=0; 00326 return mi_close(tmp); 00327 } 00328 00329 int ha_myisam::write_row(byte * buf) 00330 { 00331 statistic_increment(table->in_use->status_var.ha_write_count,&LOCK_status); 00332 00333 /* If we have a timestamp column, update it to the current time */ 00334 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) 00335 table->timestamp_field->set_time(); 00336 00337 /* 00338 If we have an auto_increment column and we are writing a changed row 00339 or a new row, then update the auto_increment value in the record. 00340 */ 00341 if (table->next_number_field && buf == table->record[0]) 00342 update_auto_increment(); 00343 return mi_write(file,buf); 00344 } 00345 00346 int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) 00347 { 00348 if (!file) return HA_ADMIN_INTERNAL_ERROR; 00349 int error; 00350 MI_CHECK param; 00351 MYISAM_SHARE* share = file->s; 00352 const char *old_proc_info=thd->proc_info; 00353 00354 thd->proc_info="Checking table"; 00355 myisamchk_init(¶m); 00356 param.thd = thd; 00357 param.op_name = "check"; 00358 param.db_name= table->s->db.str; 00359 param.table_name= table->alias; 00360 param.testflag = check_opt->flags | T_CHECK | T_SILENT; 00361 param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method; 00362 00363 if (!(table->db_stat & HA_READ_ONLY)) 00364 param.testflag|= T_STATISTICS; 00365 param.using_global_keycache = 1; 00366 00367 if (!mi_is_crashed(file) && 00368 (((param.testflag & T_CHECK_ONLY_CHANGED) && 00369 !(share->state.changed & (STATE_CHANGED | STATE_CRASHED | 00370 STATE_CRASHED_ON_REPAIR)) && 00371 share->state.open_count == 0) || 00372 ((param.testflag & T_FAST) && (share->state.open_count == 00373 (uint) (share->global_changed ? 1 : 0))))) 00374 return HA_ADMIN_ALREADY_DONE; 00375 00376 error = chk_status(¶m, file); // Not fatal 00377 error = chk_size(¶m, file); 00378 if (!error) 00379 error |= chk_del(¶m, file, param.testflag); 00380 if (!error) 00381 error = chk_key(¶m, file); 00382 if (!error) 00383 { 00384 if ((!(param.testflag & T_QUICK) && 00385 ((share->options & 00386 (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) || 00387 (param.testflag & (T_EXTEND | T_MEDIUM)))) || 00388 mi_is_crashed(file)) 00389 { 00390 uint old_testflag=param.testflag; 00391 param.testflag|=T_MEDIUM; 00392 if (!(error= init_io_cache(¶m.read_cache, file->dfile, 00393 my_default_record_cache_size, READ_CACHE, 00394 share->pack.header_length, 1, MYF(MY_WME)))) 00395 { 00396 error= chk_data_link(¶m, file, param.testflag & T_EXTEND); 00397 end_io_cache(&(param.read_cache)); 00398 } 00399 param.testflag= old_testflag; 00400 } 00401 } 00402 if (!error) 00403 { 00404 if ((share->state.changed & (STATE_CHANGED | 00405 STATE_CRASHED_ON_REPAIR | 00406 STATE_CRASHED | STATE_NOT_ANALYZED)) || 00407 (param.testflag & T_STATISTICS) || 00408 mi_is_crashed(file)) 00409 { 00410 file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; 00411 pthread_mutex_lock(&share->intern_lock); 00412 share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED | 00413 STATE_CRASHED_ON_REPAIR); 00414 if (!(table->db_stat & HA_READ_ONLY)) 00415 error=update_state_info(¶m,file,UPDATE_TIME | UPDATE_OPEN_COUNT | 00416 UPDATE_STAT); 00417 pthread_mutex_unlock(&share->intern_lock); 00418 info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE | 00419 HA_STATUS_CONST); 00420 } 00421 } 00422 else if (!mi_is_crashed(file) && !thd->killed) 00423 { 00424 mi_mark_crashed(file); 00425 file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; 00426 } 00427 00428 thd->proc_info=old_proc_info; 00429 return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; 00430 } 00431 00432 00433 /* 00434 analyze the key distribution in the table 00435 As the table may be only locked for read, we have to take into account that 00436 two threads may do an analyze at the same time! 00437 */ 00438 00439 int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt) 00440 { 00441 int error=0; 00442 MI_CHECK param; 00443 MYISAM_SHARE* share = file->s; 00444 00445 myisamchk_init(¶m); 00446 param.thd = thd; 00447 param.op_name= "analyze"; 00448 param.db_name= table->s->db.str; 00449 param.table_name= table->alias; 00450 param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS | 00451 T_DONT_CHECK_CHECKSUM); 00452 param.using_global_keycache = 1; 00453 param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method; 00454 00455 if (!(share->state.changed & STATE_NOT_ANALYZED)) 00456 return HA_ADMIN_ALREADY_DONE; 00457 00458 error = chk_key(¶m, file); 00459 if (!error) 00460 { 00461 pthread_mutex_lock(&share->intern_lock); 00462 error=update_state_info(¶m,file,UPDATE_STAT); 00463 pthread_mutex_unlock(&share->intern_lock); 00464 } 00465 else if (!mi_is_crashed(file) && !thd->killed) 00466 mi_mark_crashed(file); 00467 return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; 00468 } 00469 00470 00471 int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) 00472 { 00473 HA_CHECK_OPT tmp_check_opt; 00474 char *backup_dir= thd->lex->backup_dir; 00475 char src_path[FN_REFLEN], dst_path[FN_REFLEN]; 00476 char table_name[FN_REFLEN]; 00477 int error; 00478 const char* errmsg; 00479 DBUG_ENTER("restore"); 00480 00481 VOID(tablename_to_filename(table->s->table_name.str, table_name, 00482 sizeof(table_name))); 00483 00484 if (fn_format_relative_to_data_home(src_path, table_name, backup_dir, 00485 MI_NAME_DEXT)) 00486 DBUG_RETURN(HA_ADMIN_INVALID); 00487 00488 strxmov(dst_path, table->s->normalized_path.str, MI_NAME_DEXT, NullS); 00489 if (my_copy(src_path, dst_path, MYF(MY_WME))) 00490 { 00491 error= HA_ADMIN_FAILED; 00492 errmsg= "Failed in my_copy (Error %d)"; 00493 goto err; 00494 } 00495 00496 tmp_check_opt.init(); 00497 tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK; 00498 DBUG_RETURN(repair(thd, &tmp_check_opt)); 00499 00500 err: 00501 { 00502 MI_CHECK param; 00503 myisamchk_init(¶m); 00504 param.thd= thd; 00505 param.op_name= "restore"; 00506 param.db_name= table->s->db.str; 00507 param.table_name= table->s->table_name.str; 00508 param.testflag= 0; 00509 mi_check_print_error(¶m, errmsg, my_errno); 00510 DBUG_RETURN(error); 00511 } 00512 } 00513 00514 00515 int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt) 00516 { 00517 char *backup_dir= thd->lex->backup_dir; 00518 char src_path[FN_REFLEN], dst_path[FN_REFLEN]; 00519 char table_name[FN_REFLEN]; 00520 int error; 00521 const char *errmsg; 00522 DBUG_ENTER("ha_myisam::backup"); 00523 00524 VOID(tablename_to_filename(table->s->table_name.str, table_name, 00525 sizeof(table_name))); 00526 00527 if (fn_format_relative_to_data_home(dst_path, table_name, backup_dir, 00528 reg_ext)) 00529 { 00530 errmsg= "Failed in fn_format() for .frm file (errno: %d)"; 00531 error= HA_ADMIN_INVALID; 00532 goto err; 00533 } 00534 00535 strxmov(src_path, table->s->normalized_path.str, reg_ext, NullS); 00536 if (my_copy(src_path, dst_path, 00537 MYF(MY_WME | MY_HOLD_ORIGINAL_MODES | MY_DONT_OVERWRITE_FILE))) 00538 { 00539 error = HA_ADMIN_FAILED; 00540 errmsg = "Failed copying .frm file (errno: %d)"; 00541 goto err; 00542 } 00543 00544 /* Change extension */ 00545 if (fn_format_relative_to_data_home(dst_path, table_name, backup_dir, 00546 MI_NAME_DEXT)) 00547 { 00548 errmsg = "Failed in fn_format() for .MYD file (errno: %d)"; 00549 error = HA_ADMIN_INVALID; 00550 goto err; 00551 } 00552 00553 strxmov(src_path, table->s->normalized_path.str, MI_NAME_DEXT, NullS); 00554 if (my_copy(src_path, dst_path, 00555 MYF(MY_WME | MY_HOLD_ORIGINAL_MODES | MY_DONT_OVERWRITE_FILE))) 00556 { 00557 errmsg = "Failed copying .MYD file (errno: %d)"; 00558 error= HA_ADMIN_FAILED; 00559 goto err; 00560 } 00561 DBUG_RETURN(HA_ADMIN_OK); 00562 00563 err: 00564 { 00565 MI_CHECK param; 00566 myisamchk_init(¶m); 00567 param.thd= thd; 00568 param.op_name= "backup"; 00569 param.db_name= table->s->db.str; 00570 param.table_name= table->s->table_name.str; 00571 param.testflag = 0; 00572 mi_check_print_error(¶m,errmsg, my_errno); 00573 DBUG_RETURN(error); 00574 } 00575 } 00576 00577 00578 int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) 00579 { 00580 int error; 00581 MI_CHECK param; 00582 ha_rows start_records; 00583 00584 if (!file) return HA_ADMIN_INTERNAL_ERROR; 00585 00586 myisamchk_init(¶m); 00587 param.thd = thd; 00588 param.op_name= "repair"; 00589 param.testflag= ((check_opt->flags & ~(T_EXTEND)) | 00590 T_SILENT | T_FORCE_CREATE | T_CALC_CHECKSUM | 00591 (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT)); 00592 param.sort_buffer_length= check_opt->sort_buffer_size; 00593 start_records=file->state->records; 00594 while ((error=repair(thd,param,0)) && param.retry_repair) 00595 { 00596 param.retry_repair=0; 00597 if (test_all_bits(param.testflag, 00598 (uint) (T_RETRY_WITHOUT_QUICK | T_QUICK))) 00599 { 00600 param.testflag&= ~T_RETRY_WITHOUT_QUICK; 00601 sql_print_information("Retrying repair of: '%s' without quick", 00602 table->s->path); 00603 continue; 00604 } 00605 param.testflag&= ~T_QUICK; 00606 if ((param.testflag & T_REP_BY_SORT)) 00607 { 00608 param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP; 00609 sql_print_information("Retrying repair of: '%s' with keycache", 00610 table->s->path); 00611 continue; 00612 } 00613 break; 00614 } 00615 if (!error && start_records != file->state->records && 00616 !(check_opt->flags & T_VERY_SILENT)) 00617 { 00618 char llbuff[22],llbuff2[22]; 00619 sql_print_information("Found %s of %s rows when repairing '%s'", 00620 llstr(file->state->records, llbuff), 00621 llstr(start_records, llbuff2), 00622 table->s->path); 00623 } 00624 return error; 00625 } 00626 00627 int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) 00628 { 00629 int error; 00630 if (!file) return HA_ADMIN_INTERNAL_ERROR; 00631 MI_CHECK param; 00632 00633 myisamchk_init(¶m); 00634 param.thd = thd; 00635 param.op_name= "optimize"; 00636 param.testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE | 00637 T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX); 00638 param.sort_buffer_length= check_opt->sort_buffer_size; 00639 if ((error= repair(thd,param,1)) && param.retry_repair) 00640 { 00641 sql_print_warning("Warning: Optimize table got errno %d, retrying", 00642 my_errno); 00643 param.testflag&= ~T_REP_BY_SORT; 00644 error= repair(thd,param,1); 00645 } 00646 return error; 00647 } 00648 00649 00650 int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) 00651 { 00652 int error=0; 00653 uint local_testflag=param.testflag; 00654 bool optimize_done= !optimize, statistics_done=0; 00655 const char *old_proc_info=thd->proc_info; 00656 char fixed_name[FN_REFLEN]; 00657 MYISAM_SHARE* share = file->s; 00658 ha_rows rows= file->state->records; 00659 DBUG_ENTER("ha_myisam::repair"); 00660 00661 param.db_name= table->s->db.str; 00662 param.table_name= table->alias; 00663 param.tmpfile_createflag = O_RDWR | O_TRUNC; 00664 param.using_global_keycache = 1; 00665 param.thd= thd; 00666 param.tmpdir= &mysql_tmpdir_list; 00667 param.out_flag= 0; 00668 strmov(fixed_name,file->filename); 00669 00670 // Don't lock tables if we have used LOCK TABLE 00671 if (!thd->locked_tables && 00672 mi_lock_database(file, table->s->tmp_table ? F_EXTRA_LCK : F_WRLCK)) 00673 { 00674 mi_check_print_error(¶m,ER(ER_CANT_LOCK),my_errno); 00675 DBUG_RETURN(HA_ADMIN_FAILED); 00676 } 00677 00678 if (!optimize || 00679 ((file->state->del || share->state.split != file->state->records) && 00680 (!(param.testflag & T_QUICK) || 00681 !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS)))) 00682 { 00683 ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ? 00684 mi_get_mask_all_keys_active(share->base.keys) : 00685 share->state.key_map); 00686 uint testflag=param.testflag; 00687 if (mi_test_if_sort_rep(file,file->state->records,key_map,0) && 00688 (local_testflag & T_REP_BY_SORT)) 00689 { 00690 local_testflag|= T_STATISTICS; 00691 param.testflag|= T_STATISTICS; // We get this for free 00692 statistics_done=1; 00693 if (thd->variables.myisam_repair_threads>1) 00694 { 00695 char buf[40]; 00696 /* TODO: respect myisam_repair_threads variable */ 00697 my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map)); 00698 thd->proc_info=buf; 00699 error = mi_repair_parallel(¶m, file, fixed_name, 00700 param.testflag & T_QUICK); 00701 thd->proc_info="Repair done"; // to reset proc_info, as 00702 // it was pointing to local buffer 00703 } 00704 else 00705 { 00706 thd->proc_info="Repair by sorting"; 00707 error = mi_repair_by_sort(¶m, file, fixed_name, 00708 param.testflag & T_QUICK); 00709 } 00710 } 00711 else 00712 { 00713 thd->proc_info="Repair with keycache"; 00714 param.testflag &= ~T_REP_BY_SORT; 00715 error= mi_repair(¶m, file, fixed_name, 00716 param.testflag & T_QUICK); 00717 } 00718 param.testflag=testflag; 00719 optimize_done=1; 00720 } 00721 if (!error) 00722 { 00723 if ((local_testflag & T_SORT_INDEX) && 00724 (share->state.changed & STATE_NOT_SORTED_PAGES)) 00725 { 00726 optimize_done=1; 00727 thd->proc_info="Sorting index"; 00728 error=mi_sort_index(¶m,file,fixed_name); 00729 } 00730 if (!statistics_done && (local_testflag & T_STATISTICS)) 00731 { 00732 if (share->state.changed & STATE_NOT_ANALYZED) 00733 { 00734 optimize_done=1; 00735 thd->proc_info="Analyzing"; 00736 error = chk_key(¶m, file); 00737 } 00738 else 00739 local_testflag&= ~T_STATISTICS; // Don't update statistics 00740 } 00741 } 00742 thd->proc_info="Saving state"; 00743 if (!error) 00744 { 00745 if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file)) 00746 { 00747 share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED | 00748 STATE_CRASHED_ON_REPAIR); 00749 file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; 00750 } 00751 /* 00752 the following 'if', thought conceptually wrong, 00753 is a useful optimization nevertheless. 00754 */ 00755 if (file->state != &file->s->state.state) 00756 file->s->state.state = *file->state; 00757 if (file->s->base.auto_key) 00758 update_auto_increment_key(¶m, file, 1); 00759 if (optimize_done) 00760 error = update_state_info(¶m, file, 00761 UPDATE_TIME | UPDATE_OPEN_COUNT | 00762 (local_testflag & 00763 T_STATISTICS ? UPDATE_STAT : 0)); 00764 info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE | 00765 HA_STATUS_CONST); 00766 if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT)) 00767 { 00768 char llbuff[22],llbuff2[22]; 00769 mi_check_print_warning(¶m,"Number of rows changed from %s to %s", 00770 llstr(rows,llbuff), 00771 llstr(file->state->records,llbuff2)); 00772 } 00773 } 00774 else 00775 { 00776 mi_mark_crashed_on_repair(file); 00777 file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; 00778 update_state_info(¶m, file, 0); 00779 } 00780 thd->proc_info=old_proc_info; 00781 if (!thd->locked_tables) 00782 mi_lock_database(file,F_UNLCK); 00783 DBUG_RETURN(error ? HA_ADMIN_FAILED : 00784 !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK); 00785 } 00786 00787 00788 /* 00789 Assign table indexes to a specific key cache. 00790 */ 00791 00792 int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt) 00793 { 00794 KEY_CACHE *new_key_cache= check_opt->key_cache; 00795 const char *errmsg= 0; 00796 int error= HA_ADMIN_OK; 00797 ulonglong map= ~(ulonglong) 0; 00798 TABLE_LIST *table_list= table->pos_in_table_list; 00799 DBUG_ENTER("ha_myisam::assign_to_keycache"); 00800 00801 /* Check validity of the index references */ 00802 if (table_list->use_index) 00803 { 00804 /* We only come here when the user did specify an index map */ 00805 key_map kmap; 00806 if (get_key_map_from_key_list(&kmap, table, table_list->use_index)) 00807 { 00808 errmsg= thd->net.last_error; 00809 error= HA_ADMIN_FAILED; 00810 goto err; 00811 } 00812 map= kmap.to_ulonglong(); 00813 } 00814 00815 if ((error= mi_assign_to_key_cache(file, map, new_key_cache))) 00816 { 00817 char buf[STRING_BUFFER_USUAL_SIZE]; 00818 my_snprintf(buf, sizeof(buf), 00819 "Failed to flush to index file (errno: %d)", error); 00820 errmsg= buf; 00821 error= HA_ADMIN_CORRUPT; 00822 } 00823 00824 err: 00825 if (error != HA_ADMIN_OK) 00826 { 00827 /* Send error to user */ 00828 MI_CHECK param; 00829 myisamchk_init(¶m); 00830 param.thd= thd; 00831 param.op_name= "assign_to_keycache"; 00832 param.db_name= table->s->db.str; 00833 param.table_name= table->s->table_name.str; 00834 param.testflag= 0; 00835 mi_check_print_error(¶m, errmsg); 00836 } 00837 DBUG_RETURN(error); 00838 } 00839 00840 00841 /* 00842 Preload pages of the index file for a table into the key cache. 00843 */ 00844 00845 int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt) 00846 { 00847 int error; 00848 const char *errmsg; 00849 ulonglong map= ~(ulonglong) 0; 00850 TABLE_LIST *table_list= table->pos_in_table_list; 00851 my_bool ignore_leaves= table_list->ignore_leaves; 00852 00853 DBUG_ENTER("ha_myisam::preload_keys"); 00854 00855 /* Check validity of the index references */ 00856 if (table_list->use_index) 00857 { 00858 key_map kmap; 00859 get_key_map_from_key_list(&kmap, table, table_list->use_index); 00860 if (kmap.is_set_all()) 00861 { 00862 errmsg= thd->net.last_error; 00863 error= HA_ADMIN_FAILED; 00864 goto err; 00865 } 00866 if (!kmap.is_clear_all()) 00867 map= kmap.to_ulonglong(); 00868 } 00869 00870 mi_extra(file, HA_EXTRA_PRELOAD_BUFFER_SIZE, 00871 (void *) &thd->variables.preload_buff_size); 00872 00873 if ((error= mi_preload(file, map, ignore_leaves))) 00874 { 00875 switch (error) { 00876 case HA_ERR_NON_UNIQUE_BLOCK_SIZE: 00877 errmsg= "Indexes use different block sizes"; 00878 break; 00879 case HA_ERR_OUT_OF_MEM: 00880 errmsg= "Failed to allocate buffer"; 00881 break; 00882 default: 00883 char buf[ERRMSGSIZE+20]; 00884 my_snprintf(buf, ERRMSGSIZE, 00885 "Failed to read from index file (errno: %d)", my_errno); 00886 errmsg= buf; 00887 } 00888 error= HA_ADMIN_FAILED; 00889 goto err; 00890 } 00891 00892 DBUG_RETURN(HA_ADMIN_OK); 00893 00894 err: 00895 { 00896 MI_CHECK param; 00897 myisamchk_init(¶m); 00898 param.thd= thd; 00899 param.op_name= "preload_keys"; 00900 param.db_name= table->s->db.str; 00901 param.table_name= table->s->table_name.str; 00902 param.testflag= 0; 00903 mi_check_print_error(¶m, errmsg); 00904 DBUG_RETURN(error); 00905 } 00906 } 00907 00908 00909 /* 00910 Disable indexes, making it persistent if requested. 00911 00912 SYNOPSIS 00913 disable_indexes() 00914 mode mode of operation: 00915 HA_KEY_SWITCH_NONUNIQ disable all non-unique keys 00916 HA_KEY_SWITCH_ALL disable all keys 00917 HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent 00918 HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent 00919 00920 IMPLEMENTATION 00921 HA_KEY_SWITCH_NONUNIQ is not implemented. 00922 HA_KEY_SWITCH_ALL_SAVE is not implemented. 00923 00924 RETURN 00925 0 ok 00926 HA_ERR_WRONG_COMMAND mode not implemented. 00927 */ 00928 00929 int ha_myisam::disable_indexes(uint mode) 00930 { 00931 int error; 00932 00933 if (mode == HA_KEY_SWITCH_ALL) 00934 { 00935 /* call a storage engine function to switch the key map */ 00936 error= mi_disable_indexes(file); 00937 } 00938 else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE) 00939 { 00940 mi_extra(file, HA_EXTRA_NO_KEYS, 0); 00941 info(HA_STATUS_CONST); // Read new key info 00942 error= 0; 00943 } 00944 else 00945 { 00946 /* mode not implemented */ 00947 error= HA_ERR_WRONG_COMMAND; 00948 } 00949 return error; 00950 } 00951 00952 00953 /* 00954 Enable indexes, making it persistent if requested. 00955 00956 SYNOPSIS 00957 enable_indexes() 00958 mode mode of operation: 00959 HA_KEY_SWITCH_NONUNIQ enable all non-unique keys 00960 HA_KEY_SWITCH_ALL enable all keys 00961 HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent 00962 HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent 00963 00964 DESCRIPTION 00965 Enable indexes, which might have been disabled by disable_index() before. 00966 The modes without _SAVE work only if both data and indexes are empty, 00967 since the MyISAM repair would enable them persistently. 00968 To be sure in these cases, call handler::delete_all_rows() before. 00969 00970 IMPLEMENTATION 00971 HA_KEY_SWITCH_NONUNIQ is not implemented. 00972 HA_KEY_SWITCH_ALL_SAVE is not implemented. 00973 00974 RETURN 00975 0 ok 00976 !=0 Error, among others: 00977 HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry. 00978 HA_ERR_WRONG_COMMAND mode not implemented. 00979 */ 00980 00981 int ha_myisam::enable_indexes(uint mode) 00982 { 00983 int error; 00984 00985 if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys)) 00986 { 00987 /* All indexes are enabled already. */ 00988 return 0; 00989 } 00990 00991 if (mode == HA_KEY_SWITCH_ALL) 00992 { 00993 error= mi_enable_indexes(file); 00994 /* 00995 Do not try to repair on error, 00996 as this could make the enabled state persistent, 00997 but mode==HA_KEY_SWITCH_ALL forbids it. 00998 */ 00999 } 01000 else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE) 01001 { 01002 THD *thd=current_thd; 01003 MI_CHECK param; 01004 const char *save_proc_info=thd->proc_info; 01005 thd->proc_info="Creating index"; 01006 myisamchk_init(¶m); 01007 param.op_name= "recreating_index"; 01008 param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK | 01009 T_CREATE_MISSING_KEYS); 01010 param.myf_rw&= ~MY_WAIT_IF_FULL; 01011 param.sort_buffer_length= thd->variables.myisam_sort_buff_size; 01012 param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method; 01013 param.tmpdir=&mysql_tmpdir_list; 01014 if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair) 01015 { 01016 sql_print_warning("Warning: Enabling keys got errno %d, retrying", 01017 my_errno); 01018 /* Repairing by sort failed. Now try standard repair method. */ 01019 param.testflag&= ~(T_REP_BY_SORT | T_QUICK); 01020 error= (repair(thd,param,0) != HA_ADMIN_OK); 01021 /* 01022 If the standard repair succeeded, clear all error messages which 01023 might have been set by the first repair. They can still be seen 01024 with SHOW WARNINGS then. 01025 */ 01026 if (! error) 01027 thd->clear_error(); 01028 } 01029 info(HA_STATUS_CONST); 01030 thd->proc_info=save_proc_info; 01031 } 01032 else 01033 { 01034 /* mode not implemented */ 01035 error= HA_ERR_WRONG_COMMAND; 01036 } 01037 return error; 01038 } 01039 01040 01041 /* 01042 Test if indexes are disabled. 01043 01044 01045 SYNOPSIS 01046 indexes_are_disabled() 01047 no parameters 01048 01049 01050 RETURN 01051 0 indexes are not disabled 01052 1 all indexes are disabled 01053 [2 non-unique indexes are disabled - NOT YET IMPLEMENTED] 01054 */ 01055 01056 int ha_myisam::indexes_are_disabled(void) 01057 { 01058 01059 return mi_indexes_are_disabled(file); 01060 } 01061 01062 01063 /* 01064 prepare for a many-rows insert operation 01065 e.g. - disable indexes (if they can be recreated fast) or 01066 activate special bulk-insert optimizations 01067 01068 SYNOPSIS 01069 start_bulk_insert(rows) 01070 rows Rows to be inserted 01071 0 if we don't know 01072 01073 NOTICE 01074 Do not forget to call end_bulk_insert() later! 01075 */ 01076 01077 void ha_myisam::start_bulk_insert(ha_rows rows) 01078 { 01079 DBUG_ENTER("ha_myisam::start_bulk_insert"); 01080 THD *thd= current_thd; 01081 ulong size= min(thd->variables.read_buff_size, 01082 table->s->avg_row_length*rows); 01083 DBUG_PRINT("info",("start_bulk_insert: rows %lu size %lu", 01084 (ulong) rows, size)); 01085 01086 /* don't enable row cache if too few rows */ 01087 if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE)) 01088 mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size); 01089 01090 can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map, 01091 file->s->base.keys); 01092 01093 if (!(specialflag & SPECIAL_SAFE_MODE)) 01094 { 01095 /* 01096 Only disable old index if the table was empty and we are inserting 01097 a lot of rows. 01098 We should not do this for only a few rows as this is slower and 01099 we don't want to update the key statistics based of only a few rows. 01100 */ 01101 if (file->state->records == 0 && can_enable_indexes && 01102 (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES)) 01103 mi_disable_non_unique_index(file,rows); 01104 else 01105 if (!file->bulk_insert && 01106 (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT)) 01107 { 01108 mi_init_bulk_insert(file, thd->variables.bulk_insert_buff_size, rows); 01109 } 01110 } 01111 DBUG_VOID_RETURN; 01112 } 01113 01114 /* 01115 end special bulk-insert optimizations, 01116 which have been activated by start_bulk_insert(). 01117 01118 SYNOPSIS 01119 end_bulk_insert() 01120 no arguments 01121 01122 RETURN 01123 0 OK 01124 != 0 Error 01125 */ 01126 01127 int ha_myisam::end_bulk_insert() 01128 { 01129 mi_end_bulk_insert(file); 01130 int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0); 01131 return err ? err : can_enable_indexes ? 01132 enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0; 01133 } 01134 01135 01136 bool ha_myisam::check_and_repair(THD *thd) 01137 { 01138 int error=0; 01139 int marked_crashed; 01140 char *old_query; 01141 uint old_query_length; 01142 HA_CHECK_OPT check_opt; 01143 DBUG_ENTER("ha_myisam::check_and_repair"); 01144 01145 check_opt.init(); 01146 check_opt.flags= T_MEDIUM | T_AUTO_REPAIR; 01147 // Don't use quick if deleted rows 01148 if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK)) 01149 check_opt.flags|=T_QUICK; 01150 sql_print_warning("Checking table: '%s'",table->s->path); 01151 01152 old_query= thd->query; 01153 old_query_length= thd->query_length; 01154 pthread_mutex_lock(&LOCK_thread_count); 01155 thd->query= table->s->table_name.str; 01156 thd->query_length= table->s->table_name.length; 01157 pthread_mutex_unlock(&LOCK_thread_count); 01158 01159 if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt)) 01160 { 01161 sql_print_warning("Recovering table: '%s'",table->s->path); 01162 check_opt.flags= 01163 ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) | 01164 (marked_crashed ? 0 : T_QUICK) | 01165 (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) | 01166 T_AUTO_REPAIR); 01167 if (repair(thd, &check_opt)) 01168 error=1; 01169 } 01170 pthread_mutex_lock(&LOCK_thread_count); 01171 thd->query= old_query; 01172 thd->query_length= old_query_length; 01173 pthread_mutex_unlock(&LOCK_thread_count); 01174 DBUG_RETURN(error); 01175 } 01176 01177 bool ha_myisam::is_crashed() const 01178 { 01179 return (file->s->state.changed & STATE_CRASHED || 01180 (my_disable_locking && file->s->state.open_count)); 01181 } 01182 01183 int ha_myisam::update_row(const byte * old_data, byte * new_data) 01184 { 01185 statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status); 01186 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) 01187 table->timestamp_field->set_time(); 01188 return mi_update(file,old_data,new_data); 01189 } 01190 01191 int ha_myisam::delete_row(const byte * buf) 01192 { 01193 statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status); 01194 return mi_delete(file,buf); 01195 } 01196 01197 int ha_myisam::index_read(byte * buf, const byte * key, 01198 uint key_len, enum ha_rkey_function find_flag) 01199 { 01200 DBUG_ASSERT(inited==INDEX); 01201 statistic_increment(table->in_use->status_var.ha_read_key_count, 01202 &LOCK_status); 01203 int error=mi_rkey(file,buf,active_index, key, key_len, find_flag); 01204 table->status=error ? STATUS_NOT_FOUND: 0; 01205 return error; 01206 } 01207 01208 int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key, 01209 uint key_len, enum ha_rkey_function find_flag) 01210 { 01211 statistic_increment(table->in_use->status_var.ha_read_key_count, 01212 &LOCK_status); 01213 int error=mi_rkey(file,buf,index, key, key_len, find_flag); 01214 table->status=error ? STATUS_NOT_FOUND: 0; 01215 return error; 01216 } 01217 01218 int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len) 01219 { 01220 DBUG_ENTER("ha_myisam::index_read_last"); 01221 DBUG_ASSERT(inited==INDEX); 01222 statistic_increment(table->in_use->status_var.ha_read_key_count, 01223 &LOCK_status); 01224 int error=mi_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST); 01225 table->status=error ? STATUS_NOT_FOUND: 0; 01226 DBUG_RETURN(error); 01227 } 01228 01229 int ha_myisam::index_next(byte * buf) 01230 { 01231 DBUG_ASSERT(inited==INDEX); 01232 statistic_increment(table->in_use->status_var.ha_read_next_count, 01233 &LOCK_status); 01234 int error=mi_rnext(file,buf,active_index); 01235 table->status=error ? STATUS_NOT_FOUND: 0; 01236 return error; 01237 } 01238 01239 int ha_myisam::index_prev(byte * buf) 01240 { 01241 DBUG_ASSERT(inited==INDEX); 01242 statistic_increment(table->in_use->status_var.ha_read_prev_count, 01243 &LOCK_status); 01244 int error=mi_rprev(file,buf, active_index); 01245 table->status=error ? STATUS_NOT_FOUND: 0; 01246 return error; 01247 } 01248 01249 int ha_myisam::index_first(byte * buf) 01250 { 01251 DBUG_ASSERT(inited==INDEX); 01252 statistic_increment(table->in_use->status_var.ha_read_first_count, 01253 &LOCK_status); 01254 int error=mi_rfirst(file, buf, active_index); 01255 table->status=error ? STATUS_NOT_FOUND: 0; 01256 return error; 01257 } 01258 01259 int ha_myisam::index_last(byte * buf) 01260 { 01261 DBUG_ASSERT(inited==INDEX); 01262 statistic_increment(table->in_use->status_var.ha_read_last_count, 01263 &LOCK_status); 01264 int error=mi_rlast(file, buf, active_index); 01265 table->status=error ? STATUS_NOT_FOUND: 0; 01266 return error; 01267 } 01268 01269 int ha_myisam::index_next_same(byte * buf, 01270 const byte *key __attribute__((unused)), 01271 uint length __attribute__((unused))) 01272 { 01273 DBUG_ASSERT(inited==INDEX); 01274 statistic_increment(table->in_use->status_var.ha_read_next_count, 01275 &LOCK_status); 01276 int error=mi_rnext_same(file,buf); 01277 table->status=error ? STATUS_NOT_FOUND: 0; 01278 return error; 01279 } 01280 01281 01282 int ha_myisam::rnd_init(bool scan) 01283 { 01284 if (scan) 01285 return mi_scan_init(file); 01286 return mi_reset(file); // Free buffers 01287 } 01288 01289 int ha_myisam::rnd_next(byte *buf) 01290 { 01291 statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, 01292 &LOCK_status); 01293 int error=mi_scan(file, buf); 01294 table->status=error ? STATUS_NOT_FOUND: 0; 01295 return error; 01296 } 01297 01298 int ha_myisam::restart_rnd_next(byte *buf, byte *pos) 01299 { 01300 return rnd_pos(buf,pos); 01301 } 01302 01303 int ha_myisam::rnd_pos(byte * buf, byte *pos) 01304 { 01305 statistic_increment(table->in_use->status_var.ha_read_rnd_count, 01306 &LOCK_status); 01307 int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length)); 01308 table->status=error ? STATUS_NOT_FOUND: 0; 01309 return error; 01310 } 01311 01312 void ha_myisam::position(const byte* record) 01313 { 01314 my_off_t position=mi_position(file); 01315 my_store_ptr(ref, ref_length, position); 01316 } 01317 01318 void ha_myisam::info(uint flag) 01319 { 01320 MI_ISAMINFO info; 01321 char name_buff[FN_REFLEN]; 01322 01323 (void) mi_status(file,&info,flag); 01324 if (flag & HA_STATUS_VARIABLE) 01325 { 01326 stats.records = info.records; 01327 stats.deleted = info.deleted; 01328 stats.data_file_length=info.data_file_length; 01329 stats.index_file_length=info.index_file_length; 01330 stats.delete_length = info.delete_length; 01331 stats.check_time = info.check_time; 01332 stats. mean_rec_length=info.mean_reclength; 01333 } 01334 if (flag & HA_STATUS_CONST) 01335 { 01336 TABLE_SHARE *share= table->s; 01337 stats.max_data_file_length= info.max_data_file_length; 01338 stats.max_index_file_length= info.max_index_file_length; 01339 stats.create_time= info.create_time; 01340 ref_length= info.reflength; 01341 share->db_options_in_use= info.options; 01342 stats.block_size= myisam_block_size; /* record block size */ 01343 01344 /* Update share */ 01345 if (share->tmp_table == NO_TMP_TABLE) 01346 pthread_mutex_lock(&share->mutex); 01347 share->keys_in_use.set_prefix(share->keys); 01348 share->keys_in_use.intersect_extended(info.key_map); 01349 share->keys_for_keyread.intersect(share->keys_in_use); 01350 share->db_record_offset= info.record_offset; 01351 if (share->key_parts) 01352 memcpy((char*) table->key_info[0].rec_per_key, 01353 (char*) info.rec_per_key, 01354 sizeof(table->key_info[0].rec_per_key)*share->key_parts); 01355 if (share->tmp_table == NO_TMP_TABLE) 01356 pthread_mutex_unlock(&share->mutex); 01357 01358 /* 01359 Set data_file_name and index_file_name to point at the symlink value 01360 if table is symlinked (Ie; Real name is not same as generated name) 01361 */ 01362 data_file_name= index_file_name= 0; 01363 fn_format(name_buff, file->filename, "", MI_NAME_DEXT, MY_APPEND_EXT); 01364 if (strcmp(name_buff, info.data_file_name)) 01365 data_file_name=info.data_file_name; 01366 fn_format(name_buff, file->filename, "", MI_NAME_IEXT, MY_APPEND_EXT); 01367 if (strcmp(name_buff, info.index_file_name)) 01368 index_file_name=info.index_file_name; 01369 } 01370 if (flag & HA_STATUS_ERRKEY) 01371 { 01372 errkey = info.errkey; 01373 my_store_ptr(dup_ref, ref_length, info.dupp_key_pos); 01374 } 01375 if (flag & HA_STATUS_TIME) 01376 stats.update_time = info.update_time; 01377 if (flag & HA_STATUS_AUTO) 01378 stats.auto_increment_value= info.auto_increment; 01379 } 01380 01381 01382 int ha_myisam::extra(enum ha_extra_function operation) 01383 { 01384 if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD) 01385 return 0; 01386 return mi_extra(file, operation, 0); 01387 } 01388 01389 int ha_myisam::reset(void) 01390 { 01391 return mi_reset(file); 01392 } 01393 01394 /* To be used with WRITE_CACHE and EXTRA_CACHE */ 01395 01396 int ha_myisam::extra_opt(enum ha_extra_function operation, ulong cache_size) 01397 { 01398 if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE) 01399 return 0; 01400 return mi_extra(file, operation, (void*) &cache_size); 01401 } 01402 01403 int ha_myisam::delete_all_rows() 01404 { 01405 return mi_delete_all_rows(file); 01406 } 01407 01408 int ha_myisam::delete_table(const char *name) 01409 { 01410 return mi_delete_table(name); 01411 } 01412 01413 01414 int ha_myisam::external_lock(THD *thd, int lock_type) 01415 { 01416 return mi_lock_database(file, !table->s->tmp_table ? 01417 lock_type : ((lock_type == F_UNLCK) ? 01418 F_UNLCK : F_EXTRA_LCK)); 01419 } 01420 01421 THR_LOCK_DATA **ha_myisam::store_lock(THD *thd, 01422 THR_LOCK_DATA **to, 01423 enum thr_lock_type lock_type) 01424 { 01425 if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK) 01426 file->lock.type=lock_type; 01427 *to++= &file->lock; 01428 return to; 01429 } 01430 01431 void ha_myisam::update_create_info(HA_CREATE_INFO *create_info) 01432 { 01433 ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST); 01434 if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) 01435 { 01436 create_info->auto_increment_value= stats.auto_increment_value; 01437 } 01438 create_info->data_file_name=data_file_name; 01439 create_info->index_file_name=index_file_name; 01440 } 01441 01442 01443 int ha_myisam::create(const char *name, register TABLE *table_arg, 01444 HA_CREATE_INFO *info) 01445 { 01446 int error; 01447 uint i,j,recpos,minpos,fieldpos,temp_length,length, create_flags= 0; 01448 bool found_real_auto_increment=0; 01449 enum ha_base_keytype type; 01450 char buff[FN_REFLEN]; 01451 KEY *pos; 01452 MI_KEYDEF *keydef; 01453 MI_COLUMNDEF *recinfo,*recinfo_pos; 01454 HA_KEYSEG *keyseg; 01455 TABLE_SHARE *share= table_arg->s; 01456 uint options= share->db_options_in_use; 01457 DBUG_ENTER("ha_myisam::create"); 01458 01459 type=HA_KEYTYPE_BINARY; // Keep compiler happy 01460 if (!(my_multi_malloc(MYF(MY_WME), 01461 &recinfo,(share->fields*2+2)* 01462 sizeof(MI_COLUMNDEF), 01463 &keydef, share->keys*sizeof(MI_KEYDEF), 01464 &keyseg, 01465 ((share->key_parts + share->keys) * 01466 sizeof(HA_KEYSEG)), 01467 NullS))) 01468 DBUG_RETURN(HA_ERR_OUT_OF_MEM); 01469 01470 pos=table_arg->key_info; 01471 for (i=0; i < share->keys ; i++, pos++) 01472 { 01473 if (pos->flags & HA_USES_PARSER) 01474 create_flags|= HA_CREATE_RELIES_ON_SQL_LAYER; 01475 keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL)); 01476 keydef[i].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ? 01477 (pos->flags & HA_SPATIAL ? HA_KEY_ALG_RTREE : HA_KEY_ALG_BTREE) : 01478 pos->algorithm; 01479 keydef[i].block_length= pos->block_size; 01480 01481 keydef[i].seg=keyseg; 01482 keydef[i].keysegs=pos->key_parts; 01483 for (j=0 ; j < pos->key_parts ; j++) 01484 { 01485 Field *field=pos->key_part[j].field; 01486 type=field->key_type(); 01487 keydef[i].seg[j].flag=pos->key_part[j].key_part_flag; 01488 01489 if (options & HA_OPTION_PACK_KEYS || 01490 (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY | 01491 HA_SPACE_PACK_USED))) 01492 { 01493 if (pos->key_part[j].length > 8 && 01494 (type == HA_KEYTYPE_TEXT || 01495 type == HA_KEYTYPE_NUM || 01496 (type == HA_KEYTYPE_BINARY && !field->zero_pack()))) 01497 { 01498 /* No blobs here */ 01499 if (j == 0) 01500 keydef[i].flag|=HA_PACK_KEY; 01501 if (!(field->flags & ZEROFILL_FLAG) && 01502 (field->type() == MYSQL_TYPE_STRING || 01503 field->type() == MYSQL_TYPE_VAR_STRING || 01504 ((int) (pos->key_part[j].length - field->decimals())) 01505 >= 4)) 01506 keydef[i].seg[j].flag|=HA_SPACE_PACK; 01507 } 01508 else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16)) 01509 keydef[i].flag|= HA_BINARY_PACK_KEY; 01510 } 01511 keydef[i].seg[j].type= (int) type; 01512 keydef[i].seg[j].start= pos->key_part[j].offset; 01513 keydef[i].seg[j].length= pos->key_part[j].length; 01514 keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end= 01515 keydef[i].seg[j].bit_length= 0; 01516 keydef[i].seg[j].bit_pos= 0; 01517 keydef[i].seg[j].language= field->charset()->number; 01518 01519 if (field->null_ptr) 01520 { 01521 keydef[i].seg[j].null_bit=field->null_bit; 01522 keydef[i].seg[j].null_pos= (uint) (field->null_ptr- 01523 (uchar*) table_arg->record[0]); 01524 } 01525 else 01526 { 01527 keydef[i].seg[j].null_bit=0; 01528 keydef[i].seg[j].null_pos=0; 01529 } 01530 if (field->type() == FIELD_TYPE_BLOB || 01531 field->type() == FIELD_TYPE_GEOMETRY) 01532 { 01533 keydef[i].seg[j].flag|=HA_BLOB_PART; 01534 /* save number of bytes used to pack length */ 01535 keydef[i].seg[j].bit_start= (uint) (field->pack_length() - 01536 share->blob_ptr_size); 01537 } 01538 else if (field->type() == FIELD_TYPE_BIT) 01539 { 01540 keydef[i].seg[j].bit_length= ((Field_bit *) field)->bit_len; 01541 keydef[i].seg[j].bit_start= ((Field_bit *) field)->bit_ofs; 01542 keydef[i].seg[j].bit_pos= (uint) (((Field_bit *) field)->bit_ptr - 01543 (uchar*) table_arg->record[0]); 01544 } 01545 } 01546 keyseg+=pos->key_parts; 01547 } 01548 01549 if (table_arg->found_next_number_field) 01550 { 01551 keydef[share->next_number_index].flag|= HA_AUTO_KEY; 01552 found_real_auto_increment= share->next_number_key_offset == 0; 01553 } 01554 01555 recpos=0; recinfo_pos=recinfo; 01556 while (recpos < (uint) share->reclength) 01557 { 01558 Field **field,*found=0; 01559 minpos= share->reclength; 01560 length=0; 01561 01562 for (field=table_arg->field ; *field ; field++) 01563 { 01564 if ((fieldpos=(*field)->offset()) >= recpos && 01565 fieldpos <= minpos) 01566 { 01567 /* skip null fields */ 01568 if (!(temp_length= (*field)->pack_length_in_rec())) 01569 continue; /* Skip null-fields */ 01570 if (! found || fieldpos < minpos || 01571 (fieldpos == minpos && temp_length < length)) 01572 { 01573 minpos=fieldpos; found= *field; length=temp_length; 01574 } 01575 } 01576 } 01577 DBUG_PRINT("loop",("found: 0x%lx recpos: %d minpos: %d length: %d", 01578 found,recpos,minpos,length)); 01579 if (recpos != minpos) 01580 { // Reserved space (Null bits?) 01581 bzero((char*) recinfo_pos,sizeof(*recinfo_pos)); 01582 recinfo_pos->type=(int) FIELD_NORMAL; 01583 recinfo_pos++->length= (uint16) (minpos-recpos); 01584 } 01585 if (! found) 01586 break; 01587 01588 if (found->flags & BLOB_FLAG) 01589 recinfo_pos->type= (int) FIELD_BLOB; 01590 else if (found->type() == MYSQL_TYPE_VARCHAR) 01591 recinfo_pos->type= FIELD_VARCHAR; 01592 else if (!(options & HA_OPTION_PACK_RECORD)) 01593 recinfo_pos->type= (int) FIELD_NORMAL; 01594 else if (found->zero_pack()) 01595 recinfo_pos->type= (int) FIELD_SKIP_ZERO; 01596 else 01597 recinfo_pos->type= (int) ((length <= 3 || 01598 (found->flags & ZEROFILL_FLAG)) ? 01599 FIELD_NORMAL : 01600 found->type() == MYSQL_TYPE_STRING || 01601 found->type() == MYSQL_TYPE_VAR_STRING ? 01602 FIELD_SKIP_ENDSPACE : 01603 FIELD_SKIP_PRESPACE); 01604 if (found->null_ptr) 01605 { 01606 recinfo_pos->null_bit=found->null_bit; 01607 recinfo_pos->null_pos= (uint) (found->null_ptr- 01608 (uchar*) table_arg->record[0]); 01609 } 01610 else 01611 { 01612 recinfo_pos->null_bit=0; 01613 recinfo_pos->null_pos=0; 01614 } 01615 (recinfo_pos++)->length= (uint16) length; 01616 recpos=minpos+length; 01617 DBUG_PRINT("loop",("length: %d type: %d", 01618 recinfo_pos[-1].length,recinfo_pos[-1].type)); 01619 01620 } 01621 MI_CREATE_INFO create_info; 01622 bzero((char*) &create_info,sizeof(create_info)); 01623 create_info.max_rows= share->max_rows; 01624 create_info.reloc_rows= share->min_rows; 01625 create_info.with_auto_increment=found_real_auto_increment; 01626 create_info.auto_increment=(info->auto_increment_value ? 01627 info->auto_increment_value -1 : 01628 (ulonglong) 0); 01629 create_info.data_file_length= ((ulonglong) share->max_rows * 01630 share->avg_row_length); 01631 create_info.data_file_name= info->data_file_name; 01632 create_info.index_file_name= info->index_file_name; 01633 01634 if (info->options & HA_LEX_CREATE_TMP_TABLE) 01635 create_flags|= HA_CREATE_TMP_TABLE; 01636 if (options & HA_OPTION_PACK_RECORD) 01637 create_flags|= HA_PACK_RECORD; 01638 if (options & HA_OPTION_CHECKSUM) 01639 create_flags|= HA_CREATE_CHECKSUM; 01640 if (options & HA_OPTION_DELAY_KEY_WRITE) 01641 create_flags|= HA_CREATE_DELAY_KEY_WRITE; 01642 01643 /* TODO: Check that the following fn_format is really needed */ 01644 error=mi_create(fn_format(buff,name,"","",MY_UNPACK_FILENAME|MY_APPEND_EXT), 01645 share->keys,keydef, 01646 (uint) (recinfo_pos-recinfo), recinfo, 01647 0, (MI_UNIQUEDEF*) 0, 01648 &create_info, create_flags); 01649 01650 my_free((gptr) recinfo,MYF(0)); 01651 DBUG_RETURN(error); 01652 } 01653 01654 01655 int ha_myisam::rename_table(const char * from, const char * to) 01656 { 01657 return mi_rename(from,to); 01658 } 01659 01660 01661 void ha_myisam::get_auto_increment(ulonglong offset, ulonglong increment, 01662 ulonglong nb_desired_values, 01663 ulonglong *first_value, 01664 ulonglong *nb_reserved_values) 01665 { 01666 ulonglong nr; 01667 int error; 01668 byte key[MI_MAX_KEY_LENGTH]; 01669 01670 if (!table->s->next_number_key_offset) 01671 { // Autoincrement at key-start 01672 ha_myisam::info(HA_STATUS_AUTO); 01673 *first_value= stats.auto_increment_value; 01674 /* MyISAM has only table-level lock, so reserves to +inf */ 01675 *nb_reserved_values= ULONGLONG_MAX; 01676 return; 01677 } 01678 01679 /* it's safe to call the following if bulk_insert isn't on */ 01680 mi_flush_bulk_insert(file, table->s->next_number_index); 01681 01682 (void) extra(HA_EXTRA_KEYREAD); 01683 key_copy(key, table->record[0], 01684 table->key_info + table->s->next_number_index, 01685 table->s->next_number_key_offset); 01686 error= mi_rkey(file,table->record[1],(int) table->s->next_number_index, 01687 key,table->s->next_number_key_offset,HA_READ_PREFIX_LAST); 01688 if (error) 01689 nr= 1; 01690 else 01691 { 01692 /* Get data from record[1] */ 01693 nr= ((ulonglong) table->next_number_field-> 01694 val_int_offset(table->s->rec_buff_length)+1); 01695 } 01696 extra(HA_EXTRA_NO_KEYREAD); 01697 *first_value= nr; 01698 /* 01699 MySQL needs to call us for next row: assume we are inserting ("a",null) 01700 here, we return 3, and next this statement will want to insert ("b",null): 01701 there is no reason why ("b",3+1) would be the good row to insert: maybe it 01702 already exists, maybe 3+1 is too large... 01703 */ 01704 *nb_reserved_values= 1; 01705 } 01706 01707 01708 /* 01709 Find out how many rows there is in the given range 01710 01711 SYNOPSIS 01712 records_in_range() 01713 inx Index to use 01714 min_key Start of range. Null pointer if from first key 01715 max_key End of range. Null pointer if to last key 01716 01717 NOTES 01718 min_key.flag can have one of the following values: 01719 HA_READ_KEY_EXACT Include the key in the range 01720 HA_READ_AFTER_KEY Don't include key in range 01721 01722 max_key.flag can have one of the following values: 01723 HA_READ_BEFORE_KEY Don't include key in range 01724 HA_READ_AFTER_KEY Include all 'end_key' values in the range 01725 01726 RETURN 01727 HA_POS_ERROR Something is wrong with the index tree. 01728 0 There is no matching keys in the given range 01729 number > 0 There is approximately 'number' matching rows in 01730 the range. 01731 */ 01732 01733 ha_rows ha_myisam::records_in_range(uint inx, key_range *min_key, 01734 key_range *max_key) 01735 { 01736 return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key); 01737 } 01738 01739 01740 int ha_myisam::ft_read(byte * buf) 01741 { 01742 int error; 01743 01744 if (!ft_handler) 01745 return -1; 01746 01747 thread_safe_increment(table->in_use->status_var.ha_read_next_count, 01748 &LOCK_status); // why ? 01749 01750 error=ft_handler->please->read_next(ft_handler,(char*) buf); 01751 01752 table->status=error ? STATUS_NOT_FOUND: 0; 01753 return error; 01754 } 01755 01756 uint ha_myisam::checksum() const 01757 { 01758 return (uint)file->state->checksum; 01759 } 01760 01761 01762 bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info, 01763 uint table_changes) 01764 { 01765 uint options= table->s->db_options_in_use; 01766 01767 if (info->auto_increment_value != stats.auto_increment_value || 01768 info->data_file_name != data_file_name || 01769 info->index_file_name != index_file_name || 01770 table_changes == IS_EQUAL_NO || 01771 table_changes & IS_EQUAL_PACK_LENGTH) // Not implemented yet 01772 return COMPATIBLE_DATA_NO; 01773 01774 if ((options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM | 01775 HA_OPTION_DELAY_KEY_WRITE)) != 01776 (info->table_options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM | 01777 HA_OPTION_DELAY_KEY_WRITE))) 01778 return COMPATIBLE_DATA_NO; 01779 return COMPATIBLE_DATA_YES; 01780 } 01781 01782 handlerton myisam_hton; 01783 01784 static int myisam_init() 01785 { 01786 myisam_hton.state=SHOW_OPTION_YES; 01787 myisam_hton.db_type=DB_TYPE_MYISAM; 01788 myisam_hton.create=myisam_create_handler; 01789 myisam_hton.panic=mi_panic; 01790 myisam_hton.flags=HTON_CAN_RECREATE; 01791 return 0; 01792 } 01793 01794 struct st_mysql_storage_engine myisam_storage_engine= 01795 { MYSQL_HANDLERTON_INTERFACE_VERSION, &myisam_hton }; 01796 01797 mysql_declare_plugin(myisam) 01798 { 01799 MYSQL_STORAGE_ENGINE_PLUGIN, 01800 &myisam_storage_engine, 01801 "MyISAM", 01802 "MySQL AB", 01803 "Default engine as of MySQL 3.23 with great performance", 01804 myisam_init, /* Plugin Init */ 01805 NULL, /* Plugin Deinit */ 01806 0x0100, /* 1.0 */ 01807 0 01808 } 01809 mysql_declare_plugin_end; 01810
1.4.7

