Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 53 additions & 24 deletions src/tools/fe/table_info/browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub fn run_interactive(config: &crate::config::Config) -> Result<()> {
let total = FeTableInfoTool::list_tables(config, Some(&db_name))?.len();
let conc = FeTableInfoTool::suggest_concurrency(total);
let reports = FeTableInfoTool::collect_all_in_db(config, &db_name, conc)?;
if let Ok(files) = save_reports_txt(config, &reports, false) {
if let Ok(files) = save_reports_txt(config, &reports, SaveMode::PerDatabase) {
for f in files {
print_info(&format!("Saved: {}", f.display()));
}
Expand All @@ -36,7 +36,7 @@ pub fn run_interactive(config: &crate::config::Config) -> Result<()> {
};
print_info(&format!("Found {} tables, starting...", all_tables.len()));
let reports = FeTableInfoTool::collect_many(config, &all_tables, conc)?;
if let Ok(files) = save_reports_txt(config, &reports, true) {
if let Ok(files) = save_reports_txt(config, &reports, SaveMode::SingleFile) {
print_info(&format!("Saved: {}", files[0].display()));
}
render_batch_summary("<all_dbs>", reports.len());
Expand Down Expand Up @@ -189,7 +189,13 @@ fn generate_report_content(report: &super::TableInfoReport) -> String {
report
.indexes
.iter()
.map(|i| format!("{}({})", i.name, i.index_type))
.map(|i| {
if i.name.contains('(') {
i.name.clone()
} else {
format!("{}({})", i.name, i.index_type)
}
})
.collect::<Vec<_>>()
.join(", ")
};
Expand Down Expand Up @@ -298,37 +304,60 @@ fn prompt_next_action() -> Result<NextAction> {
}
}

#[derive(Debug, Clone)]
enum SaveMode {
SingleFile,
PerDatabase,
}

fn save_reports_txt(
config: &crate::config::Config,
reports: &[super::TableInfoReport],
single_file: bool,
mode: SaveMode,
) -> anyhow::Result<Vec<PathBuf>> {
let base_dir: PathBuf = config.output_dir.join("table-info");
config.ensure_output_dir()?;

if single_file {
let file_path = base_dir.join("all_databases_table_info.txt");
crate::tools::common::fs_utils::ensure_dir_exists(&file_path)?;
let mut content = String::new();
for r in reports {
content.push_str(&generate_report_content(r));
content.push('\n');
content.push_str(&"-".repeat(80));
content.push('\n');
}
fs::write(&file_path, content)?;
Ok(vec![file_path])
} else {
let mut files: Vec<PathBuf> = Vec::with_capacity(reports.len());
for r in reports {
let dir = base_dir.join(&r.ident.schema);
let file_path = dir.join(format!("{}.txt", &r.ident.name));
match mode {
SaveMode::SingleFile => {
let file_path = base_dir.join("all_databases_table_info.txt");
crate::tools::common::fs_utils::ensure_dir_exists(&file_path)?;
let content = generate_report_content(r);
let mut content = String::new();
for r in reports {
content.push_str(&generate_report_content(r));
content.push('\n');
content.push_str(&"-".repeat(80));
content.push('\n');
}
fs::write(&file_path, content)?;
files.push(file_path);
Ok(vec![file_path])
}
SaveMode::PerDatabase => {
let mut db_groups: std::collections::HashMap<String, Vec<&super::TableInfoReport>> =
std::collections::HashMap::new();
for report in reports {
db_groups
.entry(report.ident.schema.clone())
.or_default()
.push(report);
}

let mut files: Vec<PathBuf> = Vec::with_capacity(db_groups.len());
for (db_name, db_reports) in db_groups {
let file_path = base_dir.join(format!("{}.txt", db_name));
crate::tools::common::fs_utils::ensure_dir_exists(&file_path)?;
let mut content = String::new();
for r in db_reports {
content.push_str(&generate_report_content(r));
content.push('\n');
content.push_str(&"-".repeat(80));
content.push('\n');
}
fs::write(&file_path, content)?;
files.push(file_path);
}
Ok(files)
}
Ok(files)
}
}

Expand Down
32 changes: 24 additions & 8 deletions src/tools/fe/table_info/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,13 @@ pub fn parse_partitions(rows: &super::sql::ResultSet) -> Result<TableStatsFromPa
}

pub fn parse_indexes_from_create(ddl: &str) -> Vec<IndexInfo> {
let mut result = Vec::new();
// Match lines like: INDEX idx_comment (`comment`) USING INVERTED ...
let re = Regex::new(
r"(?m)^\s*INDEX\s+`?(?P<name>\w+)`?\s*\((?P<cols>[^\)]*)\)\s*USING\s+(?P<itype>\w+)",
)
.ok();
if let Some(re) = re {
for cap in re.captures_iter(ddl) {
let mut result: Vec<IndexInfo> = Vec::new();

// Parse explicit INDEX ... USING ... (case-insensitive, supports multiple lines)
if let Ok(re_idx) = Regex::new(
r"(?mi)^\s*INDEX\s+`?(?P<name>[A-Za-z0-9_]+)`?\s*\((?P<cols>[^\)]*)\)\s*USING\s+(?P<itype>[A-Za-z0-9_]+)",
) {
for cap in re_idx.captures_iter(ddl) {
let name = cap
.name("name")
.map(|m| m.as_str())
Expand All @@ -217,5 +216,22 @@ pub fn parse_indexes_from_create(ddl: &str) -> Vec<IndexInfo> {
});
}
}

// Parse bloom_filter_columns from PROPERTIES (fallback)
if let Ok(re_bf) = Regex::new(r#"(?i)"bloom_filter_columns"\s*=\s*"(?P<cols>[^"]*)""#)
&& let Some(cap) = re_bf.captures(ddl)
{
let cols_raw = cap.name("cols").map(|m| m.as_str()).unwrap_or("");
let columns = parse_column_list(cols_raw);
if !columns.is_empty() {
let display_name = format!("bloom_filter({})", columns.join(","));
result.push(IndexInfo {
name: display_name,
columns,
index_type: "BLOOM_FILTER".to_string(),
});
}
}

result
}