Skip to main content

idb/cli/
tsid.rs

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    // Collect tablespace IDs
61    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                // Try reading space_id directly from FSP header position
76                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        // Filter by tablespace ID if specified
96        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        // Print results
124        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