1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /* write whats in isam.log */
24 
25 #ifndef USE_MY_FUNC
26 #define USE_MY_FUNC
27 #endif
28 
29 #include "myisamdef.h"
30 #include <my_tree.h>
31 #include <stdarg.h>
32 #ifdef HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
34 #endif
35 
36 #define FILENAME(A) (A ? A->show_name : "Unknown")
37 
38 struct file_info {
39   long process;
40   int  filenr,id;
41   uint rnd;
42   char *name, *show_name;
43   uchar *record;
44   MI_INFO *isam;
45   my_bool closed, used;
46   ulong accessed;
47 };
48 
49 struct test_if_open_param {
50   char * name;
51   int max_id;
52 };
53 
54 struct st_access_param
55 {
56   ulong min_accessed;
57   struct file_info *found;
58 };
59 
60 #define NO_FILEPOS (ulong) ~0L
61 
62 extern int main(int argc,char * *argv);
63 static void get_options(int *argc,char ***argv);
64 static int examine_log(char * file_name,char **table_names);
65 static int read_string(IO_CACHE *file,uchar* *to,uint length);
66 static int file_info_compare(void *cmp_arg, void *a,void *b);
67 static int test_if_open(struct file_info *key,element_count count,
68 			struct test_if_open_param *param);
69 static void fix_blob_pointers(MI_INFO *isam,uchar *record);
70 static int test_when_accessed(struct file_info *key,element_count count,
71 			      struct st_access_param *access_param);
72 static void file_info_free(struct file_info *info);
73 static int close_some_file(TREE *tree);
74 static int reopen_closed_file(TREE *tree,struct file_info *file_info);
75 static int find_record_with_key(struct file_info *file_info,uchar *record);
76 static void printf_log(const char *str,...);
77 static my_bool cmp_filename(struct file_info *file_info,char * name);
78 
79 static uint verbose=0,update=0,test_info=0,max_files=0,re_open_count=0,
80   recover=0,prefix_remove=0,opt_processes=0;
81 static char *log_filename=0, *filepath=0, *write_filename=0;
82 static char *record_pos_file= 0;
83 static ulong com_count[10][3],number_of_commands=(ulong) ~0L,
84 	     isamlog_process;
85 static my_off_t isamlog_filepos,start_offset=0,record_pos= HA_OFFSET_ERROR;
86 static const char *command_name[]=
87 {"open","write","update","delete","close","extra","lock","re-open",
88  "delete-all", NullS};
89 
90 
keycache_thread_var()91 extern st_keycache_thread_var *keycache_thread_var()
92 {
93   return &main_thread_keycache_var;
94 }
95 
96 
main(int argc,char ** argv)97 int main(int argc, char **argv)
98 {
99   int error,i,first;
100   ulong total_count,total_error,total_recover;
101   MY_INIT(argv[0]);
102 
103   memset(&main_thread_keycache_var, 0, sizeof(st_keycache_thread_var));
104   mysql_cond_init(PSI_NOT_INSTRUMENTED,
105                   &main_thread_keycache_var.suspend);
106 
107   log_filename=myisam_log_filename;
108   get_options(&argc,&argv);
109   /* Number of MyISAM files we can have open at one time */
110   max_files= (my_set_max_open_files(MY_MIN(max_files, 8)) - 6) / 2;
111   if (update)
112     printf("Trying to %s MyISAM files according to log '%s'\n",
113 	   (recover ? "recover" : "update"),log_filename);
114   error= examine_log(log_filename,argv);
115   if (update && ! error)
116     puts("Tables updated successfully");
117   total_count=total_error=total_recover=0;
118   for (i=first=0 ; command_name[i] ; i++)
119   {
120     if (com_count[i][0])
121     {
122       if (!first++)
123       {
124 	if (verbose || update)
125 	  puts("");
126 	puts("Commands   Used count    Errors   Recover errors");
127       }
128       printf("%-12s%9ld%10ld%17ld\n",command_name[i],com_count[i][0],
129 	     com_count[i][1],com_count[i][2]);
130       total_count+=com_count[i][0];
131       total_error+=com_count[i][1];
132       total_recover+=com_count[i][2];
133     }
134   }
135   if (total_count)
136     printf("%-12s%9ld%10ld%17ld\n","Total",total_count,total_error,
137 	   total_recover);
138   if (re_open_count)
139     printf("Had to do %d re-open because of too few possibly open files\n",
140 	   re_open_count);
141   (void) mi_panic(HA_PANIC_CLOSE);
142   my_free_open_file_info();
143   my_end(test_info ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
144   mysql_cond_destroy(&main_thread_keycache_var.suspend);
145   exit(error);
146   return 0;				/* No compiler warning */
147 } /* main */
148 
149 
get_options(int * argc,char *** argv)150 static void get_options(int *argc, char ***argv)
151 {
152   int help,version;
153   const char *pos,*usage;
154   char option;
155 
156   help=0;
157   usage="Usage: %s [-?iruvDIV] [-c #] [-f #] [-F filepath/] [-o #] [-R file recordpos] [-w write_file] [log-filename [table ...]] \n";
158   pos="";
159 
160   while (--*argc > 0 && *(pos = *(++*argv)) == '-' ) {
161     while (*++pos)
162     {
163       version=0;
164       switch((option=*pos)) {
165       case '#':
166 	DBUG_PUSH (++pos);
167 	pos=" ";				/* Skip rest of arg */
168 	break;
169       case 'c':
170 	if (! *++pos)
171 	{
172 	  if (!--*argc)
173 	    goto err;
174 	  else
175 	    pos= *(++*argv);
176 	}
177 	number_of_commands=(ulong) atol(pos);
178 	pos=" ";
179 	break;
180       case 'u':
181 	update=1;
182 	break;
183       case 'f':
184 	if (! *++pos)
185 	{
186 	  if (!--*argc)
187 	    goto err;
188 	  else
189 	    pos= *(++*argv);
190 	}
191 	max_files=(uint) atoi(pos);
192 	pos=" ";
193 	break;
194       case 'i':
195 	test_info=1;
196 	break;
197       case 'o':
198 	if (! *++pos)
199 	{
200 	  if (!--*argc)
201 	    goto err;
202 	  else
203 	    pos= *(++*argv);
204 	}
205 	start_offset=(my_off_t) my_strtoll(pos,NULL,10);
206 	pos=" ";
207 	break;
208       case 'p':
209 	if (! *++pos)
210 	{
211 	  if (!--*argc)
212 	    goto err;
213 	  else
214 	    pos= *(++*argv);
215 	}
216 	prefix_remove=atoi(pos);
217 	break;
218       case 'r':
219 	update=1;
220 	recover++;
221 	break;
222       case 'P':
223 	opt_processes=1;
224 	break;
225       case 'R':
226 	if (! *++pos)
227 	{
228 	  if (!--*argc)
229 	    goto err;
230 	  else
231 	    pos= *(++*argv);
232 	}
233 	record_pos_file=(char*) pos;
234 	if (!--*argc)
235 	  goto err;
236 	record_pos=(my_off_t) my_strtoll(*(++*argv),NULL,10);
237 	pos=" ";
238 	break;
239       case 'v':
240 	verbose++;
241 	break;
242       case 'w':
243 	if (! *++pos)
244 	{
245 	  if (!--*argc)
246 	    goto err;
247 	  else
248 	    pos= *(++*argv);
249 	}
250 	write_filename=(char*) pos;
251 	pos=" ";
252 	break;
253       case 'F':
254 	if (! *++pos)
255 	{
256 	  if (!--*argc)
257 	    goto err;
258 	  else
259 	    pos= *(++*argv);
260 	}
261 	filepath= (char*) pos;
262 	pos=" ";
263 	break;
264       case 'V':
265 	version=1;
266 	/* Fall through */
267       case 'I':
268       case '?':
269 	printf("%s  Ver 1.4 for %s at %s\n",my_progname,SYSTEM_TYPE,
270 	       MACHINE_TYPE);
271 	puts("By Monty, for your professional use\n");
272 	if (version)
273 	  break;
274 	puts("Write info about whats in a MyISAM log file.");
275 	printf("If no file name is given %s is used\n",log_filename);
276 	puts("");
277 	printf(usage,my_progname);
278 	puts("");
279 	puts("Options: -? or -I \"Info\"     -V \"version\"   -c \"do only # commands\"");
280 	puts("         -f \"max open files\" -F \"filepath\"  -i \"extra info\"");
281 	puts("         -o \"offset\"         -p # \"remove # components from path\"");
282 	puts("         -r \"recover\"        -R \"file recordposition\"");
283 	puts("         -u \"update\"         -v \"verbose\"   -w \"write file\"");
284 	puts("         -D \"myisam compiled with DBUG\"   -P \"processes\"");
285 	puts("\nOne can give a second and a third '-v' for more verbose.");
286 	puts("Normaly one does a update (-u).");
287 	puts("If a recover is done all writes and all possibly updates and deletes is done\nand errors are only counted.");
288 	puts("If one gives table names as arguments only these tables will be updated\n");
289 	help=1;
290 	break;
291       default:
292 	printf("illegal option: \"-%c\"\n",*pos);
293 	break;
294       }
295     }
296   }
297   if (! *argc)
298   {
299     if (help)
300     exit(0);
301     (*argv)++;
302   }
303   if (*argc >= 1)
304   {
305     log_filename=(char*) pos;
306     (*argc)--;
307     (*argv)++;
308   }
309   return;
310  err:
311   (void) fprintf(stderr,"option \"%c\" used without or with wrong argument\n",
312 	       option);
313   exit(1);
314 }
315 
316 
examine_log(char * file_name,char ** table_names)317 static int examine_log(char * file_name, char **table_names)
318 {
319   uint command,result,files_open;
320   ulong access_time,length;
321   my_off_t filepos;
322   int lock_command,mi_result;
323   char isam_file_name[FN_REFLEN],llbuff[21],llbuff2[21];
324   uchar head[20];
325   uchar*	buff;
326   struct test_if_open_param open_param;
327   IO_CACHE cache;
328   File file;
329   FILE *write_file;
330   enum ha_extra_function extra_command;
331   TREE tree;
332   struct file_info file_info,*curr_file_info;
333   DBUG_ENTER("examine_log");
334 
335   if ((file=my_open(file_name,O_RDONLY,MYF(MY_WME))) < 0)
336     DBUG_RETURN(1);
337   write_file=0;
338   if (write_filename)
339   {
340     if (!(write_file=my_fopen(write_filename,O_WRONLY,MYF(MY_WME))))
341     {
342       my_close(file,MYF(0));
343       DBUG_RETURN(1);
344     }
345   }
346 
347   init_io_cache(&cache,file,0,READ_CACHE,start_offset,0,MYF(0));
348   memset(com_count, 0, sizeof(com_count));
349   init_tree(&tree,0,0,sizeof(file_info),(qsort_cmp2) file_info_compare,1,
350 	    (tree_element_free) file_info_free, NULL);
351   (void) init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,KEY_CACHE_SIZE,
352                       0, 0);
353 
354   files_open=0; access_time=0;
355   while (access_time++ != number_of_commands &&
356 	 !my_b_read(&cache,(uchar*) head,9))
357   {
358     isamlog_filepos=my_b_tell(&cache)-9L;
359     file_info.filenr= mi_uint2korr(head+1);
360     isamlog_process=file_info.process=(long) mi_uint4korr(head+3);
361     if (!opt_processes)
362       file_info.process=0;
363     result= mi_uint2korr(head+7);
364     if ((curr_file_info=(struct file_info*) tree_search(&tree, &file_info,
365 							tree.custom_arg)))
366     {
367       curr_file_info->accessed=access_time;
368       if (update && curr_file_info->used && curr_file_info->closed)
369       {
370 	if (reopen_closed_file(&tree,curr_file_info))
371 	{
372 	  command=sizeof(com_count)/sizeof(com_count[0][0])/3;
373 	  result=0;
374 	  goto com_err;
375 	}
376       }
377     }
378     command=(uint) head[0];
379     if (command < sizeof(com_count)/sizeof(com_count[0][0])/3 &&
380 	(!table_names[0] || (curr_file_info && curr_file_info->used)))
381     {
382       com_count[command][0]++;
383       if (result)
384 	com_count[command][1]++;
385     }
386     switch ((enum myisam_log_commands) command) {
387     case MI_LOG_OPEN:
388       if (!table_names[0])
389       {
390 	com_count[command][0]--;		/* Must be counted explicite */
391 	if (result)
392 	  com_count[command][1]--;
393       }
394 
395       if (curr_file_info)
396 	printf("\nWarning: %s is opened with same process and filenumber\n"
397                "Maybe you should use the -P option ?\n",
398 	       curr_file_info->show_name);
399       if (my_b_read(&cache,(uchar*) head,2))
400 	goto err;
401       buff= 0;
402       file_info.name=0;
403       file_info.show_name=0;
404       file_info.record=0;
405       if (read_string(&cache, &buff, (uint) mi_uint2korr(head)))
406 	goto err;
407       {
408 	uint i;
409 	char *pos,*to;
410 
411 	/* Fix if old DOS files to new format */
412 	for (pos=file_info.name=(char*)buff; (pos=strchr(pos,'\\')) ; pos++)
413 	  *pos= '/';
414 
415 	pos=file_info.name;
416 	for (i=0 ; i < prefix_remove ; i++)
417 	{
418 	  char *next;
419 	  if (!(next=strchr(pos,'/')))
420 	    break;
421 	  pos=next+1;
422 	}
423 	to=isam_file_name;
424 	if (filepath)
425 	  to=convert_dirname(isam_file_name,filepath,NullS);
426 	my_stpcpy(to,pos);
427 	fn_ext(isam_file_name)[0]=0;	/* Remove extension */
428       }
429       open_param.name=file_info.name;
430       open_param.max_id=0;
431       (void) tree_walk(&tree,(tree_walk_action) test_if_open,(void*) &open_param,
432 		     left_root_right);
433       file_info.id=open_param.max_id+1;
434       /*
435        * In the line below +10 is added to accomodate '<' and '>' chars
436        * plus '\0' at the end, so that there is place for 7 digits.
437        * It is  improbable that same table can have that many entries in
438        * the table cache.
439        * The additional space is needed for the sprintf commands two lines
440        * below.
441        */
442       file_info.show_name=my_memdup(PSI_NOT_INSTRUMENTED,
443                                     isam_file_name,
444 				    (uint) strlen(isam_file_name)+10,
445 				    MYF(MY_WME));
446       if (file_info.id > 1)
447 	sprintf(strend(file_info.show_name),"<%d>",file_info.id);
448       file_info.closed=1;
449       file_info.accessed=access_time;
450       file_info.used=1;
451       if (table_names[0])
452       {
453 	char **name;
454 	file_info.used=0;
455 	for (name=table_names ; *name ; name++)
456 	{
457 	  if (!strcmp(*name,isam_file_name))
458 	    file_info.used=1;			/* Update/log only this */
459 	}
460       }
461       if (update && file_info.used)
462       {
463 	if (files_open >= max_files)
464 	{
465 	  if (close_some_file(&tree))
466 	    goto com_err;
467 	  files_open--;
468 	}
469 	if (!(file_info.isam= mi_open(isam_file_name,O_RDWR,
470 				      HA_OPEN_WAIT_IF_LOCKED)))
471 	  goto com_err;
472 	if (!(file_info.record=my_malloc(PSI_NOT_INSTRUMENTED,
473                                          file_info.isam->s->base.reclength,
474 					 MYF(MY_WME))))
475 	  goto end;
476 	files_open++;
477 	file_info.closed=0;
478       }
479       (void) tree_insert(&tree, (uchar*) &file_info, 0, tree.custom_arg);
480       if (file_info.used)
481       {
482 	if (verbose && !record_pos_file)
483 	  printf_log("%s: open -> %d",file_info.show_name, file_info.filenr);
484 	com_count[command][0]++;
485 	if (result)
486 	  com_count[command][1]++;
487       }
488       break;
489     case MI_LOG_CLOSE:
490       if (verbose && !record_pos_file &&
491 	  (!table_names[0] || (curr_file_info && curr_file_info->used)))
492 	printf_log("%s: %s -> %d",FILENAME(curr_file_info),
493 	       command_name[command],result);
494       if (curr_file_info)
495       {
496 	if (!curr_file_info->closed)
497 	  files_open--;
498         (void) tree_delete(&tree, (uchar*) curr_file_info, 0, tree.custom_arg);
499       }
500       break;
501     case MI_LOG_EXTRA:
502       if (my_b_read(&cache,(uchar*) head,1))
503 	goto err;
504       extra_command=(enum ha_extra_function) head[0];
505       if (verbose && !record_pos_file &&
506 	  (!table_names[0] || (curr_file_info && curr_file_info->used)))
507 	printf_log("%s: %s(%d) -> %d",FILENAME(curr_file_info),
508 		   command_name[command], (int) extra_command,result);
509       if (update && curr_file_info && !curr_file_info->closed)
510       {
511 	if (mi_extra(curr_file_info->isam, extra_command, 0) != (int) result)
512 	{
513 	  fflush(stdout);
514 	  (void) fprintf(stderr,
515                          "Warning: error %d, expected %d on command %s at %s\n",
516                          my_errno(),result,command_name[command],
517                          llstr(isamlog_filepos,llbuff));
518 	  fflush(stderr);
519 	}
520       }
521       break;
522     case MI_LOG_DELETE:
523       if (my_b_read(&cache,(uchar*) head,8))
524 	goto err;
525       filepos=mi_sizekorr(head);
526       if (verbose && (!record_pos_file ||
527 		      ((record_pos == filepos || record_pos == NO_FILEPOS) &&
528 		       !cmp_filename(curr_file_info,record_pos_file))) &&
529 	  (!table_names[0] || (curr_file_info && curr_file_info->used)))
530 	printf_log("%s: %s at %ld -> %d",FILENAME(curr_file_info),
531 		   command_name[command],(long) filepos,result);
532       if (update && curr_file_info && !curr_file_info->closed)
533       {
534 	if (mi_rrnd(curr_file_info->isam,curr_file_info->record,filepos))
535 	{
536 	  if (!recover)
537 	    goto com_err;
538 	  if (verbose)
539 	    printf_log("error: Didn't find row to delete with mi_rrnd");
540 	  com_count[command][2]++;		/* Mark error */
541 	}
542 	mi_result=mi_delete(curr_file_info->isam,curr_file_info->record);
543 	if ((mi_result == 0 && result) ||
544 	    (mi_result && (uint) my_errno() != result))
545 	{
546 	  if (!recover)
547 	    goto com_err;
548 	  if (mi_result)
549 	    com_count[command][2]++;		/* Mark error */
550 	  if (verbose)
551 	    printf_log("error: Got result %d from mi_delete instead of %d",
552 		       mi_result, result);
553 	}
554       }
555       break;
556     case MI_LOG_WRITE:
557     case MI_LOG_UPDATE:
558       if (my_b_read(&cache,(uchar*) head,12))
559 	goto err;
560       filepos=mi_sizekorr(head);
561       length=mi_uint4korr(head+8);
562       buff=0;
563       if (read_string(&cache,&buff,(uint) length))
564 	goto err;
565       if ((!record_pos_file ||
566 	  ((record_pos == filepos || record_pos == NO_FILEPOS) &&
567 	   !cmp_filename(curr_file_info,record_pos_file))) &&
568 	  (!table_names[0] || (curr_file_info && curr_file_info->used)))
569       {
570 	if (write_file &&
571 	    (my_fwrite(write_file,buff,length,MYF(MY_WAIT_IF_FULL | MY_NABP))))
572 	  goto end;
573 	if (verbose)
574 	  printf_log("%s: %s at %ld, length=%ld -> %d",
575 		     FILENAME(curr_file_info),
576 		     command_name[command], filepos,length,result);
577       }
578       if (update && curr_file_info && !curr_file_info->closed)
579       {
580 	if (curr_file_info->isam->s->base.blobs)
581 	  fix_blob_pointers(curr_file_info->isam,buff);
582 	if ((enum myisam_log_commands) command == MI_LOG_UPDATE)
583 	{
584 	  if (mi_rrnd(curr_file_info->isam,curr_file_info->record,filepos))
585 	  {
586 	    if (!recover)
587 	    {
588 	      result=0;
589 	      goto com_err;
590 	    }
591 	    if (verbose)
592 	      printf_log("error: Didn't find row to update with mi_rrnd");
593 	    if (recover == 1 || result ||
594 		find_record_with_key(curr_file_info,buff))
595 	    {
596 	      com_count[command][2]++;		/* Mark error */
597 	      break;
598 	    }
599 	  }
600 	  mi_result=mi_update(curr_file_info->isam,curr_file_info->record,
601 			      buff);
602 	  if ((mi_result == 0 && result) ||
603 	      (mi_result && (uint) my_errno() != result))
604 	  {
605 	    if (!recover)
606 	      goto com_err;
607 	    if (verbose)
608 	      printf_log("error: Got result %d from mi_update instead of %d",
609 			 mi_result, result);
610 	    if (mi_result)
611 	      com_count[command][2]++;		/* Mark error */
612 	  }
613 	}
614 	else
615 	{
616 	  mi_result=mi_write(curr_file_info->isam,buff);
617 	  if ((mi_result == 0 && result) ||
618 	      (mi_result && (uint) my_errno() != result))
619 	  {
620 	    if (!recover)
621 	      goto com_err;
622 	    if (verbose)
623 	      printf_log("error: Got result %d from mi_write instead of %d",
624 			 mi_result, result);
625 	    if (mi_result)
626 	      com_count[command][2]++;		/* Mark error */
627 	  }
628 	  if (!recover && filepos != curr_file_info->isam->lastpos)
629 	  {
630 	    printf("error: Wrote at position: %s, should have been %s",
631 		   llstr(curr_file_info->isam->lastpos,llbuff),
632 		   llstr(filepos,llbuff2));
633 	    goto end;
634 	  }
635 	}
636       }
637       my_free(buff);
638       break;
639     case MI_LOG_LOCK:
640       if (my_b_read(&cache,(uchar*) head,sizeof(lock_command)))
641 	goto err;
642       memcpy(&lock_command, head, sizeof(lock_command));
643       if (verbose && !record_pos_file &&
644 	  (!table_names[0] || (curr_file_info && curr_file_info->used)))
645 	printf_log("%s: %s(%d) -> %d\n",FILENAME(curr_file_info),
646 		   command_name[command],lock_command,result);
647       if (update && curr_file_info && !curr_file_info->closed)
648       {
649 	if (mi_lock_database(curr_file_info->isam,lock_command) !=
650 	    (int) result)
651 	  goto com_err;
652       }
653       break;
654     case MI_LOG_DELETE_ALL:
655       if (verbose && !record_pos_file &&
656 	  (!table_names[0] || (curr_file_info && curr_file_info->used)))
657 	printf_log("%s: %s -> %d\n",FILENAME(curr_file_info),
658 		   command_name[command],result);
659       break;
660     default:
661       fflush(stdout);
662       (void) fprintf(stderr,
663 		   "Error: found unknown command %d in logfile, aborted\n",
664 		   command);
665       fflush(stderr);
666       goto end;
667     }
668   }
669   end_key_cache(dflt_key_cache,1);
670   delete_tree(&tree);
671   (void) end_io_cache(&cache);
672   (void) my_close(file,MYF(0));
673   if (write_file && my_fclose(write_file,MYF(MY_WME)))
674     DBUG_RETURN(1);
675   DBUG_RETURN(0);
676 
677  err:
678   fflush(stdout);
679   (void) fprintf(stderr,"Got error %d when reading from logfile\n",my_errno());
680   fflush(stderr);
681   goto end;
682  com_err:
683   fflush(stdout);
684   (void) fprintf(stderr,"Got error %d, expected %d on command %s at %s\n",
685                  my_errno(),result,command_name[command],
686                  llstr(isamlog_filepos,llbuff));
687   fflush(stderr);
688  end:
689   end_key_cache(dflt_key_cache, 1);
690   delete_tree(&tree);
691   (void) end_io_cache(&cache);
692   (void) my_close(file,MYF(0));
693   if (write_file)
694     (void) my_fclose(write_file,MYF(MY_WME));
695   DBUG_RETURN(1);
696 }
697 
698 
read_string(IO_CACHE * file,uchar ** to,uint length)699 static int read_string(IO_CACHE *file, uchar* *to, uint length)
700 {
701   DBUG_ENTER("read_string");
702 
703   if (*to)
704     my_free(*to);
705   if (!(*to= (uchar*) my_malloc(PSI_NOT_INSTRUMENTED,
706                                 length+1,MYF(MY_WME))) ||
707       my_b_read(file,(uchar*) *to,length))
708   {
709     if (*to)
710       my_free(*to);
711     *to= 0;
712     DBUG_RETURN(1);
713   }
714   *((uchar*) *to+length)= '\0';
715   DBUG_RETURN (0);
716 }				/* read_string */
717 
718 
file_info_compare(void * cmp_arg MY_ATTRIBUTE ((unused)),void * a,void * b)719 static int file_info_compare(void* cmp_arg MY_ATTRIBUTE((unused)),
720 			     void *a, void *b)
721 {
722   long lint;
723 
724   if ((lint=((struct file_info*) a)->process -
725        ((struct file_info*) b)->process))
726     return lint < 0L ? -1 : 1;
727   return ((struct file_info*) a)->filenr - ((struct file_info*) b)->filenr;
728 }
729 
730 	/* ARGSUSED */
731 
test_if_open(struct file_info * key,element_count count MY_ATTRIBUTE ((unused)),struct test_if_open_param * param)732 static int test_if_open (struct file_info *key,
733 			 element_count count MY_ATTRIBUTE((unused)),
734 			 struct test_if_open_param *param)
735 {
736   if (!strcmp(key->name,param->name) && key->id > param->max_id)
737     param->max_id=key->id;
738   return 0;
739 }
740 
741 
fix_blob_pointers(MI_INFO * info,uchar * record)742 static void fix_blob_pointers(MI_INFO *info, uchar *record)
743 {
744   uchar *pos;
745   MI_BLOB *blob,*end;
746 
747   pos=record+info->s->base.reclength;
748   for (end=info->blobs+info->s->base.blobs, blob= info->blobs;
749        blob != end ;
750        blob++)
751   {
752     memcpy(record+blob->offset+blob->pack_length, &pos, sizeof(char*));
753     pos+=_mi_calc_blob_length(blob->pack_length,record+blob->offset);
754   }
755 }
756 
757 	/* close the file with hasn't been accessed for the longest time */
758 	/* ARGSUSED */
759 
test_when_accessed(struct file_info * key,element_count count MY_ATTRIBUTE ((unused)),struct st_access_param * access_param)760 static int test_when_accessed (struct file_info *key,
761 			       element_count count MY_ATTRIBUTE((unused)),
762 			       struct st_access_param *access_param)
763 {
764   if (key->accessed < access_param->min_accessed && ! key->closed)
765   {
766     access_param->min_accessed=key->accessed;
767     access_param->found=key;
768   }
769   return 0;
770 }
771 
772 
file_info_free(struct file_info * fileinfo)773 static void file_info_free(struct file_info *fileinfo)
774 {
775   DBUG_ENTER("file_info_free");
776   if (update)
777   {
778     if (!fileinfo->closed)
779       (void) mi_close(fileinfo->isam);
780     if (fileinfo->record)
781       my_free(fileinfo->record);
782   }
783   my_free(fileinfo->name);
784   my_free(fileinfo->show_name);
785   DBUG_VOID_RETURN;
786 }
787 
788 
789 
close_some_file(TREE * tree)790 static int close_some_file(TREE *tree)
791 {
792   struct st_access_param access_param;
793 
794   access_param.min_accessed=LONG_MAX;
795   access_param.found=0;
796 
797   (void) tree_walk(tree,(tree_walk_action) test_when_accessed,
798 		 (void*) &access_param,left_root_right);
799   if (!access_param.found)
800     return 1;			/* No open file that is possibly to close */
801   if (mi_close(access_param.found->isam))
802     return 1;
803   access_param.found->closed=1;
804   return 0;
805 }
806 
807 
reopen_closed_file(TREE * tree,struct file_info * fileinfo)808 static int reopen_closed_file(TREE *tree, struct file_info *fileinfo)
809 {
810   char name[FN_REFLEN];
811   if (close_some_file(tree))
812     return 1;				/* No file to close */
813   my_stpcpy(name,fileinfo->show_name);
814   if (fileinfo->id > 1)
815     *strrchr(name,'<')='\0';		/* Remove "<id>" */
816 
817   if (!(fileinfo->isam= mi_open(name,O_RDWR,HA_OPEN_WAIT_IF_LOCKED)))
818     return 1;
819   fileinfo->closed=0;
820   re_open_count++;
821   return 0;
822 }
823 
824 	/* Try to find record with uniq key */
825 
find_record_with_key(struct file_info * file_info,uchar * record)826 static int find_record_with_key(struct file_info *file_info, uchar *record)
827 {
828   uint key;
829   MI_INFO *info=file_info->isam;
830   uchar tmp_key[MI_MAX_KEY_BUFF];
831 
832   for (key=0 ; key < info->s->base.keys ; key++)
833   {
834     if (mi_is_key_active(info->s->state.key_map, key) &&
835 	info->s->keyinfo[key].flag & HA_NOSAME)
836     {
837       (void) _mi_make_key(info,key,tmp_key,record,0L);
838       return mi_rkey(info,file_info->record,(int) key,tmp_key,0,
839 		     HA_READ_KEY_EXACT);
840     }
841   }
842   return 1;
843 }
844 
845 
printf_log(const char * format,...)846 static void printf_log(const char *format,...)
847 {
848   char llbuff[21];
849   va_list args;
850   va_start(args,format);
851   if (verbose > 2)
852     printf("%9s:",llstr(isamlog_filepos,llbuff));
853   if (verbose > 1)
854     printf("%5ld ",isamlog_process);	/* Write process number */
855   (void) vprintf((char*) format,args);
856   putchar('\n');
857   va_end(args);
858 }
859 
860 
cmp_filename(struct file_info * file_info,char * name)861 static my_bool cmp_filename(struct file_info *file_info, char * name)
862 {
863   if (!file_info)
864     return 1;
865   return strcmp(file_info->name,name) ? 1 : 0;
866 }
867 
868 #include "mi_extrafunc.h"
869