idb/cli/app.rs
1use clap::{Parser, Subcommand, ValueEnum};
2
3/// Top-level CLI definition for the `inno` binary.
4#[derive(Parser)]
5#[command(name = "inno")]
6#[command(about = "InnoDB file analysis toolkit")]
7#[command(version)]
8pub struct Cli {
9 /// Control colored output
10 #[arg(long, default_value = "auto", global = true)]
11 pub color: ColorMode,
12
13 /// Write output to a file instead of stdout
14 #[arg(short, long, global = true)]
15 pub output: Option<String>,
16
17 /// Number of threads for parallel page processing (0 = auto-detect CPU count)
18 #[arg(long, default_value = "0", global = true)]
19 pub threads: usize,
20
21 /// Use memory-mapped I/O for file access (can be faster for large files)
22 #[arg(long, global = true)]
23 pub mmap: bool,
24
25 /// Output format (text, json, or csv); overrides per-subcommand --json
26 #[arg(long, global = true, default_value = "text")]
27 pub format: OutputFormat,
28
29 /// Write NDJSON audit events to a file for all write operations
30 #[arg(long = "audit-log", global = true)]
31 pub audit_log: Option<String>,
32
33 #[command(subcommand)]
34 pub command: Commands,
35}
36
37/// Controls when colored output is emitted.
38#[derive(Clone, Copy, ValueEnum)]
39pub enum ColorMode {
40 Auto,
41 Always,
42 Never,
43}
44
45/// Global output format selection.
46#[derive(Clone, Copy, PartialEq, Eq, ValueEnum)]
47pub enum OutputFormat {
48 Text,
49 Json,
50 Csv,
51}
52
53/// Available subcommands for the `inno` CLI.
54#[derive(Subcommand)]
55pub enum Commands {
56 /// Parse .ibd file and display page summary
57 ///
58 /// Reads the 38-byte FIL header of every page in a tablespace, decodes the
59 /// page type, checksum, LSN, prev/next pointers, and space ID, then prints
60 /// a per-page breakdown followed by a page-type frequency summary table.
61 /// Page 0 additionally shows the FSP header (space ID, size, flags).
62 /// Use `--no-empty` to skip zero-checksum allocated pages, or `-p` to
63 /// inspect a single page in detail. With `--verbose`, checksum validation
64 /// and LSN consistency results are included for each page.
65 Parse {
66 /// Path to InnoDB data file (.ibd)
67 #[arg(short, long)]
68 file: String,
69
70 /// Display a specific page number
71 #[arg(short, long)]
72 page: Option<u64>,
73
74 /// Display additional information
75 #[arg(short, long)]
76 verbose: bool,
77
78 /// Skip empty/allocated pages
79 #[arg(short = 'e', long = "no-empty")]
80 no_empty: bool,
81
82 /// Output in JSON format
83 #[arg(long)]
84 json: bool,
85
86 /// Override page size (default: auto-detect)
87 #[arg(long = "page-size")]
88 page_size: Option<u32>,
89
90 /// Path to MySQL keyring file for decrypting encrypted tablespaces
91 #[arg(long)]
92 keyring: Option<String>,
93
94 /// Stream results incrementally for lower memory usage (disables parallel processing)
95 #[arg(long)]
96 streaming: bool,
97 },
98
99 /// Detailed page structure analysis
100 ///
101 /// Goes beyond FIL headers to decode the internal structure of each page
102 /// type: INDEX pages show the B+Tree index header, FSEG inode pointers, and
103 /// infimum/supremum system records; UNDO pages show the undo page header
104 /// and segment state; BLOB/LOB pages show chain pointers and data lengths;
105 /// and page 0 shows extended FSP header fields including compression and
106 /// encryption flags. Use `-l` for a compact one-line-per-page listing,
107 /// `-t INDEX` to filter by page type, or `-p` for a single page deep dive.
108 Pages {
109 /// Path to InnoDB data file (.ibd)
110 #[arg(short, long)]
111 file: String,
112
113 /// Display a specific page number
114 #[arg(short, long)]
115 page: Option<u64>,
116
117 /// Display additional information
118 #[arg(short, long)]
119 verbose: bool,
120
121 /// Show empty/allocated pages
122 #[arg(short = 'e', long = "show-empty")]
123 show_empty: bool,
124
125 /// Compact list mode (one line per page)
126 #[arg(short, long)]
127 list: bool,
128
129 /// Filter by page type (e.g., INDEX)
130 #[arg(short = 't', long = "type")]
131 filter_type: Option<String>,
132
133 /// Show delete-marked record statistics for INDEX pages
134 #[arg(long)]
135 deleted: bool,
136
137 /// Traverse and display LOB/BLOB chain details for LOB first pages
138 #[arg(long = "lob-chain")]
139 lob_chain: bool,
140
141 /// Output in JSON format
142 #[arg(long)]
143 json: bool,
144
145 /// Override page size (default: auto-detect)
146 #[arg(long = "page-size")]
147 page_size: Option<u32>,
148
149 /// Path to MySQL keyring file for decrypting encrypted tablespaces
150 #[arg(long)]
151 keyring: Option<String>,
152 },
153
154 /// Hex dump of raw page bytes
155 ///
156 /// Operates in two modes: **page mode** (default) reads a full page by
157 /// number and produces a formatted hex dump with file-relative offsets;
158 /// **offset mode** (`--offset`) reads bytes at an arbitrary file position,
159 /// useful for inspecting structures that cross page boundaries. Use
160 /// `--length` to limit the number of bytes shown, or `--raw` to emit
161 /// unformatted binary bytes suitable for piping to other tools.
162 Dump {
163 /// Path to InnoDB data file
164 #[arg(short, long)]
165 file: String,
166
167 /// Page number to dump (default: 0)
168 #[arg(short, long)]
169 page: Option<u64>,
170
171 /// Absolute byte offset to start dumping (bypasses page mode)
172 #[arg(long)]
173 offset: Option<u64>,
174
175 /// Number of bytes to dump (default: page size or 256 for offset mode)
176 #[arg(short, long)]
177 length: Option<usize>,
178
179 /// Output raw binary bytes (no formatting)
180 #[arg(long)]
181 raw: bool,
182
183 /// Override page size (default: auto-detect)
184 #[arg(long = "page-size")]
185 page_size: Option<u32>,
186
187 /// Path to MySQL keyring file for decrypting encrypted tablespaces
188 #[arg(long)]
189 keyring: Option<String>,
190
191 /// Decrypt page before dumping (requires --keyring)
192 #[arg(long)]
193 decrypt: bool,
194 },
195
196 /// Intentionally corrupt pages for testing
197 ///
198 /// Writes random bytes into a tablespace file to simulate data corruption.
199 /// Targets can be the FIL header (`-k`), the record data area (`-r`), or
200 /// an absolute byte offset (`--offset`). If no page is specified, one is
201 /// chosen at random. Use `--verify` to print before/after checksum
202 /// comparisons confirming the page is now invalid — useful for verifying
203 /// that `inno checksum` correctly detects the damage.
204 Corrupt {
205 /// Path to data file
206 #[arg(short, long)]
207 file: String,
208
209 /// Page number to corrupt (random if not specified)
210 #[arg(short, long)]
211 page: Option<u64>,
212
213 /// Number of bytes to corrupt
214 #[arg(short, long, default_value = "1")]
215 bytes: usize,
216
217 /// Corrupt the FIL header area
218 #[arg(short = 'k', long = "header")]
219 header: bool,
220
221 /// Corrupt the record data area
222 #[arg(short, long)]
223 records: bool,
224
225 /// Absolute byte offset to corrupt (bypasses page calculation)
226 #[arg(long)]
227 offset: Option<u64>,
228
229 /// Show before/after checksum comparison
230 #[arg(long)]
231 verify: bool,
232
233 /// Output in JSON format
234 #[arg(long)]
235 json: bool,
236
237 /// Override page size (default: auto-detect)
238 #[arg(long = "page-size")]
239 page_size: Option<u32>,
240 },
241
242 /// Export record-level data from a tablespace
243 ///
244 /// Extracts user records from clustered index leaf pages and outputs
245 /// them as CSV, JSON, or raw hex. Uses SDI metadata (MySQL 8.0+) to
246 /// decode field types and column names. Without SDI, falls back to
247 /// hex-only output.
248 ///
249 /// Supported types: integers (TINYINT–BIGINT), FLOAT, DOUBLE,
250 /// DATE, DATETIME, TIMESTAMP, YEAR, VARCHAR, CHAR. Unsupported
251 /// types (DECIMAL, BLOB, JSON, etc.) are exported as hex strings.
252 ///
253 /// Use `--where-delete-mark` to include only delete-marked records
254 /// (useful for forensic recovery). Use `--system-columns` to include
255 /// DB_TRX_ID and DB_ROLL_PTR in the output.
256 Export {
257 /// Path to InnoDB data file (.ibd)
258 #[arg(short, long)]
259 file: String,
260
261 /// Export records from a specific page only
262 #[arg(short, long)]
263 page: Option<u64>,
264
265 /// Output format: csv, json, or hex
266 #[arg(long, default_value = "csv")]
267 format: String,
268
269 /// Include only delete-marked records
270 #[arg(long = "where-delete-mark")]
271 where_delete_mark: bool,
272
273 /// Include system columns (DB_TRX_ID, DB_ROLL_PTR) in output
274 #[arg(long = "system-columns")]
275 system_columns: bool,
276
277 /// Show additional details
278 #[arg(short, long)]
279 verbose: bool,
280
281 /// Override page size (default: auto-detect)
282 #[arg(long = "page-size")]
283 page_size: Option<u32>,
284
285 /// Path to MySQL keyring file for decrypting encrypted tablespaces
286 #[arg(long)]
287 keyring: Option<String>,
288 },
289
290 /// Search for pages across data directory
291 ///
292 /// Recursively discovers all `.ibd` files under a MySQL data directory,
293 /// opens each as a tablespace, and reads the FIL header of every page
294 /// looking for a matching `page_number` field. Optional `--checksum` and
295 /// `--space-id` filters narrow results when the same page number appears
296 /// in multiple tablespaces. Use `--first` to stop after the first match
297 /// for faster lookups.
298 ///
299 /// With `--corrupt`, scans all pages for checksum mismatches instead of
300 /// searching by page number. Reports corrupt pages with their stored and
301 /// calculated checksums plus corruption pattern classification.
302 Find {
303 /// MySQL data directory path
304 #[arg(short, long)]
305 datadir: String,
306
307 /// Page number to search for
308 #[arg(short, long)]
309 page: Option<u64>,
310
311 /// Checksum to match (page-number search only)
312 #[arg(short, long)]
313 checksum: Option<u32>,
314
315 /// Space ID to match
316 #[arg(short, long)]
317 space_id: Option<u32>,
318
319 /// Scan for pages with checksum mismatches
320 #[arg(long)]
321 corrupt: bool,
322
323 /// Stop at first match
324 #[arg(long)]
325 first: bool,
326
327 /// Output in JSON format
328 #[arg(long)]
329 json: bool,
330
331 /// Override page size (default: auto-detect)
332 #[arg(long = "page-size")]
333 page_size: Option<u32>,
334
335 /// Maximum directory recursion depth (default: 2, 0 = unlimited)
336 #[arg(long)]
337 depth: Option<u32>,
338 },
339
340 /// List/find tablespace IDs
341 ///
342 /// Scans `.ibd` and `.ibu` files under a MySQL data directory and reads
343 /// the space ID from the FSP header (page 0, offset 38) of each file.
344 /// In **list mode** (`-l`) it prints every file and its space ID; in
345 /// **lookup mode** (`-t <id>`) it finds the file that owns a specific
346 /// tablespace ID. Useful for mapping a space ID seen in error logs or
347 /// `INFORMATION_SCHEMA` back to a physical file on disk.
348 Tsid {
349 /// MySQL data directory path
350 #[arg(short, long)]
351 datadir: String,
352
353 /// List all tablespace IDs
354 #[arg(short, long)]
355 list: bool,
356
357 /// Find table file by tablespace ID
358 #[arg(short = 't', long = "tsid")]
359 tablespace_id: Option<u32>,
360
361 /// Output in JSON format
362 #[arg(long)]
363 json: bool,
364
365 /// Override page size (default: auto-detect)
366 #[arg(long = "page-size")]
367 page_size: Option<u32>,
368
369 /// Maximum directory recursion depth (default: 2, 0 = unlimited)
370 #[arg(long)]
371 depth: Option<u32>,
372 },
373
374 /// Extract SDI metadata (MySQL 8.0+)
375 ///
376 /// Locates SDI (Serialized Dictionary Information) pages in a tablespace
377 /// by scanning for page type 17853, then reassembles multi-page SDI
378 /// records by following the page chain. The zlib-compressed payload is
379 /// decompressed and printed as JSON. Each tablespace in MySQL 8.0+
380 /// embeds its own table/column/index definitions as SDI records,
381 /// eliminating the need for the `.frm` files used in older versions.
382 /// Use `--pretty` for indented JSON output.
383 Sdi {
384 /// Path to InnoDB data file (.ibd)
385 #[arg(short, long)]
386 file: String,
387
388 /// Pretty-print JSON output
389 #[arg(short, long)]
390 pretty: bool,
391
392 /// Override page size (default: auto-detect)
393 #[arg(long = "page-size")]
394 page_size: Option<u32>,
395
396 /// Path to MySQL keyring file for decrypting encrypted tablespaces
397 #[arg(long)]
398 keyring: Option<String>,
399 },
400
401 /// Extract schema and reconstruct DDL from tablespace metadata
402 ///
403 /// Reads SDI (Serialized Dictionary Information) from MySQL 8.0+
404 /// tablespaces, parses the embedded data dictionary JSON into typed
405 /// column, index, and foreign key definitions, and reconstructs a
406 /// complete `CREATE TABLE` DDL statement. For pre-8.0 tablespaces
407 /// that lack SDI, scans INDEX pages to infer basic index structure
408 /// and record format (compact vs. redundant).
409 ///
410 /// Use `--verbose` for a structured breakdown of columns, indexes,
411 /// and foreign keys above the DDL. Use `--json` for machine-readable
412 /// output including the full schema definition and DDL as a JSON object.
413 Schema {
414 /// Path to InnoDB data file (.ibd)
415 #[arg(short, long)]
416 file: String,
417
418 /// Show structured schema breakdown above the DDL
419 #[arg(short, long)]
420 verbose: bool,
421
422 /// Output in JSON format
423 #[arg(long)]
424 json: bool,
425
426 /// Override page size (default: auto-detect)
427 #[arg(long = "page-size")]
428 page_size: Option<u32>,
429
430 /// Path to MySQL keyring file for decrypting encrypted tablespaces
431 #[arg(long)]
432 keyring: Option<String>,
433 },
434
435 /// Analyze InnoDB redo log files
436 ///
437 /// Opens an InnoDB redo log file (`ib_logfile0`/`ib_logfile1` for
438 /// MySQL < 8.0.30, or `#ib_redo*` files for 8.0.30+) and displays
439 /// the log file header, both checkpoint records, and per-block details
440 /// including block number, data length, checkpoint number, and CRC-32C
441 /// checksum status. With `--verbose`, MLOG record types within each
442 /// data block are decoded and summarized. Use `--blocks N` to limit
443 /// output to the first N data blocks, or `--no-empty` to skip blocks
444 /// that contain no redo data.
445 Log {
446 /// Path to redo log file (ib_logfile0, ib_logfile1, or #ib_redo*)
447 #[arg(short, long)]
448 file: String,
449
450 /// Limit to first N data blocks
451 #[arg(short, long)]
452 blocks: Option<u64>,
453
454 /// Skip empty blocks
455 #[arg(long)]
456 no_empty: bool,
457
458 /// Display additional information
459 #[arg(short, long)]
460 verbose: bool,
461
462 /// Output in JSON format
463 #[arg(long)]
464 json: bool,
465 },
466
467 /// Show InnoDB file and system information
468 ///
469 /// Operates in three modes. **`--ibdata`** reads the `ibdata1` page 0
470 /// FIL header and redo log checkpoint LSNs. **`--lsn-check`** compares
471 /// the `ibdata1` header LSN with the latest redo log checkpoint LSN to
472 /// detect whether the system tablespace and redo log are in sync (useful
473 /// for diagnosing crash-recovery state). **`-D`/`-t`** queries a live
474 /// MySQL instance via `INFORMATION_SCHEMA.INNODB_TABLES` and
475 /// `INNODB_INDEXES` for tablespace IDs, table IDs, index root pages,
476 /// and key InnoDB status metrics (requires the `mysql` feature).
477 Info {
478 /// Inspect ibdata1 page 0 header
479 #[arg(long)]
480 ibdata: bool,
481
482 /// Compare ibdata1 and redo log LSNs
483 #[arg(long = "lsn-check")]
484 lsn_check: bool,
485
486 /// MySQL data directory path
487 #[arg(short, long)]
488 datadir: Option<String>,
489
490 /// Database name (for table/index info)
491 #[arg(short = 'D', long)]
492 database: Option<String>,
493
494 /// Table name (for table/index info)
495 #[arg(short, long)]
496 table: Option<String>,
497
498 /// MySQL host
499 #[arg(long)]
500 host: Option<String>,
501
502 /// MySQL port
503 #[arg(long)]
504 port: Option<u16>,
505
506 /// MySQL user
507 #[arg(long)]
508 user: Option<String>,
509
510 /// MySQL password
511 #[arg(long)]
512 password: Option<String>,
513
514 /// Path to MySQL defaults file (.my.cnf)
515 #[arg(long = "defaults-file")]
516 defaults_file: Option<String>,
517
518 /// Scan data directory and produce a tablespace ID mapping
519 #[arg(long = "tablespace-map")]
520 tablespace_map: bool,
521
522 /// Output in JSON format
523 #[arg(long)]
524 json: bool,
525
526 /// Override page size (default: auto-detect)
527 #[arg(long = "page-size")]
528 page_size: Option<u32>,
529 },
530
531 /// Recover data from corrupt/damaged tablespace files
532 ///
533 /// Scans a tablespace file and classifies each page as intact, corrupt,
534 /// empty, or unreadable. For INDEX pages, counts recoverable user records
535 /// by walking the compact record chain. Produces a recovery assessment
536 /// showing how many pages and records can be salvaged.
537 ///
538 /// Use `--force` to also extract records from pages with bad checksums
539 /// but valid-looking headers — useful when data is partially damaged
540 /// but the record chain is still intact. Use `--page-size` to override
541 /// page size detection when page 0 is corrupt.
542 ///
543 /// With `--verbose`, per-page details are shown including page type,
544 /// status, LSN, and record count. With `--json`, a structured report
545 /// is emitted including optional per-record detail when combined with
546 /// `--verbose`.
547 Recover {
548 /// Path to InnoDB data file (.ibd)
549 #[arg(short, long)]
550 file: String,
551
552 /// Analyze a single page instead of full scan
553 #[arg(short, long)]
554 page: Option<u64>,
555
556 /// Show per-page details
557 #[arg(short, long)]
558 verbose: bool,
559
560 /// Output in JSON format
561 #[arg(long)]
562 json: bool,
563
564 /// Extract records from corrupt pages with valid headers
565 #[arg(long)]
566 force: bool,
567
568 /// Override page size (critical when page 0 is corrupt)
569 #[arg(long = "page-size")]
570 page_size: Option<u32>,
571
572 /// Path to MySQL keyring file for decrypting encrypted tablespaces
573 #[arg(long)]
574 keyring: Option<String>,
575
576 /// Stream results incrementally for lower memory usage (disables parallel processing)
577 #[arg(long)]
578 streaming: bool,
579
580 /// Write a new tablespace from recoverable pages to the given path
581 #[arg(long)]
582 rebuild: Option<String>,
583 },
584
585 /// Validate page checksums
586 ///
587 /// Reads every page in a tablespace and validates its stored checksum
588 /// against both CRC-32C (MySQL 5.7.7+) and legacy InnoDB algorithms.
589 /// Also checks that the header LSN low-32 bits match the FIL trailer.
590 /// All-zero pages are counted as empty and skipped. With `--verbose`,
591 /// per-page results are printed including the detected algorithm and
592 /// stored vs. calculated values. Exits with code 1 if any page has an
593 /// invalid checksum, making it suitable for use in scripts and CI.
594 Checksum {
595 /// Path to InnoDB data file (.ibd)
596 #[arg(short, long)]
597 file: String,
598
599 /// Show per-page checksum details
600 #[arg(short, long)]
601 verbose: bool,
602
603 /// Output in JSON format
604 #[arg(long)]
605 json: bool,
606
607 /// Override page size (default: auto-detect)
608 #[arg(long = "page-size")]
609 page_size: Option<u32>,
610
611 /// Path to MySQL keyring file for decrypting encrypted tablespaces
612 #[arg(long)]
613 keyring: Option<String>,
614
615 /// Stream results incrementally for lower memory usage (disables parallel processing)
616 #[arg(long)]
617 streaming: bool,
618 },
619
620 /// Monitor a tablespace file for page-level changes
621 ///
622 /// Polls an InnoDB tablespace file at a configurable interval and reports
623 /// which pages have been modified, added, or removed since the last poll.
624 /// Change detection is based on LSN comparison — if a page's LSN changes
625 /// between polls, it was modified by a write. Checksums are validated for
626 /// each changed page to detect corruption during writes.
627 ///
628 /// The tablespace is re-opened each cycle to detect file growth and avoid
629 /// stale file handles. Use `--verbose` for per-field diffs on changed
630 /// pages, or `--json` for NDJSON streaming output (one JSON object per
631 /// line). Press Ctrl+C for a clean exit with a summary of total changes.
632 Watch {
633 /// Path to InnoDB data file (.ibd)
634 #[arg(short, long)]
635 file: String,
636
637 /// Polling interval in milliseconds
638 #[arg(short, long, default_value = "1000")]
639 interval: u64,
640
641 /// Show per-field diffs for changed pages
642 #[arg(short, long)]
643 verbose: bool,
644
645 /// Output in NDJSON streaming format
646 #[arg(long)]
647 json: bool,
648
649 /// Emit per-page NDJSON change events (audit-log compatible)
650 #[arg(long)]
651 events: bool,
652
653 /// Override page size (default: auto-detect)
654 #[arg(long = "page-size")]
655 page_size: Option<u32>,
656
657 /// Path to MySQL keyring file for decrypting encrypted tablespaces
658 #[arg(long)]
659 keyring: Option<String>,
660 },
661
662 /// Repair corrupt page checksums
663 ///
664 /// Recalculates and writes correct checksums for pages with invalid checksums
665 /// or LSN mismatches. By default, auto-detects the checksum algorithm from
666 /// page 0 and creates a `.bak` backup before modifying the file. Use
667 /// `--algorithm` to force a specific algorithm, `--dry-run` to preview
668 /// repairs without modifying the file, or `--no-backup` to skip the backup.
669 Repair {
670 /// Path to InnoDB data file (.ibd)
671 #[arg(short, long)]
672 file: Option<String>,
673
674 /// Repair all .ibd files under a data directory
675 #[arg(long)]
676 batch: Option<String>,
677
678 /// Repair only a specific page number
679 #[arg(short, long)]
680 page: Option<u64>,
681
682 /// Checksum algorithm: auto, crc32c, innodb, full_crc32
683 #[arg(short, long, default_value = "auto")]
684 algorithm: String,
685
686 /// Skip creating a backup before repair
687 #[arg(long)]
688 no_backup: bool,
689
690 /// Preview repairs without modifying the file
691 #[arg(long)]
692 dry_run: bool,
693
694 /// Show per-page repair details
695 #[arg(short, long)]
696 verbose: bool,
697
698 /// Output in JSON format
699 #[arg(long)]
700 json: bool,
701
702 /// Override page size (default: auto-detect)
703 #[arg(long = "page-size")]
704 page_size: Option<u32>,
705
706 /// Path to MySQL keyring file for decrypting encrypted tablespaces
707 #[arg(long)]
708 keyring: Option<String>,
709 },
710
711 /// Compare two tablespace files page-by-page
712 ///
713 /// Reads two InnoDB tablespace files and compares them page-by-page,
714 /// reporting which pages are identical, modified, or only present in
715 /// one file. With `--verbose`, per-page FIL header field diffs are
716 /// shown for modified pages, highlighting changes to checksums, LSNs,
717 /// page types, and space IDs. Add `--byte-ranges` (with `-v`) to see
718 /// the exact byte offsets where page content differs. Use `-p` to
719 /// compare a single page, or `--json` for machine-readable output.
720 ///
721 /// When files have different page sizes, only FIL headers (first 38
722 /// bytes) are compared and a warning is displayed.
723 Diff {
724 /// First InnoDB data file (.ibd)
725 file1: String,
726
727 /// Second InnoDB data file (.ibd)
728 file2: String,
729
730 /// Show per-page header field diffs
731 #[arg(short, long)]
732 verbose: bool,
733
734 /// Show byte-range diffs for changed pages (requires -v)
735 #[arg(short = 'b', long = "byte-ranges")]
736 byte_ranges: bool,
737
738 /// Compare a single page only
739 #[arg(short, long)]
740 page: Option<u64>,
741
742 /// Annotate diff with MySQL version information from SDI metadata
743 #[arg(long = "version-aware")]
744 version_aware: bool,
745
746 /// Output in JSON format
747 #[arg(long)]
748 json: bool,
749
750 /// Override page size (default: auto-detect)
751 #[arg(long = "page-size")]
752 page_size: Option<u32>,
753
754 /// Path to MySQL keyring file for decrypting encrypted tablespaces
755 #[arg(long)]
756 keyring: Option<String>,
757 },
758
759 /// Copy specific pages from a donor tablespace into a target
760 ///
761 /// Reads pages from the donor file and writes them into the target file at
762 /// the same page number. Safety checks ensure page sizes and space IDs
763 /// match. Page 0 (FSP_HDR) is rejected unless `--force` is used. Donor
764 /// pages with invalid checksums are skipped unless `--force` is used.
765 ///
766 /// A backup of the target is created by default. Use `--dry-run` to preview
767 /// which pages would be transplanted without modifying the target.
768 Transplant {
769 /// Path to donor tablespace file (source of pages)
770 donor: String,
771
772 /// Path to target tablespace file (destination)
773 target: String,
774
775 /// Page numbers to transplant (comma-separated)
776 #[arg(short, long, value_delimiter = ',')]
777 pages: Vec<u64>,
778
779 /// Skip creating a backup of the target
780 #[arg(long)]
781 no_backup: bool,
782
783 /// Allow space ID mismatch, corrupt donor pages, and page 0 transplant
784 #[arg(long)]
785 force: bool,
786
787 /// Preview without modifying the target file
788 #[arg(long)]
789 dry_run: bool,
790
791 /// Show per-page details
792 #[arg(short, long)]
793 verbose: bool,
794
795 /// Output in JSON format
796 #[arg(long)]
797 json: bool,
798
799 /// Override page size (default: auto-detect)
800 #[arg(long = "page-size")]
801 page_size: Option<u32>,
802
803 /// Path to MySQL keyring file for decrypting encrypted tablespaces
804 #[arg(long)]
805 keyring: Option<String>,
806 },
807
808 /// Per-index B+Tree health metrics
809 ///
810 /// Scans all INDEX pages in a tablespace and computes per-index health
811 /// metrics including fill factor (average, min, max), garbage ratio,
812 /// fragmentation, tree depth, and page counts. Optionally resolves
813 /// index names from SDI metadata (MySQL 8.0+).
814 ///
815 /// Use `--verbose` for additional detail including total records and
816 /// empty leaf page counts. Use `--json` for machine-readable output.
817 Health {
818 /// Path to InnoDB data file (.ibd)
819 #[arg(short, long)]
820 file: String,
821
822 /// Show additional detail (records, empty leaves)
823 #[arg(short, long)]
824 verbose: bool,
825
826 /// Output in JSON format
827 #[arg(long, conflicts_with = "prometheus")]
828 json: bool,
829
830 /// Output metrics in Prometheus exposition format
831 #[arg(long, conflicts_with = "json")]
832 prometheus: bool,
833
834 /// Override page size (default: auto-detect)
835 #[arg(long = "page-size")]
836 page_size: Option<u32>,
837
838 /// Path to MySQL keyring file for decrypting encrypted tablespaces
839 #[arg(long)]
840 keyring: Option<String>,
841 },
842
843 /// Audit a MySQL data directory for integrity, health, or corruption
844 ///
845 /// Scans all `.ibd` files under a data directory and validates checksums,
846 /// computes health metrics, or lists corrupt pages — replacing the need
847 /// to run `inno checksum` or `inno health` file-by-file. Three modes:
848 ///
849 /// **Default (integrity)**: validates checksums across all tablespace files
850 /// and reports per-file pass/fail with a directory-wide integrity percentage.
851 ///
852 /// **`--health`**: computes per-tablespace fill factor, fragmentation, and
853 /// garbage ratio, ranked worst-first. Use `--min-fill-factor` and
854 /// `--max-fragmentation` to filter for unhealthy tablespaces only.
855 ///
856 /// **`--checksum-mismatch`**: compact listing of only corrupt pages with
857 /// stored vs. calculated checksums, suitable for piping to `inno repair`.
858 ///
859 /// `--health` and `--checksum-mismatch` are mutually exclusive.
860 Audit {
861 /// MySQL data directory path
862 #[arg(short, long)]
863 datadir: String,
864
865 /// Show per-tablespace health metrics instead of checksum validation
866 #[arg(long)]
867 health: bool,
868
869 /// List only pages with checksum mismatches (compact output)
870 #[arg(long = "checksum-mismatch", conflicts_with = "prometheus")]
871 checksum_mismatch: bool,
872
873 /// Show additional details (per-page results in default mode)
874 #[arg(short, long)]
875 verbose: bool,
876
877 /// Output in JSON format
878 #[arg(long, conflicts_with = "prometheus")]
879 json: bool,
880
881 /// Output metrics in Prometheus exposition format
882 #[arg(long, conflicts_with = "json", conflicts_with = "checksum_mismatch")]
883 prometheus: bool,
884
885 /// Override page size (default: auto-detect per file)
886 #[arg(long = "page-size")]
887 page_size: Option<u32>,
888
889 /// Path to MySQL keyring file for decrypting encrypted tablespaces
890 #[arg(long)]
891 keyring: Option<String>,
892
893 /// Show tables with fill factor below this threshold (0-100, --health only)
894 #[arg(long = "min-fill-factor")]
895 min_fill_factor: Option<f64>,
896
897 /// Show tables with fragmentation above this threshold (0-100, --health only)
898 #[arg(long = "max-fragmentation")]
899 max_fragmentation: Option<f64>,
900
901 /// Maximum directory recursion depth (default: 2, 0 = unlimited)
902 #[arg(long)]
903 depth: Option<u32>,
904 },
905
906 /// Check tablespace compatibility with a target MySQL version
907 ///
908 /// Analyzes tablespace files to determine whether they are compatible
909 /// with a target MySQL version. Checks include page size support, row
910 /// format, encryption, compression, SDI presence, and vendor
911 /// compatibility (MariaDB tablespaces are flagged as incompatible).
912 ///
913 /// **Single-file mode** (`--file`): checks one tablespace and reports
914 /// all compatibility findings with severity levels (info/warning/error).
915 ///
916 /// **Directory scan mode** (`--scan`): discovers all `.ibd` files under
917 /// a data directory, runs compatibility checks on each in parallel, and
918 /// produces a per-file summary plus an overall compatible/incompatible
919 /// count.
920 ///
921 /// `--file` and `--scan` are mutually exclusive.
922 Compat {
923 /// Path to InnoDB data file (.ibd)
924 #[arg(short, long)]
925 file: Option<String>,
926
927 /// Scan a data directory for compatibility issues (mutually exclusive with --file)
928 #[arg(short, long)]
929 scan: Option<String>,
930
931 /// Target MySQL version (e.g., "8.4.0", "9.0.0")
932 #[arg(short, long)]
933 target: String,
934
935 /// Show detailed check information
936 #[arg(short, long)]
937 verbose: bool,
938
939 /// Output in JSON format
940 #[arg(long)]
941 json: bool,
942
943 /// Override page size (default: auto-detect)
944 #[arg(long = "page-size")]
945 page_size: Option<u32>,
946
947 /// Path to MySQL keyring file for decrypting encrypted tablespaces
948 #[arg(long)]
949 keyring: Option<String>,
950
951 /// Maximum directory recursion depth (default: 2, 0 = unlimited)
952 #[arg(long)]
953 depth: Option<u32>,
954 },
955
956 /// Defragment a tablespace by reclaiming free space and reordering pages
957 ///
958 /// Reads all pages from a source tablespace, removes empty and corrupt
959 /// pages, sorts INDEX pages by (index_id, level, page_number), fixes
960 /// prev/next chain pointers within each index group, renumbers pages
961 /// sequentially, rebuilds page 0, recalculates all checksums, and writes
962 /// the result to a new output file. The source file is never modified.
963 Defrag {
964 /// Path to source InnoDB data file (.ibd)
965 #[arg(short, long)]
966 file: String,
967
968 /// Path to output file (required — always writes a new file)
969 #[arg(short, long)]
970 output: String,
971
972 /// Show per-page details
973 #[arg(short, long)]
974 verbose: bool,
975
976 /// Output in JSON format
977 #[arg(long)]
978 json: bool,
979
980 /// Override page size (default: auto-detect)
981 #[arg(long = "page-size")]
982 page_size: Option<u32>,
983
984 /// Path to MySQL keyring file for decrypting encrypted tablespaces
985 #[arg(long)]
986 keyring: Option<String>,
987 },
988
989 /// Cross-validate tablespace files against live MySQL metadata
990 ///
991 /// Scans a data directory for .ibd files and compares their space IDs
992 /// against MySQL's INFORMATION_SCHEMA.INNODB_TABLESPACES. Detects
993 /// orphan files (on disk but not in MySQL), missing tablespaces (in
994 /// MySQL but not on disk), and space ID mismatches. Requires --host
995 /// and --user for MySQL connection (mysql feature must be compiled).
996 Validate {
997 /// Path to MySQL data directory
998 #[arg(short, long)]
999 datadir: String,
1000
1001 /// Database name to filter (optional)
1002 #[arg(short = 'D', long)]
1003 database: Option<String>,
1004
1005 /// Deep-validate a specific table (format: db.table or db/table)
1006 #[arg(short = 't', long)]
1007 table: Option<String>,
1008
1009 /// MySQL host
1010 #[arg(long)]
1011 host: Option<String>,
1012
1013 /// MySQL port
1014 #[arg(long)]
1015 port: Option<u16>,
1016
1017 /// MySQL user
1018 #[arg(short, long)]
1019 user: Option<String>,
1020
1021 /// MySQL password
1022 #[arg(short, long)]
1023 password: Option<String>,
1024
1025 /// Path to MySQL defaults file (.my.cnf)
1026 #[arg(long = "defaults-file")]
1027 defaults_file: Option<String>,
1028
1029 /// Show detailed output
1030 #[arg(short, long)]
1031 verbose: bool,
1032
1033 /// Output in JSON format
1034 #[arg(long)]
1035 json: bool,
1036
1037 /// Override page size (default: auto-detect)
1038 #[arg(long = "page-size")]
1039 page_size: Option<u32>,
1040
1041 /// Maximum directory recursion depth (default: 2, 0 = unlimited)
1042 #[arg(long)]
1043 depth: Option<u32>,
1044 },
1045
1046 /// Verify structural integrity of a tablespace
1047 ///
1048 /// Runs pure structural checks on a tablespace file without requiring
1049 /// valid checksums. Checks page number sequence, space ID consistency,
1050 /// LSN monotonicity, B+Tree level validity, page chain bounds, and
1051 /// trailer LSN matching. Exits with code 1 if any check fails.
1052 Verify {
1053 /// Path to InnoDB data file (.ibd)
1054 #[arg(short, long)]
1055 file: String,
1056
1057 /// Show per-page findings
1058 #[arg(short, long)]
1059 verbose: bool,
1060
1061 /// Output in JSON format
1062 #[arg(long)]
1063 json: bool,
1064
1065 /// Override page size (default: auto-detect)
1066 #[arg(long = "page-size")]
1067 page_size: Option<u32>,
1068
1069 /// Path to MySQL keyring file for decrypting encrypted tablespaces
1070 #[arg(long)]
1071 keyring: Option<String>,
1072
1073 /// Path to redo log file to verify LSN continuity against the tablespace
1074 #[arg(long)]
1075 redo: Option<String>,
1076
1077 /// Verify backup chain continuity across multiple tablespace files
1078 #[arg(long = "chain", num_args = 1..)]
1079 chain: Vec<String>,
1080 },
1081
1082 /// Parse and analyze MySQL binary log files
1083 ///
1084 /// Reads the format description event, then iterates all events in the
1085 /// binary log to produce type distribution statistics, table map details,
1086 /// and an event listing. Supports filtering by event type and limiting
1087 /// the number of events displayed.
1088 Binlog {
1089 /// Path to MySQL binary log file
1090 #[arg(short, long)]
1091 file: String,
1092
1093 /// Maximum number of events to display
1094 #[arg(short, long)]
1095 limit: Option<usize>,
1096
1097 /// Filter events by type name (e.g. TABLE_MAP, WRITE_ROWS)
1098 #[arg(long = "filter-type")]
1099 filter_type: Option<String>,
1100
1101 /// Show additional detail (column types for TABLE_MAP events)
1102 #[arg(short, long)]
1103 verbose: bool,
1104
1105 /// Output in JSON format
1106 #[arg(long)]
1107 json: bool,
1108 },
1109
1110 /// Analyze undo tablespace files (.ibu or .ibd)
1111 ///
1112 /// Reads rollback segment arrays, rollback segment headers, and undo
1113 /// segment pages to report transaction history and segment states.
1114 /// Supports MySQL 8.0+ dedicated undo tablespaces (.ibu) and legacy
1115 /// system tablespace undo logs.
1116 Undo {
1117 /// Path to InnoDB undo tablespace file (.ibu or .ibd)
1118 #[arg(short, long)]
1119 file: String,
1120
1121 /// Show a specific undo page only
1122 #[arg(short, long)]
1123 page: Option<u64>,
1124
1125 /// Show additional detail including undo records
1126 #[arg(short, long)]
1127 verbose: bool,
1128
1129 /// Output in JSON format
1130 #[arg(long)]
1131 json: bool,
1132
1133 /// Override page size (default: auto-detect)
1134 #[arg(long = "page-size")]
1135 page_size: Option<u32>,
1136
1137 /// Path to MySQL keyring file for decrypting encrypted tablespaces
1138 #[arg(long)]
1139 keyring: Option<String>,
1140 },
1141
1142 /// Recover deleted records from a tablespace
1143 ///
1144 /// Scans InnoDB tablespace files for deleted records using three strategies:
1145 /// delete-marked records still in the active B+Tree chain (confidence 1.0),
1146 /// records in the page free list (confidence 0.3-0.7), and optionally
1147 /// DEL_MARK_REC entries in undo log pages (confidence 0.1-0.3).
1148 ///
1149 /// Output formats include CSV (default), JSON array of record objects,
1150 /// SQL INSERT statements, or hex dump. Use `--json` for a full metadata
1151 /// envelope including summary statistics.
1152 ///
1153 /// With `--undo-file`, provide an undo tablespace (ibdata1 or .ibu) to
1154 /// enable undo log scanning for additional PK-only recovery.
1155 Undelete {
1156 /// Path to InnoDB data file (.ibd)
1157 #[arg(short, long)]
1158 file: String,
1159
1160 /// Path to undo tablespace (ibdata1 or .ibu) for undo log scanning
1161 #[arg(long = "undo-file")]
1162 undo_file: Option<String>,
1163
1164 /// Filter by table name
1165 #[arg(short, long)]
1166 table: Option<String>,
1167
1168 /// Minimum transaction ID to include
1169 #[arg(long = "min-trx-id")]
1170 min_trx_id: Option<u64>,
1171
1172 /// Minimum confidence threshold (0.0-1.0, default: 0.0)
1173 #[arg(long, default_value = "0.0")]
1174 confidence: f64,
1175
1176 /// Record output format: csv, json, sql, hex
1177 #[arg(long, default_value = "csv")]
1178 format: String,
1179
1180 /// Show additional details
1181 #[arg(short, long)]
1182 verbose: bool,
1183
1184 /// Recover from a specific page only
1185 #[arg(short, long)]
1186 page: Option<u64>,
1187
1188 /// Output full metadata JSON envelope
1189 #[arg(long)]
1190 json: bool,
1191
1192 /// Override page size (default: auto-detect)
1193 #[arg(long = "page-size")]
1194 page_size: Option<u32>,
1195
1196 /// Path to MySQL keyring file for decrypting encrypted tablespaces
1197 #[arg(long)]
1198 keyring: Option<String>,
1199 },
1200
1201 /// Generate shell completion scripts
1202 Completions {
1203 /// Shell to generate completions for
1204 shell: clap_complete::Shell,
1205 },
1206}