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