1use std::io::Write;
2
3use crate::cli::wprintln;
4use crate::innodb::sdi;
5use crate::innodb::tablespace::Tablespace;
6use crate::IdbError;
7
8pub struct SdiOptions {
10 pub file: String,
12 pub pretty: bool,
14 pub page_size: Option<u32>,
16}
17
18pub fn execute(opts: &SdiOptions, writer: &mut dyn Write) -> Result<(), IdbError> {
36 let mut ts = match opts.page_size {
37 Some(ps) => Tablespace::open_with_page_size(&opts.file, ps)?,
38 None => Tablespace::open(&opts.file)?,
39 };
40
41 let sdi_pages = sdi::find_sdi_pages(&mut ts)?;
43
44 if sdi_pages.is_empty() {
45 wprintln!(writer, "No SDI pages found in {}.", opts.file)?;
46 wprintln!(writer, "SDI is only available in MySQL 8.0+ tablespaces.")?;
47 return Ok(());
48 }
49
50 wprintln!(
51 writer,
52 "Found {} SDI page(s): {:?}",
53 sdi_pages.len(),
54 sdi_pages
55 )?;
56
57 let records = sdi::extract_sdi_from_pages(&mut ts, &sdi_pages)?;
59
60 if records.is_empty() {
61 wprintln!(
62 writer,
63 "No SDI records found (pages may be non-leaf or empty)."
64 )?;
65 return Ok(());
66 }
67
68 for rec in &records {
69 wprintln!(writer)?;
70 wprintln!(
71 writer,
72 "=== SDI Record: type={} ({}), id={}",
73 rec.sdi_type,
74 sdi::sdi_type_name(rec.sdi_type),
75 rec.sdi_id
76 )?;
77 wprintln!(
78 writer,
79 "Compressed: {} bytes, Uncompressed: {} bytes",
80 rec.compressed_len,
81 rec.uncompressed_len
82 )?;
83
84 if rec.data.is_empty() {
85 wprintln!(
86 writer,
87 "(Data could not be decompressed - may span multiple pages)"
88 )?;
89 continue;
90 }
91
92 if opts.pretty {
93 match serde_json::from_str::<serde_json::Value>(&rec.data) {
95 Ok(json) => {
96 wprintln!(
97 writer,
98 "{}",
99 serde_json::to_string_pretty(&json).unwrap_or(rec.data.clone())
100 )?;
101 }
102 Err(_) => {
103 wprintln!(writer, "{}", rec.data)?;
104 }
105 }
106 } else {
107 wprintln!(writer, "{}", rec.data)?;
108 }
109 }
110
111 wprintln!(writer)?;
112 wprintln!(writer, "Total SDI records: {}", records.len())?;
113
114 Ok(())
115}