fix(transfer): keep identity sources auto-increment on mysql targets

This commit is contained in:
ordinary being
2026-10-19 11:58:45 +08:00
committed by GitHub
parent 0ff3461815
commit 175c79f2ee
+71 -1
View File
@@ -2762,6 +2762,15 @@ fn parse_mysql_extra_clauses(extra: Option<&str>) -> MysqlExtraClauses {
result
}
/// Whether a mapped target type is one MySQL accepts for `AUTO_INCREMENT`.
///
/// MySQL allows the clause on integer columns only, so an identity source mapped to
/// `DECIMAL`/`NUMBER` must not receive it even when its metadata is identity-style.
fn mysql_auto_increment_type_supported(mapped_type: &str) -> bool {
let base = mapped_type.split(['(', ' ']).next().unwrap_or("").trim().to_ascii_lowercase();
matches!(base.as_str(), "tinyint" | "smallint" | "mediumint" | "int" | "integer" | "bigint")
}
fn postgres_order_by_expression(columns: &[String], db_type: &DatabaseType) -> Option<String> {
postgres_order_by_expression_with_identifier_quote(columns, db_type, None)
}
@@ -4768,7 +4777,15 @@ fn generate_create_table_ddl_with_column_quoting(
}
if is_mysql_family {
let extra_clauses = parse_mysql_extra_clauses(c.extra.as_deref());
if extra_clauses.auto_increment {
// `AUTO_INCREMENT` must also follow identity-style sources (SQL
// Server `identity(1, 1)`, Dameng `identity`): the MySQL-spelling
// parser only recognises `auto_increment`. Only a primary key on
// an integer column qualifies — MySQL requires the auto-increment
// column to be a key and rejects the clause on other types.
let identity_source = c.is_primary_key
&& column_is_auto_increment(c)
&& mysql_auto_increment_type_supported(&mapped_type);
if extra_clauses.auto_increment || identity_source {
line.push_str(" AUTO_INCREMENT");
}
if let Some(on_update_expr) = extra_clauses.on_update {
@@ -23337,6 +23354,59 @@ SELECT 1 FROM dual"#
}
}
#[test]
fn transfer_create_table_preserves_identity_sources_for_mysql() {
// `AUTO_INCREMENT` must also follow identity-style sources (SQL Server
// `identity(1, 1)`, Dameng `identity`); the MySQL-spelling parser alone only
// recognises `auto_increment`.
for (source, extra) in [(DatabaseType::SqlServer, "identity(1,1)"), (DatabaseType::Dameng, "identity")] {
let cols = vec![db::ColumnInfo {
is_primary_key: true,
is_nullable: false,
extra: Some(extra.to_string()),
..test_column("id", "int")
}];
let ddl =
generate_create_table_ddl(&cols, "users", "source", "shop", &DatabaseType::Mysql, &source, None, None);
assert!(ddl.contains("`id` INT NOT NULL AUTO_INCREMENT"), "{source:?}->Mysql: {ddl}");
}
}
#[test]
fn transfer_create_table_keeps_non_key_identity_sources_out_of_auto_increment() {
// MySQL requires the auto-increment column to be a key: an identity source on
// a non-key column stays a plain column instead of failing CREATE TABLE.
let cols = vec![db::ColumnInfo {
is_nullable: false,
extra: Some("identity".to_string()),
..test_column("ticket", "int")
}];
let ddl = generate_create_table_ddl(
&cols,
"users",
"source",
"shop",
&DatabaseType::Mysql,
&DatabaseType::Dameng,
None,
None,
);
assert!(!ddl.contains("AUTO_INCREMENT"), "ddl: {ddl}");
}
#[test]
fn mysql_auto_increment_type_supported_accepts_only_mysql_integers() {
for supported in ["INT", "int(11)", "BIGINT", "TINYINT(1)", "SMALLINT UNSIGNED", "MEDIUMINT", "INTEGER"] {
assert!(mysql_auto_increment_type_supported(supported), "{supported}");
}
for rejected in ["DECIMAL(10,0)", "NUMBER(10)", "VARCHAR(32)", "TEXT", "POINT", "DATETIME"] {
assert!(!mysql_auto_increment_type_supported(rejected), "{rejected}");
}
}
#[test]
fn postgres_create_table_default_clause_unchanged() {
let cols = vec![db::ColumnInfo {