| 1 | /* Copyright (C) 2007 MySQL AB |
| 2 | Copyright (C) 2010 Monty Program Ab |
| 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 Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
| 16 | |
| 17 | #include "maria_def.h" |
| 18 | #include "ma_recovery.h" |
| 19 | #include <my_getopt.h> |
| 20 | |
| 21 | #define LOG_FLAGS 0 |
| 22 | |
| 23 | static const char *load_default_groups[]= { "aria_read_log" ,0 }; |
| 24 | static void get_options(int *argc,char * * *argv); |
| 25 | #ifndef DBUG_OFF |
| 26 | #if defined(__WIN__) |
| 27 | const char *default_dbug_option= "d:t:O,\\aria_read_log.trace" ; |
| 28 | #else |
| 29 | const char *default_dbug_option= "d:t:o,/tmp/aria_read_log.trace" ; |
| 30 | #endif |
| 31 | #endif /* DBUG_OFF */ |
| 32 | static my_bool opt_display_only, opt_apply, opt_apply_undo, opt_silent; |
| 33 | static my_bool opt_check; |
| 34 | static const char *opt_tmpdir; |
| 35 | static ulong opt_translog_buffer_size; |
| 36 | static ulonglong opt_page_buffer_size; |
| 37 | static ulonglong opt_start_from_lsn, opt_end_lsn, opt_start_from_checkpoint; |
| 38 | static MY_TMPDIR maria_chk_tmpdir; |
| 39 | |
| 40 | |
| 41 | int main(int argc, char **argv) |
| 42 | { |
| 43 | LSN lsn; |
| 44 | char **default_argv; |
| 45 | uint warnings_count; |
| 46 | MY_INIT(argv[0]); |
| 47 | |
| 48 | maria_data_root= (char *)"." ; |
| 49 | sf_leaking_memory=1; /* don't report memory leaks on early exits */ |
| 50 | load_defaults_or_exit("my" , load_default_groups, &argc, &argv); |
| 51 | default_argv= argv; |
| 52 | get_options(&argc, &argv); |
| 53 | |
| 54 | maria_in_recovery= TRUE; |
| 55 | |
| 56 | if (maria_init()) |
| 57 | { |
| 58 | fprintf(stderr, "Can't init Aria engine (%d)\n" , errno); |
| 59 | goto err; |
| 60 | } |
| 61 | maria_block_size= 0; /* Use block size from file */ |
| 62 | /* we don't want to create a control file, it MUST exist */ |
| 63 | if (ma_control_file_open(FALSE, TRUE)) |
| 64 | { |
| 65 | fprintf(stderr, "Can't open control file (%d)\n" , errno); |
| 66 | goto err; |
| 67 | } |
| 68 | if (last_logno == FILENO_IMPOSSIBLE) |
| 69 | { |
| 70 | fprintf(stderr, "Can't find any log\n" ); |
| 71 | goto err; |
| 72 | } |
| 73 | if (init_pagecache(maria_pagecache, (size_t)opt_page_buffer_size, 0, 0, |
| 74 | maria_block_size, 0, MY_WME) == 0) |
| 75 | { |
| 76 | fprintf(stderr, "Got error in init_pagecache() (errno: %d)\n" , errno); |
| 77 | goto err; |
| 78 | } |
| 79 | /* |
| 80 | If log handler does not find the "last_logno" log it will return error, |
| 81 | which is good. |
| 82 | But if it finds a log and this log was crashed, it will create a new log, |
| 83 | which is useless. TODO: start log handler in read-only mode. |
| 84 | */ |
| 85 | if (init_pagecache(maria_log_pagecache, opt_translog_buffer_size, |
| 86 | 0, 0, TRANSLOG_PAGE_SIZE, 0, MY_WME) == 0 || |
| 87 | translog_init(maria_data_root, TRANSLOG_FILE_SIZE, |
| 88 | 0, 0, maria_log_pagecache, TRANSLOG_DEFAULT_FLAGS, |
| 89 | opt_display_only)) |
| 90 | { |
| 91 | fprintf(stderr, "Can't init loghandler (%d)\n" , errno); |
| 92 | goto err; |
| 93 | } |
| 94 | |
| 95 | if (opt_display_only) |
| 96 | printf("You are using --display-only, NOTHING will be written to disk\n" ); |
| 97 | |
| 98 | lsn= translog_first_lsn_in_log(); |
| 99 | if (lsn == LSN_ERROR) |
| 100 | { |
| 101 | fprintf(stderr, "Opening transaction log failed\n" ); |
| 102 | goto end; |
| 103 | } |
| 104 | if (lsn == LSN_IMPOSSIBLE) |
| 105 | { |
| 106 | fprintf(stdout, "The transaction log is empty\n" ); |
| 107 | } |
| 108 | if (opt_start_from_checkpoint && !opt_start_from_lsn && |
| 109 | last_checkpoint_lsn != LSN_IMPOSSIBLE) |
| 110 | { |
| 111 | lsn= LSN_IMPOSSIBLE; /* LSN set in maria_apply_log() */ |
| 112 | fprintf(stdout, "Starting from checkpoint " LSN_FMT "\n" , |
| 113 | LSN_IN_PARTS(last_checkpoint_lsn)); |
| 114 | } |
| 115 | else |
| 116 | fprintf(stdout, "The transaction log starts from lsn " LSN_FMT "\n" , |
| 117 | LSN_IN_PARTS(lsn)); |
| 118 | |
| 119 | if (opt_start_from_lsn) |
| 120 | { |
| 121 | if (opt_start_from_lsn < (ulonglong) lsn) |
| 122 | { |
| 123 | fprintf(stderr, "start_from_lsn is too small. Aborting\n" ); |
| 124 | maria_end(); |
| 125 | goto err; |
| 126 | } |
| 127 | lsn= (LSN) opt_start_from_lsn; |
| 128 | fprintf(stdout, "Starting reading log from lsn " LSN_FMT "\n" , |
| 129 | LSN_IN_PARTS(lsn)); |
| 130 | } |
| 131 | |
| 132 | if (opt_end_lsn != LSN_IMPOSSIBLE) |
| 133 | { |
| 134 | /* We can't apply undo if we use end_lsn */ |
| 135 | opt_apply_undo= 0; |
| 136 | } |
| 137 | |
| 138 | fprintf(stdout, "TRACE of the last aria_read_log\n" ); |
| 139 | if (maria_apply_log(lsn, opt_end_lsn, opt_apply ? MARIA_LOG_APPLY : |
| 140 | (opt_check ? MARIA_LOG_CHECK : |
| 141 | MARIA_LOG_DISPLAY_HEADER), opt_silent ? NULL : stdout, |
| 142 | opt_apply_undo, FALSE, FALSE, &warnings_count)) |
| 143 | goto err; |
| 144 | if (warnings_count == 0) |
| 145 | fprintf(stdout, "%s: SUCCESS\n" , my_progname_short); |
| 146 | else |
| 147 | fprintf(stdout, "%s: DOUBTFUL (%u warnings, check previous output)\n" , |
| 148 | my_progname_short, warnings_count); |
| 149 | |
| 150 | end: |
| 151 | maria_end(); |
| 152 | free_tmpdir(&maria_chk_tmpdir); |
| 153 | free_defaults(default_argv); |
| 154 | my_end(0); |
| 155 | sf_leaking_memory=0; |
| 156 | exit(0); |
| 157 | return 0; /* No compiler warning */ |
| 158 | |
| 159 | err: |
| 160 | /* don't touch anything more, in case we hit a bug */ |
| 161 | fprintf(stderr, "%s: FAILED\n" , my_progname_short); |
| 162 | free_tmpdir(&maria_chk_tmpdir); |
| 163 | free_defaults(default_argv); |
| 164 | exit(1); |
| 165 | } |
| 166 | |
| 167 | |
| 168 | #include "ma_check_standalone.h" |
| 169 | |
| 170 | enum options_mc { |
| 171 | OPT_CHARSETS_DIR=256, OPT_FORCE_CRASH, OPT_TRANSLOG_BUFFER_SIZE |
| 172 | }; |
| 173 | |
| 174 | static struct my_option my_long_options[] = |
| 175 | { |
| 176 | {"apply" , 'a', |
| 177 | "Apply log to tables: modifies tables! you should make a backup first! " |
| 178 | " Displays a lot of information if not run with --silent" , |
| 179 | (uchar **) &opt_apply, (uchar **) &opt_apply, 0, |
| 180 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 181 | {"character-sets-dir" , OPT_CHARSETS_DIR, |
| 182 | "Directory where character sets are." , |
| 183 | (char**) &charsets_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 184 | {"check" , 'c', |
| 185 | "if --display-only, check if record is fully readable (for debugging)" , |
| 186 | (uchar **) &opt_check, (uchar **) &opt_check, 0, |
| 187 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 188 | #ifndef DBUG_OFF |
| 189 | {"debug" , '#', "Output debug log. Often the argument is 'd:t:o,filename'." , |
| 190 | 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
| 191 | {"force-crash" , OPT_FORCE_CRASH, "Force crash after # recovery events" , |
| 192 | &maria_recovery_force_crash_counter, 0,0, GET_ULONG, REQUIRED_ARG, |
| 193 | 0, 0, ~(long) 0, 0, 0, 0}, |
| 194 | #endif |
| 195 | {"help" , '?', "Display this help and exit." , |
| 196 | 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 197 | {"display-only" , 'd', "display brief info read from records' header" , |
| 198 | &opt_display_only, &opt_display_only, 0, GET_BOOL, |
| 199 | NO_ARG,0, 0, 0, 0, 0, 0}, |
| 200 | { "end-lsn" , 'e', "Stop applying at this lsn. If end-lsn is used, UNDO:s " |
| 201 | "will not be applied" , &opt_end_lsn, &opt_end_lsn, |
| 202 | 0, GET_ULL, REQUIRED_ARG, 0, 0, ~(longlong) 0, 0, 0, 0 }, |
| 203 | {"aria-log-dir-path" , 'h', |
| 204 | "Path to the directory where to store transactional log" , |
| 205 | (uchar **) &maria_data_root, (uchar **) &maria_data_root, 0, |
| 206 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 207 | { "page-buffer-size" , 'P', |
| 208 | "The size of the buffer used for index blocks for Aria tables" , |
| 209 | &opt_page_buffer_size, &opt_page_buffer_size, 0, |
| 210 | GET_ULL, REQUIRED_ARG, PAGE_BUFFER_INIT, |
| 211 | PAGE_BUFFER_INIT, SIZE_T_MAX, MALLOC_OVERHEAD, (long) IO_SIZE, 0}, |
| 212 | { "start-from-lsn" , 'o', "Start reading log from this lsn" , |
| 213 | &opt_start_from_lsn, &opt_start_from_lsn, |
| 214 | 0, GET_ULL, REQUIRED_ARG, 0, 0, ~(longlong) 0, 0, 0, 0 }, |
| 215 | {"start-from-checkpoint" , 'C', "Start applying from last checkpoint" , |
| 216 | &opt_start_from_checkpoint, &opt_start_from_checkpoint, 0, |
| 217 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 218 | {"silent" , 's', "Print less information during apply/undo phase" , |
| 219 | &opt_silent, &opt_silent, 0, |
| 220 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 221 | {"tables-to-redo" , 'T', |
| 222 | "List of tables sepearated with , that we should apply REDO on. Use this if you only want to recover some tables" , |
| 223 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 224 | {"tmpdir" , 't', "Path for temporary files. Multiple paths can be specified, " |
| 225 | "separated by " |
| 226 | #if defined( __WIN__) || defined(__NETWARE__) |
| 227 | "semicolon (;)" |
| 228 | #else |
| 229 | "colon (:)" |
| 230 | #endif |
| 231 | , (char**) &opt_tmpdir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 232 | { "translog-buffer-size" , OPT_TRANSLOG_BUFFER_SIZE, |
| 233 | "The size of the buffer used for transaction log for Aria tables" , |
| 234 | &opt_translog_buffer_size, &opt_translog_buffer_size, 0, |
| 235 | GET_ULONG, REQUIRED_ARG, (long) TRANSLOG_PAGECACHE_SIZE, |
| 236 | 1024L*1024L, (long) ~(ulong) 0, (long) MALLOC_OVERHEAD, |
| 237 | (long) IO_SIZE, 0}, |
| 238 | {"undo" , 'u', "Apply UNDO records to tables. (disable with --disable-undo)" , |
| 239 | (uchar **) &opt_apply_undo, (uchar **) &opt_apply_undo, 0, |
| 240 | GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, |
| 241 | {"verbose" , 'v', "Print more information during apply/undo phase" , |
| 242 | &maria_recovery_verbose, &maria_recovery_verbose, 0, |
| 243 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 244 | {"version" , 'V', "Print version and exit." , |
| 245 | 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 246 | { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} |
| 247 | }; |
| 248 | |
| 249 | |
| 250 | static void print_version(void) |
| 251 | { |
| 252 | printf("%s Ver 1.3 for %s on %s\n" , |
| 253 | my_progname_short, SYSTEM_TYPE, MACHINE_TYPE); |
| 254 | } |
| 255 | |
| 256 | |
| 257 | static void usage(void) |
| 258 | { |
| 259 | print_version(); |
| 260 | puts("Copyright (C) 2007 MySQL AB, 2009-2011 Monty Program Ab" ); |
| 261 | puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software," ); |
| 262 | puts("and you are welcome to modify and redistribute it under the GPL license\n" ); |
| 263 | |
| 264 | puts("Display and apply log records from a Aria transaction log" ); |
| 265 | puts("found in the current directory (for now)" ); |
| 266 | #ifndef IDENTICAL_PAGES_AFTER_RECOVERY |
| 267 | puts("\nNote: Aria is compiled without -DIDENTICAL_PAGES_AFTER_RECOVERY\n" |
| 268 | "which means that the table files are not byte-to-byte identical to\n" |
| 269 | "files created during normal execution. This should be ok, except for\n" |
| 270 | "test scripts that tries to compare files before and after recovery." ); |
| 271 | #endif |
| 272 | printf("\nUsage: %s OPTIONS\n" , my_progname_short); |
| 273 | puts("You need to use one of -d or -a" ); |
| 274 | my_print_help(my_long_options); |
| 275 | print_defaults("my" , load_default_groups); |
| 276 | my_print_variables(my_long_options); |
| 277 | } |
| 278 | |
| 279 | |
| 280 | static uchar* my_hash_get_string(const uchar *record, size_t *length, |
| 281 | my_bool first __attribute__ ((unused))) |
| 282 | { |
| 283 | *length= (size_t) (strcend((const char*) record,',')- (const char*) record); |
| 284 | return (uchar*) record; |
| 285 | } |
| 286 | |
| 287 | |
| 288 | static my_bool |
| 289 | get_one_option(int optid __attribute__((unused)), |
| 290 | const struct my_option *opt __attribute__((unused)), |
| 291 | char *argument) |
| 292 | { |
| 293 | switch (optid) { |
| 294 | case '?': |
| 295 | usage(); |
| 296 | exit(0); |
| 297 | case 'V': |
| 298 | print_version(); |
| 299 | exit(0); |
| 300 | case 'T': |
| 301 | { |
| 302 | char *pos; |
| 303 | if (!my_hash_inited(&tables_to_redo)) |
| 304 | { |
| 305 | my_hash_init2(&tables_to_redo, 16, &my_charset_bin, |
| 306 | 16, 0, 0, my_hash_get_string, 0, 0, HASH_UNIQUE); |
| 307 | } |
| 308 | do |
| 309 | { |
| 310 | pos= strcend(argument, ','); |
| 311 | if (pos != argument) /* Skip empty strings */ |
| 312 | my_hash_insert(&tables_to_redo, (uchar*) argument); |
| 313 | argument= pos+1; |
| 314 | } while (*(pos++)); |
| 315 | break; |
| 316 | } |
| 317 | #ifndef DBUG_OFF |
| 318 | case '#': |
| 319 | DBUG_SET_INITIAL(argument ? argument : default_dbug_option); |
| 320 | break; |
| 321 | #endif |
| 322 | } |
| 323 | return 0; |
| 324 | } |
| 325 | |
| 326 | static void get_options(int *argc,char ***argv) |
| 327 | { |
| 328 | int ho_error; |
| 329 | my_bool need_help= 0; |
| 330 | |
| 331 | if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option))) |
| 332 | exit(ho_error); |
| 333 | |
| 334 | if (!opt_apply) |
| 335 | opt_apply_undo= FALSE; |
| 336 | |
| 337 | if (*argc > 0) |
| 338 | { |
| 339 | need_help= 1; |
| 340 | fprintf(stderr, "Too many arguments given\n" ); |
| 341 | } |
| 342 | if ((opt_display_only + opt_apply) != 1) |
| 343 | { |
| 344 | need_help= 1; |
| 345 | fprintf(stderr, |
| 346 | "You must use one and only one of the options 'display-only' or " |
| 347 | "'apply'\n" ); |
| 348 | } |
| 349 | |
| 350 | if (need_help) |
| 351 | { |
| 352 | fflush(stderr); |
| 353 | need_help =1; |
| 354 | usage(); |
| 355 | exit(1); |
| 356 | } |
| 357 | if (init_tmpdir(&maria_chk_tmpdir, opt_tmpdir)) |
| 358 | exit(1); |
| 359 | maria_tmpdir= &maria_chk_tmpdir; |
| 360 | } |
| 361 | |
| 362 | |