1 parent
fee3ed1d5a
commit
1bba6fa09e
2 files changed
+140
-19
No files matched your search
@@ -2589,6 +2589,40 @@ async fn list_table_names_show(pool: &MySqlPool, database: &str) -> Result<Vec<T
|
||||
list_table_names_show_filtered(pool, database, None, &[]).await
|
||||
}
|
||||
|
||||
fn shardingsphere_show_full_tables_sql(database: &str) -> String {
|
||||
if database.trim().is_empty() {
|
||||
"SHOW FULL TABLES".to_string()
|
||||
} else {
|
||||
format!("SHOW FULL TABLES FROM {}", quote_identifier(database))
|
||||
}
|
||||
}
|
||||
|
||||
fn table_infos_from_show_rows(rows: &[mysql_async::Row]) -> Vec<TableInfo> {
|
||||
let mut tables = rows
|
||||
.iter()
|
||||
.filter_map(|row| {
|
||||
let name = get_str(row, 0).trim().to_string();
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let table_type = normalize_mysql_table_type(&get_str(row, 1));
|
||||
Some(TableInfo { name, table_type, comment: None, parent_schema: None, parent_name: None })
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tables.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
tables
|
||||
}
|
||||
|
||||
pub async fn list_shardingsphere_tables(pool: &MySqlPool, database: &str) -> Result<Vec<TableInfo>, 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);
|
||||
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::<mysql_async::Row>().await.map_err(|error| error.to_string())?;
|
||||
Ok(table_infos_from_show_rows(&rows))
|
||||
}
|
||||
|
||||
async fn list_table_names_show_filtered(
|
||||
pool: &MySqlPool,
|
||||
database: &str,
|
||||
@@ -2618,19 +2652,7 @@ async fn list_table_names_show_filtered(
|
||||
}
|
||||
}
|
||||
let rows = rows.ok_or_else(|| last_error.unwrap_or_else(|| "SHOW TABLES returned no result".to_string()))?;
|
||||
let mut tables: Vec<TableInfo> = rows
|
||||
.iter()
|
||||
.filter_map(|row| {
|
||||
let name = get_str(row, 0).trim().to_string();
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let table_type = normalize_mysql_table_type(&get_str(row, 1));
|
||||
Some(TableInfo { name, table_type, comment: None, parent_schema: None, parent_name: None })
|
||||
})
|
||||
.collect();
|
||||
tables.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(tables)
|
||||
Ok(table_infos_from_show_rows(&rows))
|
||||
}
|
||||
|
||||
struct ShowTablesQueryAttempt {
|
||||
@@ -5861,6 +5883,29 @@ mod tests {
|
||||
assert_eq!(normalize_mysql_table_type(""), "TABLE");
|
||||
}
|
||||
|
||||
#[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");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shardingsphere_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())]),
|
||||
mysql_test_row(vec![Value::Bytes(b"active_users".to_vec()), Value::Bytes(b"VIEW".to_vec())]),
|
||||
];
|
||||
|
||||
let tables = table_infos_from_show_rows(&rows);
|
||||
|
||||
assert_eq!(
|
||||
tables.iter().map(|table| (table.name.as_str(), table.table_type.as_str())).collect::<Vec<_>>(),
|
||||
vec![("active_users", "VIEW"), ("normal_table", "BASE TABLE"), ("t_order", "BASE TABLE")]
|
||||
);
|
||||
assert!(tables.iter().all(|table| table.comment.is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_filtered_show_fallback_is_server_bounded() {
|
||||
let tables_sql = show_tables_filtered_sql("app", true, Some("missing_%"), &[]);
|
||||
|
||||
@@ -2163,6 +2163,10 @@ async fn list_tables_once(
|
||||
if *mode == MysqlMode::OceanBaseOracle {
|
||||
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))
|
||||
.await
|
||||
.map(|tables| filter_table_infos(tables, filter, limit, offset, object_types, table_name_filter))
|
||||
} else {
|
||||
db::mysql::list_tables_filtered(
|
||||
p,
|
||||
@@ -2605,6 +2609,29 @@ fn mysql_table_metadata_catalog<'a>(database: &'a str, schema: &'a str) -> &'a s
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum MysqlTableListSource {
|
||||
InformationSchema,
|
||||
ShowFullTables,
|
||||
}
|
||||
|
||||
fn is_shardingsphere_proxy_version(version: &str) -> bool {
|
||||
const MARKER: &[u8] = b"shardingsphere-proxy";
|
||||
version.as_bytes().windows(MARKER.len()).any(|window| window.eq_ignore_ascii_case(MARKER))
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
MysqlTableListSource::ShowFullTables
|
||||
} else {
|
||||
MysqlTableListSource::InformationSchema
|
||||
}
|
||||
}
|
||||
|
||||
fn quote_presto_like_identifier(identifier: &str) -> String {
|
||||
format!("\"{}\"", identifier.replace('"', "\"\""))
|
||||
}
|
||||
@@ -2628,17 +2655,17 @@ mod tests {
|
||||
gbase8a_object_statistics_sql, is_agent_postgres_metadata_fallback_config, is_mysql_external_driver_config,
|
||||
is_retryable_metadata_error, metadata_error_action, metadata_name_or_comment_matches,
|
||||
mysql_external_driver_ddl_from_query_result, mysql_external_driver_ddl_sql,
|
||||
mysql_object_source_ddl_column_index, mysql_object_source_sql, mysql_table_metadata_catalog,
|
||||
normalize_information_schema_table_type, oracle_columns_from_query_result, oracle_columns_sql,
|
||||
oracle_object_statistics_dba_segments_sql, oracle_object_statistics_from_query_result,
|
||||
mysql_object_source_ddl_column_index, mysql_object_source_sql, mysql_table_list_source_for_config,
|
||||
mysql_table_metadata_catalog, normalize_information_schema_table_type, oracle_columns_from_query_result,
|
||||
oracle_columns_sql, oracle_object_statistics_dba_segments_sql, oracle_object_statistics_from_query_result,
|
||||
oracle_object_statistics_rows_only_sql, oracle_object_statistics_sql,
|
||||
oracle_object_statistics_user_segments_sql, oracle_table_comment_from_query_result, oracle_table_comment_sql,
|
||||
oracle_table_comments_sql, presto_like_columns_from_query_result, presto_like_information_schema_columns_sql,
|
||||
presto_like_information_schema_tables_sql, presto_like_tables_from_query_result, replace_metadata_runtime,
|
||||
should_query_oracle_columns_via_sql_first, table_comments_from_query_result, table_name_filter_matches,
|
||||
tdengine_table_comment_like_pattern, tdengine_table_comment_sql, tdengine_table_comments_sql,
|
||||
uses_mongodb_agent_collection_listing, visible_schema_filter, MetadataErrorAction, TableNameFilter,
|
||||
TDENGINE_COMMENT_SEARCH_TIMEOUT, TDENGINE_LIKE_PATTERN_MAX_BYTES,
|
||||
uses_mongodb_agent_collection_listing, visible_schema_filter, MetadataErrorAction, MysqlTableListSource,
|
||||
TableNameFilter, TDENGINE_COMMENT_SEARCH_TIMEOUT, TDENGINE_LIKE_PATTERN_MAX_BYTES,
|
||||
};
|
||||
use super::{list_databases_core, list_tables_core};
|
||||
use super::{
|
||||
@@ -2646,7 +2673,7 @@ mod tests {
|
||||
object_types_only_custom_types, supports_custom_type_details, supports_pg_custom_type_objects,
|
||||
};
|
||||
use crate::connection::{AppState, PoolKind};
|
||||
use crate::models::connection::{ConnectionConfig, DatabaseType};
|
||||
use crate::models::connection::{ConnectionConfig, DatabaseConnectionInfo, DatabaseType};
|
||||
use crate::storage::Storage;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
@@ -3447,6 +3474,55 @@ for line in sys.stdin:
|
||||
assert_eq!(filtered[0].name, "audit_record");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shardingsphere_proxy_marker_is_ascii_case_insensitive_and_exact() {
|
||||
assert!(super::is_shardingsphere_proxy_version("5.7.22-ShardingSphere-Proxy 5.5.2"));
|
||||
assert!(super::is_shardingsphere_proxy_version("8.0.36-SHARDINGSPHERE-PROXY 5.5.2"));
|
||||
assert!(!super::is_shardingsphere_proxy_version("8.0.36-ShardingSphere Proxy 5.5.2"));
|
||||
assert!(!super::is_shardingsphere_proxy_version("8.0.36-MySQL Community Server"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_table_list_source_uses_only_saved_shardingsphere_version() {
|
||||
let mut config = test_connection_config(DatabaseType::Mysql);
|
||||
assert_eq!(mysql_table_list_source_for_config(Some(&config)), MysqlTableListSource::InformationSchema);
|
||||
|
||||
config.database_info = Some(DatabaseConnectionInfo {
|
||||
product_version: Some("5.7.22-ShardingSphere-Proxy 5.5.2".to_string()),
|
||||
..DatabaseConnectionInfo::default()
|
||||
});
|
||||
assert_eq!(mysql_table_list_source_for_config(Some(&config)), MysqlTableListSource::ShowFullTables);
|
||||
|
||||
config.database_info.as_mut().unwrap().product_version = Some("8.0.36-MySQL Community Server".to_string());
|
||||
assert_eq!(mysql_table_list_source_for_config(Some(&config)), MysqlTableListSource::InformationSchema);
|
||||
assert_eq!(mysql_table_list_source_for_config(None), MysqlTableListSource::InformationSchema);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shardingsphere_logical_tables_keep_local_constraints() {
|
||||
let tables = vec![
|
||||
test_table_info("normal_table"),
|
||||
test_table_info("t_order"),
|
||||
test_table_info("t_order_archive"),
|
||||
super::db::TableInfo {
|
||||
name: "t_order_view".to_string(),
|
||||
table_type: "VIEW".to_string(),
|
||||
comment: None,
|
||||
parent_schema: None,
|
||||
parent_name: None,
|
||||
},
|
||||
test_table_info("t_user"),
|
||||
];
|
||||
let table_types = vec!["TABLE".to_string()];
|
||||
let name_filter =
|
||||
TableNameFilter { include_patterns: vec!["t_%".to_string()], exclude_patterns: vec!["%user%".to_string()] };
|
||||
|
||||
let filtered =
|
||||
filter_table_infos(tables, Some("order"), Some(1), Some(1), Some(&table_types), Some(&name_filter));
|
||||
|
||||
assert_eq!(filtered.into_iter().map(|table| table.name).collect::<Vec<_>>(), vec!["t_order_archive"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mongodb_agent_collection_listing_only_applies_to_mongodb() {
|
||||
let mongodb = test_connection_config(DatabaseType::MongoDb);
|
||||
|
||||
Reference in new issue
Block a user