22 #include "myisamdef.h"
26 #include <sys/resource.h>
29 #define FILENAME(A) (A ? A->show_name : "Unknown")
35 char *name, *show_name;
53 #define NO_FILEPOS (ulong) ~0L
55 extern int main(
int argc,
char * *argv);
56 static void get_options(
int *argc,
char ***argv);
57 static int examine_log(
char * file_name,
char **table_names);
59 static int file_info_compare(
void *cmp_arg,
void *a,
void *b);
60 static int test_if_open(
struct file_info *key,element_count count,
63 static int test_when_accessed(
struct file_info *key,element_count count,
65 static void file_info_free(
struct file_info *info);
66 static int close_some_file(
TREE *tree);
69 static void printf_log(
const char *str,...);
72 static uint verbose=0,update=0,test_info=0,max_files=0,re_open_count=0,
73 recover=0,prefix_remove=0,opt_processes=0;
74 static char *log_filename=0, *filepath=0, *write_filename=0;
75 static char *record_pos_file= 0;
76 static ulong com_count[10][3],number_of_commands=(ulong) ~0L,
78 static my_off_t isamlog_filepos,start_offset=0,record_pos= HA_OFFSET_ERROR;
79 static const char *command_name[]=
80 {
"open",
"write",
"update",
"delete",
"close",
"extra",
"lock",
"re-open",
84 int main(
int argc,
char **argv)
87 ulong total_count,total_error,total_recover;
90 log_filename=myisam_log_filename;
91 get_options(&argc,&argv);
93 max_files= (my_set_max_open_files(MY_MIN(max_files, 8)) - 6) / 2;
95 printf(
"Trying to %s MyISAM files according to log '%s'\n",
96 (recover ?
"recover" :
"update"),log_filename);
97 error= examine_log(log_filename,argv);
98 if (update && ! error)
99 puts(
"Tables updated successfully");
100 total_count=total_error=total_recover=0;
101 for (i=first=0 ; command_name[
i] ; i++)
107 if (verbose || update)
109 puts(
"Commands Used count Errors Recover errors");
111 printf(
"%-12s%9ld%10ld%17ld\n",command_name[i],com_count[i][0],
112 com_count[i][1],com_count[i][2]);
113 total_count+=com_count[
i][0];
114 total_error+=com_count[
i][1];
115 total_recover+=com_count[
i][2];
119 printf(
"%-12s%9ld%10ld%17ld\n",
"Total",total_count,total_error,
122 printf(
"Had to do %d re-open because of too few possibly open files\n",
124 (void) mi_panic(HA_PANIC_CLOSE);
125 my_free_open_file_info();
126 my_end(test_info ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
132 static void get_options(
register int *argc,
register char ***argv)
135 const char *pos,*usage;
139 usage=
"Usage: %s [-?iruvDIV] [-c #] [-f #] [-F filepath/] [-o #] [-R file recordpos] [-w write_file] [log-filename [table ...]] \n";
142 while (--*argc > 0 && *(pos = *(++*argv)) ==
'-' ) {
146 switch((option=*pos)) {
159 number_of_commands=(ulong) atol(pos);
173 max_files=(uint) atoi(pos);
187 start_offset=(my_off_t) strtoll(pos,NULL,10);
198 prefix_remove=atoi(pos);
215 record_pos_file=(
char*) pos;
218 record_pos=(my_off_t) strtoll(*(++*argv),NULL,10);
232 write_filename=(
char*) pos;
243 filepath= (
char*) pos;
251 printf(
"%s Ver 1.4 for %s at %s\n",my_progname,SYSTEM_TYPE,
253 puts(
"By Monty, for your professional use\n");
256 puts(
"Write info about whats in a MyISAM log file.");
257 printf(
"If no file name is given %s is used\n",log_filename);
259 printf(usage,my_progname);
261 puts(
"Options: -? or -I \"Info\" -V \"version\" -c \"do only # commands\"");
262 puts(
" -f \"max open files\" -F \"filepath\" -i \"extra info\"");
263 puts(
" -o \"offset\" -p # \"remove # components from path\"");
264 puts(
" -r \"recover\" -R \"file recordposition\"");
265 puts(
" -u \"update\" -v \"verbose\" -w \"write file\"");
266 puts(
" -D \"myisam compiled with DBUG\" -P \"processes\"");
267 puts(
"\nOne can give a second and a third '-v' for more verbose.");
268 puts(
"Normaly one does a update (-u).");
269 puts(
"If a recover is done all writes and all possibly updates and deletes is done\nand errors are only counted.");
270 puts(
"If one gives table names as arguments only these tables will be updated\n");
274 printf(
"illegal option: \"-%c\"\n",*pos);
287 log_filename=(
char*) pos;
293 (void) fprintf(stderr,
"option \"%c\" used without or with wrong argument\n",
299 static int examine_log(
char * file_name,
char **table_names)
301 uint command,result,files_open;
302 ulong access_time,length;
304 int lock_command,mi_result;
305 char isam_file_name[FN_REFLEN],llbuff[21],llbuff2[21];
312 enum ha_extra_function extra_command;
315 DBUG_ENTER(
"examine_log");
317 if ((file=my_open(file_name,O_RDONLY,MYF(MY_WME))) < 0)
322 if (!(write_file=my_fopen(write_filename,O_WRONLY,MYF(MY_WME))))
324 my_close(file,MYF(0));
329 init_io_cache(&cache,file,0,READ_CACHE,start_offset,0,MYF(0));
330 memset(com_count, 0,
sizeof(com_count));
331 init_tree(&tree,0,0,
sizeof(
file_info),(qsort_cmp2) file_info_compare,1,
332 (tree_element_free) file_info_free, NULL);
333 (void) init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,KEY_CACHE_SIZE,
336 files_open=0; access_time=0;
337 while (access_time++ != number_of_commands &&
338 !my_b_read(&cache,(uchar*) head,9))
340 isamlog_filepos=my_b_tell(&cache)-9L;
342 isamlog_process=
file_info.process=(long) mi_uint4korr(head+3);
345 result= mi_uint2korr(head+7);
349 curr_file_info->accessed=access_time;
350 if (update && curr_file_info->used && curr_file_info->closed)
352 if (reopen_closed_file(&tree,curr_file_info))
354 command=
sizeof(com_count)/
sizeof(com_count[0][0])/3;
360 command=(uint) head[0];
361 if (command <
sizeof(com_count)/
sizeof(com_count[0][0])/3 &&
362 (!table_names[0] || (curr_file_info && curr_file_info->used)))
364 com_count[command][0]++;
366 com_count[command][1]++;
368 switch ((
enum myisam_log_commands) command) {
372 com_count[command][0]--;
374 com_count[command][1]--;
378 printf(
"\nWarning: %s is opened with same process and filenumber\n"
379 "Maybe you should use the -P option ?\n",
380 curr_file_info->show_name);
381 if (my_b_read(&cache,(uchar*) head,2))
387 if (read_string(&cache, &buff, (uint) mi_uint2korr(head)))
394 for (pos=
file_info.name=(
char*)buff; (pos=strchr(pos,
'\\')) ; pos++)
398 for (i=0 ; i < prefix_remove ; i++)
401 if (!(next=strchr(pos,
'/')))
407 to=convert_dirname(isam_file_name,filepath,NullS);
409 fn_ext(isam_file_name)[0]=0;
413 (void) tree_walk(&tree,(tree_walk_action) test_if_open,(
void*) &open_param,
424 file_info.show_name=my_memdup(isam_file_name,
425 (uint) strlen(isam_file_name)+10,
436 for (name=table_names ; *
name ; name++)
438 if (!strcmp(*name,isam_file_name))
444 if (files_open >= max_files)
446 if (close_some_file(&tree))
450 if (!(
file_info.isam= mi_open(isam_file_name,O_RDWR,
451 HA_OPEN_WAIT_IF_LOCKED)))
459 (void) tree_insert(&tree, (uchar*) &
file_info, 0, tree.custom_arg);
462 if (verbose && !record_pos_file)
464 com_count[command][0]++;
466 com_count[command][1]++;
470 if (verbose && !record_pos_file &&
471 (!table_names[0] || (curr_file_info && curr_file_info->used)))
472 printf_log(
"%s: %s -> %d",FILENAME(curr_file_info),
473 command_name[command],result);
476 if (!curr_file_info->closed)
478 (void) tree_delete(&tree, (uchar*) curr_file_info, 0, tree.custom_arg);
482 if (my_b_read(&cache,(uchar*) head,1))
484 extra_command=(
enum ha_extra_function) head[0];
485 if (verbose && !record_pos_file &&
486 (!table_names[0] || (curr_file_info && curr_file_info->used)))
487 printf_log(
"%s: %s(%d) -> %d",FILENAME(curr_file_info),
488 command_name[command], (
int) extra_command,result);
489 if (update && curr_file_info && !curr_file_info->closed)
491 if (mi_extra(curr_file_info->isam, extra_command, 0) != (
int) result)
494 (void) fprintf(stderr,
495 "Warning: error %d, expected %d on command %s at %s\n",
496 my_errno,result,command_name[command],
497 llstr(isamlog_filepos,llbuff));
503 if (my_b_read(&cache,(uchar*) head,8))
505 filepos=mi_sizekorr(head);
506 if (verbose && (!record_pos_file ||
507 ((record_pos == filepos || record_pos == NO_FILEPOS) &&
508 !cmp_filename(curr_file_info,record_pos_file))) &&
509 (!table_names[0] || (curr_file_info && curr_file_info->used)))
510 printf_log(
"%s: %s at %ld -> %d",FILENAME(curr_file_info),
511 command_name[command],(
long) filepos,result);
512 if (update && curr_file_info && !curr_file_info->closed)
514 if (mi_rrnd(curr_file_info->isam,curr_file_info->record,filepos))
519 printf_log(
"error: Didn't find row to delete with mi_rrnd");
520 com_count[command][2]++;
522 mi_result=mi_delete(curr_file_info->isam,curr_file_info->record);
523 if ((mi_result == 0 && result) ||
524 (mi_result && (uint) my_errno != result))
529 com_count[command][2]++;
531 printf_log(
"error: Got result %d from mi_delete instead of %d",
538 if (my_b_read(&cache,(uchar*) head,12))
540 filepos=mi_sizekorr(head);
541 length=mi_uint4korr(head+8);
543 if (read_string(&cache,&buff,(uint) length))
545 if ((!record_pos_file ||
546 ((record_pos == filepos || record_pos == NO_FILEPOS) &&
547 !cmp_filename(curr_file_info,record_pos_file))) &&
548 (!table_names[0] || (curr_file_info && curr_file_info->used)))
551 (my_fwrite(write_file,buff,length,MYF(MY_WAIT_IF_FULL | MY_NABP))))
554 printf_log(
"%s: %s at %ld, length=%ld -> %d",
555 FILENAME(curr_file_info),
556 command_name[command], filepos,length,result);
558 if (update && curr_file_info && !curr_file_info->closed)
560 if (curr_file_info->isam->s->base.blobs)
561 fix_blob_pointers(curr_file_info->isam,buff);
562 if ((
enum myisam_log_commands) command == MI_LOG_UPDATE)
564 if (mi_rrnd(curr_file_info->isam,curr_file_info->record,filepos))
572 printf_log(
"error: Didn't find row to update with mi_rrnd");
573 if (recover == 1 || result ||
574 find_record_with_key(curr_file_info,buff))
576 com_count[command][2]++;
580 mi_result=mi_update(curr_file_info->isam,curr_file_info->record,
582 if ((mi_result == 0 && result) ||
583 (mi_result && (uint) my_errno != result))
588 printf_log(
"error: Got result %d from mi_update instead of %d",
591 com_count[command][2]++;
596 mi_result=mi_write(curr_file_info->isam,buff);
597 if ((mi_result == 0 && result) ||
598 (mi_result && (uint) my_errno != result))
603 printf_log(
"error: Got result %d from mi_write instead of %d",
606 com_count[command][2]++;
608 if (!recover && filepos != curr_file_info->isam->lastpos)
610 printf(
"error: Wrote at position: %s, should have been %s",
611 llstr(curr_file_info->isam->lastpos,llbuff),
612 llstr(filepos,llbuff2));
620 if (my_b_read(&cache,(uchar*) head,
sizeof(lock_command)))
622 memcpy(&lock_command, head,
sizeof(lock_command));
623 if (verbose && !record_pos_file &&
624 (!table_names[0] || (curr_file_info && curr_file_info->used)))
625 printf_log(
"%s: %s(%d) -> %d\n",FILENAME(curr_file_info),
626 command_name[command],lock_command,result);
627 if (update && curr_file_info && !curr_file_info->closed)
629 if (mi_lock_database(curr_file_info->isam,lock_command) !=
634 case MI_LOG_DELETE_ALL:
635 if (verbose && !record_pos_file &&
636 (!table_names[0] || (curr_file_info && curr_file_info->used)))
637 printf_log(
"%s: %s -> %d\n",FILENAME(curr_file_info),
638 command_name[command],result);
642 (void) fprintf(stderr,
643 "Error: found unknown command %d in logfile, aborted\n",
649 end_key_cache(dflt_key_cache,1);
651 (void) end_io_cache(&cache);
652 (void) my_close(file,MYF(0));
653 if (write_file && my_fclose(write_file,MYF(MY_WME)))
659 (void) fprintf(stderr,
"Got error %d when reading from logfile\n",my_errno);
664 (void) fprintf(stderr,
"Got error %d, expected %d on command %s at %s\n",
665 my_errno,result,command_name[command],
666 llstr(isamlog_filepos,llbuff));
669 end_key_cache(dflt_key_cache, 1);
671 (void) end_io_cache(&cache);
672 (void) my_close(file,MYF(0));
674 (void) my_fclose(write_file,MYF(MY_WME));
679 static int read_string(
IO_CACHE *file,
register uchar* *to,
register uint length)
681 DBUG_ENTER(
"read_string");
685 if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
686 my_b_read(file,(uchar*) *to,length))
693 *((uchar*) *to+length)=
'\0';
698 static int file_info_compare(
void* cmp_arg __attribute__((unused)),
703 if ((lint=((
struct file_info*) a)->process -
705 return lint < 0L ? -1 : 1;
711 static int test_if_open (
struct file_info *key,
712 element_count count __attribute__((unused)),
715 if (!strcmp(key->name,param->name) && key->id > param->max_id)
716 param->max_id=key->id;
726 pos=record+info->s->base.reclength;
727 for (end=info->blobs+info->s->base.blobs, blob= info->blobs;
731 memcpy(record+blob->offset+blob->pack_length, &pos,
sizeof(
char*));
732 pos+=_mi_calc_blob_length(blob->pack_length,record+blob->offset);
739 static int test_when_accessed (
struct file_info *key,
740 element_count count __attribute__((unused)),
743 if (key->accessed < access_param->min_accessed && ! key->closed)
745 access_param->min_accessed=key->accessed;
746 access_param->found=key;
754 DBUG_ENTER(
"file_info_free");
757 if (!fileinfo->closed)
758 (void) mi_close(fileinfo->isam);
759 if (fileinfo->record)
760 my_free(fileinfo->record);
762 my_free(fileinfo->name);
763 my_free(fileinfo->show_name);
769 static int close_some_file(
TREE *tree)
773 access_param.min_accessed=LONG_MAX;
774 access_param.found=0;
776 (void) tree_walk(tree,(tree_walk_action) test_when_accessed,
777 (
void*) &access_param,left_root_right);
778 if (!access_param.found)
780 if (mi_close(access_param.found->isam))
782 access_param.found->closed=1;
787 static int reopen_closed_file(
TREE *tree,
struct file_info *fileinfo)
789 char name[FN_REFLEN];
790 if (close_some_file(tree))
792 strmov(name,fileinfo->show_name);
793 if (fileinfo->id > 1)
794 *strrchr(name,
'<')=
'\0';
796 if (!(fileinfo->isam= mi_open(name,O_RDWR,HA_OPEN_WAIT_IF_LOCKED)))
809 uchar tmp_key[MI_MAX_KEY_BUFF];
811 for (key=0 ; key < info->s->base.keys ; key++)
813 if (mi_is_key_active(info->s->state.key_map, key) &&
814 info->s->keyinfo[key].flag & HA_NOSAME)
816 (void) _mi_make_key(info,key,tmp_key,record,0L);
817 return mi_rkey(info,file_info->record,(
int) key,tmp_key,0,
825 static void printf_log(
const char *format,...)
829 va_start(args,format);
831 printf(
"%9s:",llstr(isamlog_filepos,llbuff));
833 printf(
"%5ld ",isamlog_process);
834 (void) vprintf((
char*) format,args);
840 static my_bool cmp_filename(
struct file_info *file_info,
char * name)
844 return strcmp(file_info->name,name) ? 1 : 0;
847 #include "mi_extrafunc.h"