mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 19:06:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			448 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (C) 2007 MySQL AB
 | |
|    Copyright (C) 2010 Monty Program Ab
 | |
|    Copyright (C) 2020 MariaDB Corporation Ab
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
 | |
| 
 | |
| #include "maria_def.h"
 | |
| #include "ma_recovery.h"
 | |
| #include <my_getopt.h>
 | |
| 
 | |
| #define LOG_FLAGS 0
 | |
| 
 | |
| static const char *load_default_groups[]= { "aria_read_log",0 };
 | |
| static void get_options(int *argc,char * * *argv);
 | |
| #ifndef DBUG_OFF
 | |
| #if defined(_WIN32)
 | |
| const char *default_dbug_option= "d:t:O,\\aria_read_log.trace";
 | |
| #else
 | |
| const char *default_dbug_option= "d:t:o,/tmp/aria_read_log.trace";
 | |
| #endif
 | |
| #endif /* DBUG_OFF */
 | |
| static my_bool opt_display_only, opt_apply, opt_silent, opt_apply_undo;
 | |
| static my_bool opt_check, opt_start_from_checkpoint;
 | |
| static my_bool opt_print_aria_log_control;
 | |
| static const char *opt_tmpdir;
 | |
| static ulong opt_translog_buffer_size;
 | |
| static ulonglong opt_page_buffer_size;
 | |
| static ulonglong opt_start_from_lsn, opt_lsn_redo_end, opt_lsn_undo_end;
 | |
| static char *start_from_lsn_buf, *lsn_redo_end_buf, *lsn_undo_end_buf;
 | |
| static MY_TMPDIR maria_chk_tmpdir;
 | |
| 
 | |
| /*
 | |
|   Get lsn from file number and offset
 | |
|   Format supported:
 | |
|   ulonglong
 | |
|   uint,0xhex
 | |
| */
 | |
| 
 | |
| static ulonglong get_lsn(const char *lsn_str)
 | |
| {
 | |
|   ulong file;
 | |
|   ulong pos;
 | |
|   if (sscanf(lsn_str, " %lu,0x%lx", &file, &pos) == 2)
 | |
|     return MAKE_LSN(file, pos);
 | |
|   if (sscanf(lsn_str, " %lu", &pos) == 1)
 | |
|     return (ulonglong) pos;
 | |
|   return ~(ulonglong) 0;                        /* Error */
 | |
| }
 | |
| 
 | |
| static my_bool get_lsn_arg(const char *lsn_string, ulonglong *lsn,
 | |
|                            const char *name)
 | |
| {
 | |
|   ulonglong value;
 | |
|   value= get_lsn(lsn_string);
 | |
|   if (value != ~(ulonglong) 0)
 | |
|   {
 | |
|     *lsn= value;
 | |
|     return 0;
 | |
|   }
 | |
|   fprintf(stderr,
 | |
|           "Wrong value '%s' for option %s. Value should be in format: "
 | |
|           "number,0xhexnumber\n",
 | |
|           lsn_string, name);
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|   LSN lsn;
 | |
|   char **default_argv;
 | |
|   uint warnings_count;
 | |
|   MY_INIT(argv[0]);
 | |
| 
 | |
|   maria_data_root= ".";
 | |
|   sf_leaking_memory=1; /* don't report memory leaks on early exits */
 | |
|   load_defaults_or_exit("my", load_default_groups, &argc, &argv);
 | |
|   default_argv= argv;
 | |
|   get_options(&argc, &argv);
 | |
| 
 | |
|   maria_in_recovery= TRUE;
 | |
| 
 | |
|   if (maria_init())
 | |
|   {
 | |
|     fprintf(stderr, "Can't init Aria engine (%d)\n", errno);
 | |
|     goto err;
 | |
|   }
 | |
|   maria_block_size= 0;                          /* Use block size from file */
 | |
|   if (opt_print_aria_log_control)
 | |
|   {
 | |
|     if (print_aria_log_control())
 | |
|       goto err;
 | |
|     goto end;
 | |
|   }
 | |
|   /* we don't want to create a control file, it MUST exist */
 | |
|   if (ma_control_file_open(FALSE, TRUE, TRUE, control_file_open_flags))
 | |
|   {
 | |
|     fprintf(stderr, "Can't open control file (%d)\n", errno);
 | |
|     goto err;
 | |
|   }
 | |
|   if (last_logno == FILENO_IMPOSSIBLE)
 | |
|   {
 | |
|     fprintf(stderr, "Can't find any log\n");
 | |
|     goto err;
 | |
|   }
 | |
|   if (init_pagecache(maria_pagecache, (size_t)opt_page_buffer_size, 0, 0,
 | |
|                      maria_block_size, 0, MY_WME) == 0)
 | |
|   {
 | |
|     fprintf(stderr, "Got error in init_pagecache() (errno: %d)\n", errno);
 | |
|     goto err;
 | |
|   }
 | |
|   /*
 | |
|     If log handler does not find the "last_logno" log it will return error,
 | |
|     which is good.
 | |
|     But if it finds a log and this log was crashed, it will create a new log,
 | |
|     which is useless. TODO: start log handler in read-only mode.
 | |
|   */
 | |
|   if (init_pagecache(maria_log_pagecache, opt_translog_buffer_size,
 | |
|                      0, 0, TRANSLOG_PAGE_SIZE, 0, MY_WME) == 0 ||
 | |
|       translog_init(maria_data_root, TRANSLOG_FILE_SIZE,
 | |
|                     0, 0, maria_log_pagecache, TRANSLOG_DEFAULT_FLAGS,
 | |
|                     opt_display_only))
 | |
|   {
 | |
|     fprintf(stderr, "Can't init loghandler (%d)\n", errno);
 | |
|     goto err;
 | |
|   }
 | |
| 
 | |
|   if (opt_display_only)
 | |
|     printf("You are using --display-only, NOTHING will be written to disk\n");
 | |
| 
 | |
|   if (translog_get_horizon() == LSN_IMPOSSIBLE)
 | |
|   {
 | |
|      fprintf(stdout, "The transaction log is empty\n");
 | |
|      goto end;
 | |
|   }
 | |
| 
 | |
|   lsn= translog_first_lsn_in_log();
 | |
|   if (lsn == LSN_ERROR)
 | |
|   {
 | |
|     fprintf(stderr, "Opening transaction log failed\n");
 | |
|     goto end;
 | |
|   }
 | |
|   if (lsn == LSN_IMPOSSIBLE)
 | |
|   {
 | |
|     fprintf(stdout, "The transaction log is empty\n");
 | |
|     goto end;
 | |
|   }
 | |
|   if (opt_start_from_checkpoint && !opt_start_from_lsn &&
 | |
|       last_checkpoint_lsn != LSN_IMPOSSIBLE)
 | |
|   {
 | |
|     lsn= LSN_IMPOSSIBLE;             /* LSN set in maria_apply_log() */
 | |
|     fprintf(stdout, "Starting from checkpoint " LSN_FMT "\n",
 | |
|             LSN_IN_PARTS(last_checkpoint_lsn));
 | |
|   }
 | |
|   else
 | |
|     fprintf(stdout, "The transaction log starts from lsn " LSN_FMT "\n",
 | |
|             LSN_IN_PARTS(lsn));
 | |
| 
 | |
|   if (opt_start_from_lsn)
 | |
|   {
 | |
|     if (opt_start_from_lsn < (ulonglong) lsn)
 | |
|     {
 | |
|       fprintf(stderr, "start_from_lsn is too small. Aborting\n");
 | |
|       maria_end();
 | |
|       goto err;
 | |
|     }
 | |
|     lsn= (LSN) opt_start_from_lsn;
 | |
|     fprintf(stdout, "Starting reading log from lsn " LSN_FMT "\n",
 | |
|             LSN_IN_PARTS(lsn));
 | |
|   }
 | |
| 
 | |
|   fprintf(stdout, "TRACE of the last aria_read_log\n");
 | |
|   if (maria_apply_log(lsn, opt_lsn_redo_end, opt_lsn_undo_end,
 | |
|                       opt_apply ?  MARIA_LOG_APPLY :
 | |
|                       (opt_check ? MARIA_LOG_CHECK :
 | |
|                        MARIA_LOG_DISPLAY_HEADER), opt_silent ? NULL : stdout,
 | |
|                       FALSE, FALSE, &warnings_count))
 | |
|     goto err;
 | |
|   if (warnings_count == 0)
 | |
|     fprintf(stdout, "%s: SUCCESS\n", my_progname_short);
 | |
|   else
 | |
|     fprintf(stdout, "%s: DOUBTFUL (%u warnings, check previous output)\n",
 | |
|             my_progname_short, warnings_count);
 | |
| 
 | |
| end:
 | |
|   maria_end();
 | |
|   free_tmpdir(&maria_chk_tmpdir);
 | |
|   free_defaults(default_argv);
 | |
|   my_end(0);
 | |
|   sf_leaking_memory=0;
 | |
|   exit(0);
 | |
|   return 0;				/* No compiler warning */
 | |
| 
 | |
| err:
 | |
|   /* don't touch anything more, in case we hit a bug */
 | |
|   fprintf(stderr, "%s: FAILED\n", my_progname_short);
 | |
|   free_tmpdir(&maria_chk_tmpdir);
 | |
|   free_defaults(default_argv);
 | |
|   exit(1);
 | |
| }
 | |
| 
 | |
| 
 | |
| #include "ma_check_standalone.h"
 | |
| 
 | |
| enum options_mc {
 | |
|   OPT_CHARSETS_DIR=256, OPT_FORCE_CRASH, OPT_TRANSLOG_BUFFER_SIZE
 | |
| };
 | |
| 
 | |
| static struct my_option my_long_options[] =
 | |
| {
 | |
|   {"apply", 'a',
 | |
|    "Apply log to tables: modifies tables! you should make a backup first! "
 | |
|    " Displays a lot of information if not run with --silent",
 | |
|    (uchar **) &opt_apply, (uchar **) &opt_apply, 0,
 | |
|    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"character-sets-dir", OPT_CHARSETS_DIR,
 | |
|    "Directory where character sets are.",
 | |
|    (char**) &charsets_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"check", 'c',
 | |
|    "if --display-only, check if record is fully readable (for debugging)",
 | |
|    (uchar **) &opt_check, (uchar **) &opt_check, 0,
 | |
|    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
| #ifndef DBUG_OFF
 | |
|   {"debug", '#', "Output debug log. Often the argument is 'd:t:o,filename'.",
 | |
|    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"force-crash", OPT_FORCE_CRASH, "Force crash after # recovery events",
 | |
|    &maria_recovery_force_crash_counter, 0,0, GET_ULONG, REQUIRED_ARG,
 | |
|    0, 0, ~(long) 0, 0, 0, 0},
 | |
| #endif
 | |
|   {"help", '?', "Display this help and exit.",
 | |
|    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"display-only", 'd', "display brief info read from records' header",
 | |
|    &opt_display_only, &opt_display_only, 0, GET_BOOL,
 | |
|    NO_ARG,0, 0, 0, 0, 0, 0},
 | |
|   { "end-lsn", 'e', "Alias for lsn-redo-end",
 | |
|     &lsn_redo_end_buf, &lsn_redo_end_buf, 0, GET_STR, REQUIRED_ARG, 0, 0,
 | |
|     0, 0, 0, 0 },
 | |
|   { "lsn-redo-end", 'e', "Stop applying at this lsn during redo. If "
 | |
|     "this option is used UNDO:s will not be applied unless --lsn-undo-end is "
 | |
|     "given", &lsn_redo_end_buf,
 | |
|     &lsn_redo_end_buf, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
 | |
|   { "lsn-undo-end", 'E', "Stop applying undo after this lsn has been applied",
 | |
|     &lsn_undo_end_buf, &lsn_undo_end_buf, 0, GET_STR, REQUIRED_ARG, 0, 0,
 | |
|     0, 0, 0, 0 },
 | |
|   {"aria-log-dir-path", 'h',
 | |
|     "Path to the directory where to store transactional log",
 | |
|     (char **) &maria_data_root, (char **) &maria_data_root, 0,
 | |
|     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   { "page-buffer-size", 'P',
 | |
|     "The size of the buffer used for index blocks for Aria tables",
 | |
|     &opt_page_buffer_size, &opt_page_buffer_size, 0,
 | |
|     GET_ULL, REQUIRED_ARG, PAGE_BUFFER_INIT,
 | |
|     PAGE_BUFFER_INIT, SIZE_T_MAX, 0, (long) IO_SIZE, 0},
 | |
|   { "print-log-control-file", 'l',
 | |
|     "Print the content of the aria_log_control_file",
 | |
|     &opt_print_aria_log_control, &opt_print_aria_log_control, 0,
 | |
|     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   { "start-from-lsn", 'o', "Start reading log from this lsn",
 | |
|     &opt_start_from_lsn, &opt_start_from_lsn,
 | |
|     0, GET_ULL, REQUIRED_ARG, 0, 0, ~(longlong) 0, 0, 0, 0 },
 | |
|   {"start-from-checkpoint", 'C', "Start applying from last checkpoint",
 | |
|    &opt_start_from_checkpoint, &opt_start_from_checkpoint, 0,
 | |
|    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"silent", 's', "Print less information during apply/undo phase",
 | |
|    &opt_silent, &opt_silent, 0,
 | |
|    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"tables-to-redo", 'T',
 | |
|    "List of tables separated with , that we should apply REDO on. Use this if you only want to recover some tables",
 | |
|    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"tmpdir", 't', "Path for temporary files. Multiple paths can be specified, "
 | |
|    "separated by "
 | |
| #if defined( _WIN32)
 | |
|    "semicolon (;)"
 | |
| #else
 | |
|    "colon (:)"
 | |
| #endif
 | |
|    , (char**) &opt_tmpdir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   { "translog-buffer-size", OPT_TRANSLOG_BUFFER_SIZE,
 | |
|     "The size of the buffer used for transaction log for Aria tables",
 | |
|     &opt_translog_buffer_size, &opt_translog_buffer_size, 0,
 | |
|     GET_ULONG, REQUIRED_ARG, (long) TRANSLOG_PAGECACHE_SIZE,
 | |
|     1024L*1024L, (long) ~(ulong) 0, 0,
 | |
|     (long) IO_SIZE, 0},
 | |
|   {"undo", 'u',
 | |
|    "Apply UNDO records to tables. (disable with --disable-undo). "
 | |
|    "Will be automatically set if lsn-undo-end is used",
 | |
|    (uchar **) &opt_apply_undo, (uchar **) &opt_apply_undo, 0,
 | |
|    GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
 | |
|   {"verbose", 'v', "Print more information during apply/undo phase",
 | |
|    &maria_recovery_verbose, &maria_recovery_verbose, 0,
 | |
|    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"version", 'V', "Print version and exit.",
 | |
|    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 | |
| };
 | |
| 
 | |
| static void print_version(void)
 | |
| {
 | |
|   printf("%s Ver 1.6 for %s on %s\n",
 | |
|               my_progname_short, SYSTEM_TYPE, MACHINE_TYPE);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void usage(void)
 | |
| {
 | |
|   print_version();
 | |
|   puts("Copyright (C) 2007 MySQL AB, 2009-2011 Monty Program Ab, 2022 MariaDB Corporation");
 | |
|   puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,");
 | |
|   puts("and you are welcome to modify and redistribute it under the GPL license\n");
 | |
| 
 | |
|   puts("Display or apply log records from a Aria transaction log");
 | |
|   puts("found in the current directory (for now)");
 | |
| #ifndef IDENTICAL_PAGES_AFTER_RECOVERY
 | |
|   puts("\nNote: Aria is compiled without -DIDENTICAL_PAGES_AFTER_RECOVERY\n"
 | |
|        "which means that the table files are not byte-to-byte identical to\n"
 | |
|        "files created during normal execution. This should be ok, except for\n"
 | |
|        "test scripts that tries to compare files before and after recovery.");
 | |
| #endif
 | |
|   printf("\nUsage: %s OPTIONS [-d | -a] -h `aria_log_directory`\n",
 | |
|          my_progname_short);
 | |
|   printf("or\n");
 | |
|   printf("Usage: %s OPTIONS -h `aria_log_directory` "
 | |
|          "--print-log-control-file\n\n",
 | |
|          my_progname_short);
 | |
| 
 | |
|   my_print_help(my_long_options);
 | |
|   print_defaults("my", load_default_groups);
 | |
|   my_print_variables(my_long_options);
 | |
| }
 | |
| 
 | |
| 
 | |
| static const uchar *my_hash_get_string(const void *record_, size_t *length,
 | |
|                                        my_bool first __attribute__((unused)))
 | |
| {
 | |
|   const char *record= record_;
 | |
|   *length= (strcend(record, ',') - record);
 | |
|   return record_;
 | |
| }
 | |
| 
 | |
| 
 | |
| static my_bool
 | |
| get_one_option(const struct my_option *opt,
 | |
|                const char *argument,
 | |
|                const char *filename __attribute__((unused)))
 | |
| {
 | |
|   switch (opt->id) {
 | |
|   case '?':
 | |
|     usage();
 | |
|     exit(0);
 | |
|   case 'V':
 | |
|     print_version();
 | |
|     exit(0);
 | |
|   case 'E':
 | |
|     opt_apply_undo= TRUE;
 | |
|     break;
 | |
|   case 'T':
 | |
|   {
 | |
|     char *pos;
 | |
|     if (!my_hash_inited(&tables_to_redo))
 | |
|     {
 | |
|       my_hash_init2(PSI_INSTRUMENT_ME, &tables_to_redo, 16, &my_charset_bin,
 | |
|                     16, 0, 0, my_hash_get_string, 0, 0, HASH_UNIQUE);
 | |
|     }
 | |
|     do
 | |
|     {
 | |
|       pos= strcend(argument, ',');
 | |
|       if (pos != argument)                      /* Skip empty strings */
 | |
|         my_hash_insert(&tables_to_redo, (uchar*) argument);
 | |
|       argument= pos+1;
 | |
|     } while (*(pos++));
 | |
|     break;
 | |
|   }
 | |
| #ifndef DBUG_OFF
 | |
|   case '#':
 | |
|     DBUG_SET_INITIAL(argument ? argument : default_dbug_option);
 | |
|     break;
 | |
| #endif
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static void get_options(int *argc,char ***argv)
 | |
| {
 | |
|   int ho_error;
 | |
|   my_bool need_help= 0, need_abort= 0;
 | |
| 
 | |
|   if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
 | |
|     exit(ho_error);
 | |
| 
 | |
|   if (start_from_lsn_buf)
 | |
|   {
 | |
|     if (get_lsn_arg(start_from_lsn_buf, &opt_start_from_lsn,
 | |
|                     "start-from-lsn"))
 | |
|       need_abort= 1;
 | |
|   }
 | |
|   if (lsn_redo_end_buf)
 | |
|   {
 | |
|     if (get_lsn_arg(lsn_redo_end_buf, &opt_lsn_redo_end,
 | |
|                     "lsn-redo-end"))
 | |
|       need_abort= 1;
 | |
|   }
 | |
|   if (lsn_undo_end_buf)
 | |
|   {
 | |
|     if (get_lsn_arg(lsn_undo_end_buf, &opt_lsn_undo_end,
 | |
|                     "lsn-undo-end"))
 | |
|       need_abort= 1;
 | |
|   }
 | |
| 
 | |
|   if (!opt_apply)
 | |
|     opt_apply_undo= FALSE;
 | |
|   if (!opt_apply_undo)
 | |
|     opt_lsn_undo_end= LSN_MAX;
 | |
| 
 | |
|   if (*argc > 0)
 | |
|   {
 | |
|     need_help= 1;
 | |
|     fprintf(stderr, "Too many arguments given\n");
 | |
|   }
 | |
|   if ((opt_display_only + opt_apply + opt_print_aria_log_control) != 1)
 | |
|   {
 | |
|     need_abort= 1;
 | |
|     fprintf(stderr,
 | |
|             "You must use one and only one of the options 'display-only', \n"
 | |
|             "'print-log-control-file' and 'apply'\n");
 | |
|   }
 | |
| 
 | |
|   if (need_help || need_abort)
 | |
|   {
 | |
|     fflush(stderr);
 | |
|     if (need_help)
 | |
|       usage();
 | |
|     exit(1);
 | |
|   }
 | |
|   if (init_tmpdir(&maria_chk_tmpdir, opt_tmpdir))
 | |
|     exit(1);
 | |
|   maria_tmpdir= &maria_chk_tmpdir;
 | |
| }
 | 
