diff --git a/crates/dbx-core/src/db/mysql.rs b/crates/dbx-core/src/db/mysql.rs index 667b0df01f..c7b1b46607 100644 --- a/crates/dbx-core/src/db/mysql.rs +++ b/crates/dbx-core/src/db/mysql.rs @@ -2816,7 +2816,7 @@ async fn list_table_names_show(pool: &MySqlPool, database: &str) -> Result String { +fn logical_show_full_tables_sql(database: &str) -> String { if database.trim().is_empty() { "SHOW FULL TABLES".to_string() } else { @@ -2840,16 +2840,32 @@ fn table_infos_from_show_rows(rows: &[mysql_async::Row]) -> Vec { tables } -pub async fn list_shardingsphere_tables(pool: &MySqlPool, database: &str) -> Result, String> { - // ShardingSphere's logical names are authoritative here. Do not add SHOW TABLE STATUS: - // this hot path must replace the information_schema lookup with one metadata request. - let sql = shardingsphere_show_full_tables_sql(database); +pub async fn list_logical_tables_show(pool: &MySqlPool, database: &str) -> Result, String> { + // Proxy logical names are authoritative here. Do not add SHOW TABLE STATUS: this hot + // path must replace the information_schema lookup with one metadata request. + let sql = logical_show_full_tables_sql(database); let mut conn = get_conn_with_timeout(pool, super::connection_timeout()).await?; let result = conn.query_iter(&sql).await.map_err(|error| error.to_string())?; let rows = result.collect_and_drop::().await.map_err(|error| error.to_string())?; Ok(table_infos_from_show_rows(&rows)) } +async fn list_logical_table_objects_show_filtered( + pool: &MySqlPool, + database: &str, + object_types: Option<&[String]>, + limit: Option, + offset: Option, +) -> Result, String> { + let tables = list_logical_tables_show(pool, database).await?; + Ok(filter_table_objects_fallback( + table_infos_to_objects(tables, &HashMap::new(), database), + object_types, + limit, + offset, + )) +} + async fn list_table_names_show_filtered( pool: &MySqlPool, database: &str, @@ -3264,6 +3280,14 @@ fn object_query_supports_paging(object_types: Option<&[String]>) -> bool { all_types_supported && uses_table_source != uses_routine_source } +fn logical_table_supplemental_types(object_types: Option<&[String]>) -> Vec { + ["PROCEDURE", "FUNCTION", "TRIGGER", "EVENT"] + .into_iter() + .filter(|object_type| requested_object_type(object_types, object_type)) + .map(str::to_string) + .collect() +} + pub async fn list_objects( pool: &MySqlPool, database: &str, @@ -3368,6 +3392,29 @@ pub async fn list_objects( Ok(PagedObjectList { objects, paging_applied }) } +pub async fn list_objects_with_logical_tables( + pool: &MySqlPool, + database: &str, + object_types: Option<&[String]>, + limit: Option, + offset: Option, +) -> Result { + if !wants_table_objects(object_types) { + return list_objects(pool, database, object_types, limit, offset).await; + } + + let paging_applied = limit.is_some() && object_query_supports_paging(object_types); + let (query_limit, query_offset) = if paging_applied { (limit, offset) } else { (None, None) }; + let mut objects = + list_logical_table_objects_show_filtered(pool, database, object_types, query_limit, query_offset).await?; + let supplemental_types = logical_table_supplemental_types(object_types); + if !supplemental_types.is_empty() { + objects.extend(list_objects(pool, database, Some(&supplemental_types), None, None).await?.objects); + } + + Ok(PagedObjectList { objects, paging_applied }) +} + pub async fn list_object_statistics(pool: &MySqlPool, database: &str) -> Result, String> { let sql = format!( "SELECT TABLE_NAME, TABLE_ROWS, COALESCE(DATA_LENGTH, 0) + COALESCE(INDEX_LENGTH, 0) AS TOTAL_BYTES \ @@ -6159,13 +6206,13 @@ mod tests { } #[test] - fn shardingsphere_show_full_tables_is_one_exact_statement() { - assert_eq!(shardingsphere_show_full_tables_sql("app"), "SHOW FULL TABLES FROM `app`"); - assert_eq!(shardingsphere_show_full_tables_sql(""), "SHOW FULL TABLES"); + fn logical_show_full_tables_is_one_exact_statement() { + assert_eq!(logical_show_full_tables_sql("app"), "SHOW FULL TABLES FROM `app`"); + assert_eq!(logical_show_full_tables_sql(""), "SHOW FULL TABLES"); } #[test] - fn shardingsphere_show_rows_keep_logical_names_and_types_without_comments() { + fn proxy_show_rows_keep_logical_names_and_types_without_comments() { let rows = vec![ mysql_test_row(vec![Value::Bytes(b"normal_table".to_vec()), Value::Bytes(b"BASE TABLE".to_vec())]), mysql_test_row(vec![Value::Bytes(b"t_order".to_vec()), Value::Bytes(b"BASE TABLE".to_vec())]), @@ -6474,6 +6521,16 @@ mod tests { assert!(!object_query_supports_paging(None)); } + #[test] + fn logical_table_objects_keep_only_requested_non_table_sources() { + assert_eq!(logical_table_supplemental_types(None), ["PROCEDURE", "FUNCTION", "TRIGGER", "EVENT"]); + assert!(logical_table_supplemental_types(Some(&["TABLE".to_string(), "VIEW".to_string()])).is_empty()); + assert_eq!( + logical_table_supplemental_types(Some(&["TABLE".to_string(), "PROCEDURE".to_string()])), + ["PROCEDURE"] + ); + } + #[test] fn mysql_completion_triggers_sql_lists_database_triggers() { let sql = list_completion_triggers_sql("app"); diff --git a/crates/dbx-core/src/schema.rs b/crates/dbx-core/src/schema.rs index 207a347f66..907e00e9b0 100644 --- a/crates/dbx-core/src/schema.rs +++ b/crates/dbx-core/src/schema.rs @@ -2278,7 +2278,7 @@ async fn list_tables_once( let tables = db::ob_oracle::list_tables(p, schema).await?; Ok(filter_table_infos(tables, filter, limit, offset, object_types, table_name_filter)) } else if mysql_table_list_source_for_config(db_config.as_ref()) == MysqlTableListSource::ShowFullTables { - db::mysql::list_shardingsphere_tables(p, mysql_table_metadata_catalog(database, schema)) + db::mysql::list_logical_tables_show(p, mysql_table_metadata_catalog(database, schema)) .await .map(|tables| filter_table_infos(tables, filter, limit, offset, object_types, table_name_filter)) } else { @@ -2739,10 +2739,11 @@ fn is_shardingsphere_proxy_version(version: &str) -> bool { } fn mysql_table_list_source_for_config(config: Option<&ConnectionConfig>) -> MysqlTableListSource { - if config - .and_then(|config| config.database_info.as_ref()) - .and_then(|info| info.product_version.as_deref()) - .is_some_and(is_shardingsphere_proxy_version) + if config.is_some_and(db::tdsql_mysql::is_config) + || config + .and_then(|config| config.database_info.as_ref()) + .and_then(|info| info.product_version.as_deref()) + .is_some_and(is_shardingsphere_proxy_version) { MysqlTableListSource::ShowFullTables } else { @@ -3859,6 +3860,14 @@ for line in sys.stdin: assert_eq!(mysql_table_list_source_for_config(None), MysqlTableListSource::InformationSchema); } + #[test] + fn tdsql_profile_uses_logical_show_full_tables_source() { + let mut config = test_connection_config(DatabaseType::Mysql); + config.driver_profile = Some("TDSQL".to_string()); + + assert_eq!(mysql_table_list_source_for_config(Some(&config)), MysqlTableListSource::ShowFullTables); + } + #[test] fn shardingsphere_logical_tables_keep_local_constraints() { let tables = vec![ @@ -5422,6 +5431,10 @@ async fn list_objects_once( db::starrocks::list_table_objects(p, database).await.map(unpaged_object_list) } else if db_config.as_ref().is_some_and(db::mysql_compatible::uses_show_metadata) { db::mysql::list_table_objects_show(p, database).await.map(unpaged_object_list) + } else if mysql_table_list_source_for_config(db_config.as_ref()) == MysqlTableListSource::ShowFullTables { + db::mysql::list_objects_with_logical_tables(p, database, object_types, mysql_limit, mysql_offset) + .await + .map(|result| ObjectListOutcome { objects: result.objects, paging_applied: result.paging_applied }) } else { db::mysql::list_objects(p, database, object_types, mysql_limit, mysql_offset) .await