feat(schema): show table properties in object browser

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-09-23 14:43:33 +08:00
1 parent 12ed7a49b3
commit 13d2db6191
22 files changed
+284 -12

No files matched your search

+5 -1
View File
@@ -1873,6 +1873,7 @@ fn oracle_object_statistics_from_query_result(result: db::QueryResult) -> Vec<db
schema: query_result_cell_string(&row, 1),
estimated_rows: query_result_cell_i64(&row, 2),
total_bytes: query_result_cell_i64(&row, 3),
..Default::default()
})
})
.collect()
@@ -6376,7 +6377,10 @@ async fn list_object_statistics_once(
if *mode == MysqlMode::OceanBaseOracle || db_config.as_ref().is_some_and(db::manticoresearch::is_config) {
Ok(vec![])
} else {
db::mysql::list_object_statistics(p, database).await
let include_mysql_details = db_config.as_ref().is_some_and(|config| {
config.db_type == DatabaseType::Mysql && !db::mysql_compatible::uses_show_metadata(config)
});
db::mysql::list_object_statistics(p, database, include_mysql_details).await
}
}
PoolKind::Postgres(p) if db_config.as_ref().is_some_and(is_questdb_config) => Ok(vec![]),
@@ -554,6 +554,7 @@ fn clickhouse_object_statistics_from_row(row: &[serde_json::Value], database: &s
schema: Some(database.to_string()),
estimated_rows: json_value_as_i64(row.get(1)),
total_bytes: json_value_as_i64(row.get(2)),
..Default::default()
})
}
@@ -321,6 +321,7 @@ pub fn object_statistics_from_collection_stats(name: &str, database: &str, resul
(None, None) => None,
(data, index) => Some(data.unwrap_or(0).saturating_add(index.unwrap_or(0))),
},
..Default::default()
}
}
+32 -5
View File
@@ -3820,10 +3820,18 @@ pub async fn list_objects_with_logical_tables(
Ok(PagedObjectList { objects, paging_applied })
}
pub async fn list_object_statistics(pool: &MySqlPool, database: &str) -> Result<Vec<ObjectStatistics>, String> {
pub async fn list_object_statistics(
pool: &MySqlPool,
database: &str,
include_mysql_details: bool,
) -> Result<Vec<ObjectStatistics>, String> {
let columns = if include_mysql_details {
"TABLE_NAME, TABLE_ROWS, DATA_LENGTH, ENGINE, CREATE_TIME, UPDATE_TIME, TABLE_COLLATION, ROW_FORMAT, AVG_ROW_LENGTH, MAX_DATA_LENGTH, CHECK_TIME, INDEX_LENGTH, AUTO_INCREMENT, DATA_FREE, COALESCE(DATA_LENGTH, 0) + COALESCE(INDEX_LENGTH, 0) AS TOTAL_BYTES"
} else {
"TABLE_NAME, TABLE_ROWS, COALESCE(DATA_LENGTH, 0) + COALESCE(INDEX_LENGTH, 0) AS TOTAL_BYTES"
};
let sql = format!(
"SELECT TABLE_NAME, TABLE_ROWS, COALESCE(DATA_LENGTH, 0) + COALESCE(INDEX_LENGTH, 0) AS TOTAL_BYTES \
FROM information_schema.TABLES \
"SELECT {columns} FROM information_schema.TABLES \
WHERE TABLE_SCHEMA = {} AND TABLE_TYPE <> 'VIEW' \
ORDER BY TABLE_NAME",
quote_value(database),
@@ -3838,12 +3846,31 @@ pub async fn list_object_statistics(pool: &MySqlPool, database: &str) -> Result<
.iter()
.filter_map(|row| {
let name = get_str_by_name(row, "TABLE_NAME").trim().to_string();
(!name.is_empty()).then_some(ObjectStatistics {
if name.is_empty() {
return None;
}
let mut statistics = ObjectStatistics {
name,
schema: Some(database.to_string()),
estimated_rows: get_opt_i64(row, "TABLE_ROWS"),
total_bytes: get_opt_i64(row, "TOTAL_BYTES"),
})
..Default::default()
};
if include_mysql_details {
statistics.data_length = get_opt_i64(row, "DATA_LENGTH");
statistics.engine = get_opt_str(row, "ENGINE");
statistics.created_at = get_opt_metadata_string(row, "CREATE_TIME");
statistics.updated_at = get_opt_metadata_string(row, "UPDATE_TIME");
statistics.collation = get_opt_str(row, "TABLE_COLLATION");
statistics.row_format = get_opt_str(row, "ROW_FORMAT");
statistics.avg_row_length = get_opt_i64(row, "AVG_ROW_LENGTH");
statistics.max_data_length = get_opt_i64(row, "MAX_DATA_LENGTH");
statistics.check_time = get_opt_metadata_string(row, "CHECK_TIME");
statistics.index_length = get_opt_i64(row, "INDEX_LENGTH");
statistics.auto_increment = get_opt_unsigned_metadata_string(row, "AUTO_INCREMENT");
statistics.data_free = get_opt_i64(row, "DATA_FREE");
}
Some(statistics)
})
.collect())
}
+1
View File
@@ -7248,6 +7248,7 @@ pub async fn list_object_statistics(pool: &Pool, schema: &str) -> Result<Vec<Obj
schema: Some(schema.to_string()),
estimated_rows: row.try_get::<_, i64>(1).ok(),
total_bytes: row.try_get::<_, i64>(2).ok(),
..Default::default()
})
.collect())
}
+1
View File
@@ -2836,6 +2836,7 @@ pub async fn list_object_statistics(
.ok()
.flatten()
.or_else(|| row.try_get::<i32, _>(2).ok().flatten().map(i64::from)),
..Default::default()
})
.filter(|stat| !stat.name.is_empty())
.collect())
@@ -270,6 +270,7 @@ pub async fn list_object_statistics(client: &VictoriaMetricsClient) -> Result<Ve
estimated_rows: item.value.as_i64().or_else(|| item.value.as_str().and_then(|value| value.parse().ok())),
// VictoriaMetrics exposes series counts here, but no per-metric storage size.
total_bytes: None,
..Default::default()
})
.collect())
}
+1 -1
View File
@@ -442,7 +442,7 @@ mod tests {
async fn http_inactivity_expiry_rolls_back_and_disconnects_inner_owner() {
let backend = Arc::new(HttpTestBackend::new());
let (url, manager, cancellation, server_task) =
start_http_test_server(backend.clone(), std::time::Duration::from_millis(50)).await;
start_http_test_server(backend.clone(), std::time::Duration::from_millis(500)).await;
let (client, _) = open_active_transaction(&url).await;
wait_for_disposal(&backend).await;
+25 -1
View File
@@ -145,12 +145,36 @@ pub struct ExtensionInfo {
pub schema: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ObjectStatistics {
pub name: String,
pub schema: Option<String>,
pub estimated_rows: Option<i64>,
pub total_bytes: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data_length: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub engine: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub updated_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub collation: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub row_format: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avg_row_length: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_data_length: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub check_time: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub index_length: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auto_increment: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data_free: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]