1use std::collections::BTreeMap;
2use std::io::Write;
3use std::path::Path;
4
5use byteorder::{BigEndian, ByteOrder};
6use serde::Serialize;
7
8use crate::cli::wprintln;
9use crate::innodb::constants::FIL_PAGE_DATA;
10use crate::innodb::tablespace::Tablespace;
11use crate::util::fs::find_tablespace_files;
12use crate::IdbError;
13
14pub struct TsidOptions {
15 pub datadir: String,
16 pub list: bool,
17 pub tablespace_id: Option<u32>,
18 pub json: bool,
19 pub page_size: Option<u32>,
20}
21
22#[derive(Serialize)]
23struct TsidResultJson {
24 datadir: String,
25 tablespaces: Vec<TsidEntryJson>,
26}
27
28#[derive(Serialize)]
29struct TsidEntryJson {
30 file: String,
31 space_id: u32,
32}
33
34pub fn execute(opts: &TsidOptions, writer: &mut dyn Write) -> Result<(), IdbError> {
35 let datadir = Path::new(&opts.datadir);
36 if !datadir.is_dir() {
37 return Err(IdbError::Argument(format!(
38 "Data directory does not exist: {}",
39 opts.datadir
40 )));
41 }
42
43 let ibd_files = find_tablespace_files(datadir, &["ibd", "ibu"])?;
44
45 if ibd_files.is_empty() {
46 if opts.json {
47 let result = TsidResultJson {
48 datadir: opts.datadir.clone(),
49 tablespaces: Vec::new(),
50 };
51 let json = serde_json::to_string_pretty(&result)
52 .map_err(|e| IdbError::Parse(format!("JSON serialization error: {}", e)))?;
53 wprintln!(writer, "{}", json)?;
54 } else {
55 wprintln!(writer, "No .ibd/.ibu files found in {}", opts.datadir)?;
56 }
57 return Ok(());
58 }
59
60 let mut results: BTreeMap<String, u32> = BTreeMap::new();
62
63 for ibd_path in &ibd_files {
64 let mut ts = match match opts.page_size {
65 Some(ps) => Tablespace::open_with_page_size(ibd_path, ps),
66 None => Tablespace::open(ibd_path),
67 } {
68 Ok(t) => t,
69 Err(_) => continue,
70 };
71
72 let space_id = match ts.fsp_header() {
73 Some(fsp) => fsp.space_id,
74 None => {
75 match ts.read_page(0) {
77 Ok(page0) => {
78 if page0.len() >= FIL_PAGE_DATA + 4 {
79 BigEndian::read_u32(&page0[FIL_PAGE_DATA..])
80 } else {
81 continue;
82 }
83 }
84 Err(_) => continue,
85 }
86 }
87 };
88
89 let display_path = ibd_path
90 .strip_prefix(datadir)
91 .unwrap_or(ibd_path)
92 .to_string_lossy()
93 .to_string();
94
95 if let Some(target_id) = opts.tablespace_id {
97 if space_id != target_id {
98 continue;
99 }
100 }
101
102 results.insert(display_path, space_id);
103 }
104
105 if opts.json {
106 let tablespaces: Vec<TsidEntryJson> = results
107 .iter()
108 .map(|(path, &space_id)| TsidEntryJson {
109 file: path.clone(),
110 space_id,
111 })
112 .collect();
113
114 let result = TsidResultJson {
115 datadir: opts.datadir.clone(),
116 tablespaces,
117 };
118
119 let json = serde_json::to_string_pretty(&result)
120 .map_err(|e| IdbError::Parse(format!("JSON serialization error: {}", e)))?;
121 wprintln!(writer, "{}", json)?;
122 } else {
123 for (path, space_id) in &results {
125 wprintln!(writer, "{} - Space ID: {}", path, space_id)?;
126 }
127
128 if results.is_empty() {
129 if let Some(target_id) = opts.tablespace_id {
130 wprintln!(writer, "Tablespace ID {} not found.", target_id)?;
131 }
132 }
133 }
134
135 Ok(())
136}
137