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 | |