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 {
9 pub file: String,
10 pub pretty: bool,
11 pub page_size: Option<u32>,
12}
13
14pub fn execute(opts: &SdiOptions, writer: &mut dyn Write) -> Result<(), IdbError> {
15 let mut ts = match opts.page_size {
16 Some(ps) => Tablespace::open_with_page_size(&opts.file, ps)?,
17 None => Tablespace::open(&opts.file)?,
18 };
19
20 let sdi_pages = sdi::find_sdi_pages(&mut ts)?;
22
23 if sdi_pages.is_empty() {
24 wprintln!(writer, "No SDI pages found in {}.", opts.file)?;
25 wprintln!(writer, "SDI is only available in MySQL 8.0+ tablespaces.")?;
26 return Ok(());
27 }
28
29 wprintln!(writer, "Found {} SDI page(s): {:?}", sdi_pages.len(), sdi_pages)?;
30
31 let records = sdi::extract_sdi_from_pages(&mut ts, &sdi_pages)?;
33
34 if records.is_empty() {
35 wprintln!(writer, "No SDI records found (pages may be non-leaf or empty).")?;
36 return Ok(());
37 }
38
39 for rec in &records {
40 wprintln!(writer)?;
41 wprintln!(
42 writer,
43 "=== SDI Record: type={} ({}), id={}",
44 rec.sdi_type,
45 sdi::sdi_type_name(rec.sdi_type),
46 rec.sdi_id
47 )?;
48 wprintln!(
49 writer,
50 "Compressed: {} bytes, Uncompressed: {} bytes",
51 rec.compressed_len, rec.uncompressed_len
52 )?;
53
54 if rec.data.is_empty() {
55 wprintln!(writer, "(Data could not be decompressed - may span multiple pages)")?;
56 continue;
57 }
58
59 if opts.pretty {
60 match serde_json::from_str::<serde_json::Value>(&rec.data) {
62 Ok(json) => {
63 wprintln!(
64 writer,
65 "{}",
66 serde_json::to_string_pretty(&json).unwrap_or(rec.data.clone())
67 )?;
68 }
69 Err(_) => {
70 wprintln!(writer, "{}", rec.data)?;
71 }
72 }
73 } else {
74 wprintln!(writer, "{}", rec.data)?;
75 }
76 }
77
78 wprintln!(writer)?;
79 wprintln!(writer, "Total SDI records: {}", records.len())?;
80
81 Ok(())
82}