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