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