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!(writer, "Found {} SDI page(s): {:?}", sdi_pages.len(), sdi_pages)?;
51
52 let records = sdi::extract_sdi_from_pages(&mut ts, &sdi_pages)?;
54
55 if records.is_empty() {
56 wprintln!(writer, "No SDI records found (pages may be non-leaf or empty).")?;
57 return Ok(());
58 }
59
60 for rec in &records {
61 wprintln!(writer)?;
62 wprintln!(
63 writer,
64 "=== SDI Record: type={} ({}), id={}",
65 rec.sdi_type,
66 sdi::sdi_type_name(rec.sdi_type),
67 rec.sdi_id
68 )?;
69 wprintln!(
70 writer,
71 "Compressed: {} bytes, Uncompressed: {} bytes",
72 rec.compressed_len, rec.uncompressed_len
73 )?;
74
75 if rec.data.is_empty() {
76 wprintln!(writer, "(Data could not be decompressed - may span multiple pages)")?;
77 continue;
78 }
79
80 if opts.pretty {
81 match serde_json::from_str::<serde_json::Value>(&rec.data) {
83 Ok(json) => {
84 wprintln!(
85 writer,
86 "{}",
87 serde_json::to_string_pretty(&json).unwrap_or(rec.data.clone())
88 )?;
89 }
90 Err(_) => {
91 wprintln!(writer, "{}", rec.data)?;
92 }
93 }
94 } else {
95 wprintln!(writer, "{}", rec.data)?;
96 }
97 }
98
99 wprintln!(writer)?;
100 wprintln!(writer, "Total SDI records: {}", records.len())?;
101
102 Ok(())
103}