1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(clippy::upper_case_acronyms)]
// #![allow(unused_imports)]

use std::path::PathBuf;

use anyhow::Result;
use clap::{Parser, Subcommand};
use ibd::redo::LogRecordTypes;
use log::info;

mod app;
mod factory;
mod ibd;
mod meta;
mod sdi;
mod util;

#[derive(Debug, Parser)]
#[command(author, version, about = "The innobase datafile(*.ibd) reader", long_about = None)]
pub struct Args {
    /// Input innodb datafile. for example departments.ibd
    input: PathBuf,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Debug, Subcommand)]
enum Commands {
    /// Print basic information.
    Info,

    /// List all page. page_type, page_number and more
    List {
        /// List index data
        #[arg(short, long, default_value_t = false)]
        index: bool,

        /// List segment data
        #[arg(short, long, default_value_t = false)]
        segment: bool,

        /// List extent data
        #[arg(short, long, default_value_t = false)]
        extent: bool,

        /// List page data
        #[arg(short, long, default_value_t = false)]
        page: bool,

        /// List all: index, segment, extent, page, ...
        #[arg(short, long, default_value_t = false)]
        all: bool,

        /// Limit the total data rows
        #[arg(short, long, default_value_t = 65535)]
        limit: usize,
    },

    /// Describe datafile information by sdi page
    Desc,

    /// Print SDI json
    Sdi {
        /// Print parsed table definition
        #[arg(short, long, default_value_t = false)]
        table_define: bool,
        /// Print index root segements
        #[arg(short, long, default_value_t = false)]
        root_segments: bool,
    },

    /// View page data with given page_no.
    View {
        /// The page number, starts from 0.
        page_no: usize,
    },

    /// Dump index page user records
    Dump {
        /// The page number, starts from 0.
        page_no: Option<usize>,

        /// Limit the total row in the dump
        #[arg(short, long, default_value_t = 10)]
        limit: usize,

        /// Dump the garbage list
        #[arg(short, long, default_value_t = false)]
        garbage: bool,

        /// Print more information
        #[arg(short, long, default_value_t = false)]
        verbose: bool,

        /// Dump the B+ tree root
        #[arg(short, long)]
        btree_root: Option<usize>,
    },

    /// Undo log print
    Undo {
        /// The page number, starts from 0.
        page_no: usize,

        /// The byte offset inside page
        boffset: usize,

        /// Number of unique keys, default value is 1.
        #[arg(short, long, default_value_t = 1)]
        n_uniq: usize,
    },

    /// Redo log print
    Redo {
        /// The block number, starts from 0.
        block_no: Option<usize>,

        /// Dump given log_type redo blocks, log_type like MLOG_xxx, MLOG_1BYTE,
        /// MLOG_REC_INSERT ...
        #[arg(short, long)]
        dump_log_type: Option<LogRecordTypes>,
    },
}

fn main() -> Result<()> {
    util::init();

    let args = Args::parse();
    let mut app = app::App::new(args.input);

    app.run(args.command)?;

    info!("time costs {:?}", app.time_costs());
    Ok(())
}