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