From a1f718d6bdd1a87bccd0eb6401d8fecf94431540 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Tue, 26 May 2026 15:03:53 +0800 Subject: [PATCH] fix: normalize mysql export row format --- crates/dbx-core/src/database_export.rs | 49 ++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/crates/dbx-core/src/database_export.rs b/crates/dbx-core/src/database_export.rs index 608e96330..a1bb2417e 100644 --- a/crates/dbx-core/src/database_export.rs +++ b/crates/dbx-core/src/database_export.rs @@ -183,6 +183,7 @@ pub fn build_database_sql_export(options: BuildDatabaseSqlExportOptions) -> Resu for table in options.tables { if let Some(ddl) = table.ddl.as_ref().map(|ddl| ddl.trim()).filter(|ddl| !ddl.is_empty()) { + let ddl = normalize_export_table_ddl(ddl, table.database_type); lines.push(format!("-- Structure for {}", table.display_name)); lines.push(format!("{};", ddl.trim_end_matches(';'))); lines.push(String::new()); @@ -230,6 +231,17 @@ fn export_qualified_table_name( Ok(qualified_table_name(database_type, schema, table_name)) } +fn normalize_export_table_ddl(ddl: &str, database_type: Option) -> String { + if database_type != Some(DatabaseType::Mysql) { + return ddl.to_string(); + } + + static LEGACY_MYSQL_ROW_FORMAT_RE: std::sync::LazyLock = + std::sync::LazyLock::new(|| regex::Regex::new(r"(?i)\bROW_FORMAT\s*=\s*(COMPACT|REDUNDANT)\b").unwrap()); + + LEGACY_MYSQL_ROW_FORMAT_RE.replace_all(ddl, "ROW_FORMAT=DYNAMIC").into_owned() +} + pub async fn is_export_cancelled(export_id: &str) -> bool { EXPORT_CANCELLED.read().await.contains(export_id) } @@ -358,6 +370,7 @@ pub async fn export_database_sql_core( .await { Ok(ddl) => { + let ddl = normalize_export_table_ddl(&ddl, Some(db_type)); writeln!(file, "{};\n", ddl).map_err(|e| format!("Failed to write file: {e}"))?; } Err(e) => { @@ -648,9 +661,9 @@ fn drop_table_if_exists_sql(table_name: &str, schema: &str, db_type: &DatabaseTy mod tests { use super::{ build_database_sql_export, build_export_insert_statements, drop_table_if_exists_sql, - filter_selected_table_infos, format_export_sql_literal, BuildDatabaseSqlExportOptions, - BuildExportInsertStatementsOptions, ExportedTableSql, DATABASE_EXPORT_INSERT_BATCH_SIZE, - DATABASE_EXPORT_ROW_LIMIT, + filter_selected_table_infos, format_export_sql_literal, normalize_export_table_ddl, + BuildDatabaseSqlExportOptions, BuildExportInsertStatementsOptions, ExportedTableSql, + DATABASE_EXPORT_INSERT_BATCH_SIZE, DATABASE_EXPORT_ROW_LIMIT, }; use crate::models::connection::DatabaseType; use crate::types::TableInfo; @@ -762,4 +775,34 @@ mod tests { .join("\n") ); } + + #[test] + fn normalizes_legacy_mysql_row_format_for_export_compatibility() { + let ddl = "CREATE TABLE `wide_table` (\n `payload` varchar(4096) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT"; + + let normalized = normalize_export_table_ddl(ddl, Some(DatabaseType::Mysql)); + + assert_eq!( + normalized, + "CREATE TABLE `wide_table` (\n `payload` varchar(4096) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC" + ); + } + + #[test] + fn normalizes_lowercase_redundant_mysql_row_format_for_export_compatibility() { + let ddl = "CREATE TABLE `wide_table` (`payload` varchar(4096)) engine=InnoDB row_format = redundant"; + + let normalized = normalize_export_table_ddl(ddl, Some(DatabaseType::Mysql)); + + assert_eq!(normalized, "CREATE TABLE `wide_table` (`payload` varchar(4096)) engine=InnoDB ROW_FORMAT=DYNAMIC"); + } + + #[test] + fn preserves_non_legacy_or_non_mysql_row_formats() { + let mysql_ddl = "CREATE TABLE `ok` (`payload` text) ENGINE=InnoDB ROW_FORMAT=COMPRESSED"; + let postgres_ddl = "CREATE TABLE users (payload text) ROW_FORMAT=COMPACT"; + + assert_eq!(normalize_export_table_ddl(mysql_ddl, Some(DatabaseType::Mysql)), mysql_ddl); + assert_eq!(normalize_export_table_ddl(postgres_ddl, Some(DatabaseType::Postgres)), postgres_ddl); + } }