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
23static const char *load_default_groups[]= { "aria_read_log",0 };
24static void get_options(int *argc,char * * *argv);
25#ifndef DBUG_OFF
26#if defined(__WIN__)
27const char *default_dbug_option= "d:t:O,\\aria_read_log.trace";
28#else
29const char *default_dbug_option= "d:t:o,/tmp/aria_read_log.trace";
30#endif
31#endif /* DBUG_OFF */
32static my_bool opt_display_only, opt_apply, opt_apply_undo, opt_silent;
33static my_bool opt_check;
34static const char *opt_tmpdir;
35static ulong opt_translog_buffer_size;
36static ulonglong opt_page_buffer_size;
37static ulonglong opt_start_from_lsn, opt_end_lsn, opt_start_from_checkpoint;
38static MY_TMPDIR maria_chk_tmpdir;
39
40
41int 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
150end:
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
159err:
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
170enum options_mc {
171 OPT_CHARSETS_DIR=256, OPT_FORCE_CRASH, OPT_TRANSLOG_BUFFER_SIZE
172};
173
174static 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
250static 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
257static 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
280static 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
288static my_bool
289get_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
326static 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