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 pub keyring: Option<String>,
18}
19
20pub fn execute(opts: &SdiOptions, writer: &mut dyn Write) -> Result<(), IdbError> {
38 let mut ts = match opts.page_size {
39 Some(ps) => Tablespace::open_with_page_size(&opts.file, ps)?,
40 None => Tablespace::open(&opts.file)?,
41 };
42
43 if let Some(ref keyring_path) = opts.keyring {
44 crate::cli::setup_decryption(&mut ts, keyring_path)?;
45 }
46
47 if ts.vendor_info().vendor == crate::innodb::vendor::InnoDbVendor::MariaDB {
49 return Err(IdbError::Argument(
50 "SDI is not available for MariaDB tablespaces. MariaDB does not use \
51 Serialized Dictionary Information (SDI); table metadata is stored \
52 in the data dictionary (mysql.* tables) or .frm files."
53 .to_string(),
54 ));
55 }
56
57 let sdi_pages = sdi::find_sdi_pages(&mut ts)?;
59
60 if sdi_pages.is_empty() {
61 wprintln!(writer, "No SDI pages found in {}.", opts.file)?;
62 wprintln!(writer, "SDI is only available in MySQL 8.0+ tablespaces.")?;
63 return Ok(());
64 }
65
66 wprintln!(
67 writer,
68 "Found {} SDI page(s): {:?}",
69 sdi_pages.len(),
70 sdi_pages
71 )?;
72
73 let records = sdi::extract_sdi_from_pages(&mut ts, &sdi_pages)?;
75
76 if records.is_empty() {
77 wprintln!(
78 writer,
79 "No SDI records found (pages may be non-leaf or empty)."
80 )?;
81 return Ok(());
82 }
83
84 for rec in &records {
85 wprintln!(writer)?;
86 wprintln!(
87 writer,
88 "=== SDI Record: type={} ({}), id={}",
89 rec.sdi_type,
90 sdi::sdi_type_name(rec.sdi_type),
91 rec.sdi_id
92 )?;
93 wprintln!(
94 writer,
95 "Compressed: {} bytes, Uncompressed: {} bytes",
96 rec.compressed_len,
97 rec.uncompressed_len
98 )?;
99
100 if rec.data.is_empty() {
101 wprintln!(
102 writer,
103 "(Data could not be decompressed - may span multiple pages)"
104 )?;
105 continue;
106 }
107
108 if opts.pretty {
109 match serde_json::from_str::<serde_json::Value>(&rec.data) {
111 Ok(json) => {
112 wprintln!(
113 writer,
114 "{}",
115 serde_json::to_string_pretty(&json).unwrap_or(rec.data.clone())
116 )?;
117 }
118 Err(_) => {
119 wprintln!(writer, "{}", rec.data)?;
120 }
121 }
122 } else {
123 wprintln!(writer, "{}", rec.data)?;
124 }
125 }
126
127 wprintln!(writer)?;
128 wprintln!(writer, "Total SDI records: {}", records.len())?;
129
130 Ok(())
131}