fix(diff): default missing VARCHAR length for MySQL-family targets
This commit is contained in:
1 parent
781bb46d3a
commit
eab1f6344b
3 files changed
+335
-14
No files matched your search
@@ -103,6 +103,11 @@ pub struct DdlDialectProfile {
|
||||
pub auto_inc: AutoIncSyntax,
|
||||
/// When false, display widths like `INT(11)` are stripped if no type_map hit.
|
||||
pub supports_display_width: bool,
|
||||
/// Whether this target's VARCHAR/CHARACTER VARYING/NVARCHAR syntax rejects a
|
||||
/// missing length (e.g. `VARCHAR` with no length is a MySQL syntax error).
|
||||
/// Distinct from `supports_display_width`: that flag is about whether the
|
||||
/// target *prints* MySQL-style `INT(11)` widths, not whether VARCHAR needs one.
|
||||
pub requires_explicit_varchar_length: bool,
|
||||
/// Cap for length params when applying `{}` templates (e.g. Access TEXT max 255).
|
||||
pub max_varchar_len: Option<u32>,
|
||||
/// MySQL-style inline `COMMENT '...'` on columns.
|
||||
@@ -314,6 +319,7 @@ fn mysql_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
quote: QuoteStyle::Backtick,
|
||||
auto_inc: AutoIncSyntax::Suffix(" AUTO_INCREMENT"),
|
||||
supports_display_width: true,
|
||||
requires_explicit_varchar_length: true,
|
||||
max_varchar_len: Some(65_535),
|
||||
inline_column_comment: true,
|
||||
foreign_keys_inline_in_create: false,
|
||||
@@ -359,6 +365,7 @@ fn postgres_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
quote: QuoteStyle::DoubleQuote,
|
||||
auto_inc: AutoIncSyntax::PostgresSequence,
|
||||
supports_display_width: false,
|
||||
requires_explicit_varchar_length: false,
|
||||
max_varchar_len: None,
|
||||
inline_column_comment: false,
|
||||
foreign_keys_inline_in_create: false,
|
||||
@@ -404,6 +411,10 @@ fn oracle_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
quote: QuoteStyle::UnquotedUpper,
|
||||
auto_inc: AutoIncSyntax::Suffix(" GENERATED BY DEFAULT AS IDENTITY"),
|
||||
supports_display_width: false,
|
||||
// Oracle's VARCHAR2 also rejects a missing length in real DDL, but this
|
||||
// is left false (unverified, no Oracle instance in scope for this
|
||||
// change) rather than asserting untested cross-dialect behavior.
|
||||
requires_explicit_varchar_length: false,
|
||||
max_varchar_len: Some(4000),
|
||||
inline_column_comment: false,
|
||||
foreign_keys_inline_in_create: false,
|
||||
@@ -450,6 +461,7 @@ fn sqlserver_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
quote: QuoteStyle::Brackets,
|
||||
auto_inc: AutoIncSyntax::Suffix(" IDENTITY(1,1)"),
|
||||
supports_display_width: false,
|
||||
requires_explicit_varchar_length: false,
|
||||
max_varchar_len: None,
|
||||
inline_column_comment: false,
|
||||
foreign_keys_inline_in_create: false,
|
||||
@@ -495,6 +507,7 @@ fn sqlite_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
quote: QuoteStyle::DoubleQuote,
|
||||
auto_inc: AutoIncSyntax::None,
|
||||
supports_display_width: false,
|
||||
requires_explicit_varchar_length: false,
|
||||
max_varchar_len: None,
|
||||
inline_column_comment: false,
|
||||
foreign_keys_inline_in_create: true,
|
||||
@@ -540,6 +553,7 @@ fn access_profile() -> DdlDialectProfile {
|
||||
quote: QuoteStyle::DoubleQuote,
|
||||
auto_inc: AutoIncSyntax::ReplaceTypeWith("COUNTER"),
|
||||
supports_display_width: false,
|
||||
requires_explicit_varchar_length: false,
|
||||
max_varchar_len: Some(255),
|
||||
inline_column_comment: false,
|
||||
foreign_keys_inline_in_create: false,
|
||||
@@ -585,6 +599,7 @@ fn h2_profile() -> DdlDialectProfile {
|
||||
quote: QuoteStyle::DoubleQuote,
|
||||
auto_inc: AutoIncSyntax::None,
|
||||
supports_display_width: false,
|
||||
requires_explicit_varchar_length: false,
|
||||
max_varchar_len: None,
|
||||
inline_column_comment: false,
|
||||
foreign_keys_inline_in_create: false,
|
||||
@@ -630,6 +645,12 @@ fn conservative_ansi(db: DatabaseType) -> DdlDialectProfile {
|
||||
quote: QuoteStyle::DoubleQuote,
|
||||
auto_inc: AutoIncSyntax::None,
|
||||
supports_display_width: false,
|
||||
// This fallback also covers DB2 and Informix, which reject a bare
|
||||
// VARCHAR with no length — but it's shared with dialects that don't
|
||||
// (Snowflake, BigQuery, DuckDB, Trino, ...), so it can't be flipped
|
||||
// true here without a dedicated DB2/Informix profile and DB
|
||||
// verification. Tracked as a known gap, not fixed by this flag.
|
||||
requires_explicit_varchar_length: false,
|
||||
max_varchar_len: None,
|
||||
inline_column_comment: false,
|
||||
foreign_keys_inline_in_create: false,
|
||||
|
||||
@@ -343,10 +343,33 @@ pub fn rewrite_column_type(source_type: &str, target: DatabaseType, source_diale
|
||||
// Dialect-kind matrix
|
||||
if let Some(src) = source_dialect {
|
||||
let target_kind = DialectKind::from_database_type(target);
|
||||
let matrix = TypeMappingMatrix::for_dialects(src, target_kind);
|
||||
let (converted, _) = matrix.convert_type(source_type);
|
||||
if converted != source_type {
|
||||
return strip_display_width_if_needed(&converted, &profile);
|
||||
if src != target_kind {
|
||||
let matrix = TypeMappingMatrix::for_dialects(src, target_kind);
|
||||
let (converted, _) = matrix.convert_type(source_type);
|
||||
if converted != source_type {
|
||||
return strip_display_width_if_needed(&converted, &profile);
|
||||
}
|
||||
// No matrix rule for this cross-dialect pair, and it's a
|
||||
// *varying*-length string type reported without a length — e.g.
|
||||
// Postgres/Kingbase's unbounded `character varying` via
|
||||
// format_type(). A bare passthrough would be invalid DDL for a
|
||||
// target that requires an explicit VARCHAR length (MySQL and its
|
||||
// family: VARCHAR/CHARACTER VARYING with no length is a syntax
|
||||
// error there). Default to 255, matching the same fallback
|
||||
// apply_template() already uses when a mapped type's length is
|
||||
// missing. CHAR/CHARACTER are deliberately excluded: MySQL
|
||||
// accepts a bare CHAR as CHAR(1), so defaulting it to 255 would
|
||||
// silently change valid output into a different, wrong length.
|
||||
// VARCHAR2/NVARCHAR2 are also excluded: they aren't valid MySQL
|
||||
// type names at all, with or without a length, so defaulting
|
||||
// their length fixes nothing.
|
||||
if params.is_none()
|
||||
&& profile.requires_explicit_varchar_length
|
||||
&& profile.max_varchar_len.is_some()
|
||||
&& matches!(base.as_str(), "VARCHAR" | "CHARACTER VARYING" | "NVARCHAR")
|
||||
{
|
||||
return format!("{}(255)", source_type.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,6 +532,45 @@ mod tests {
|
||||
assert_eq!(t, "int(11)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_defaults_missing_length_on_cross_dialect_varchar() {
|
||||
// Postgres/Kingbase report an unbounded varchar column as a bare
|
||||
// `character varying` (no length) via format_type(). MySQL's
|
||||
// VARCHAR/CHARACTER VARYING requires an explicit length, so passing
|
||||
// that through verbatim is a syntax error (issue #8011).
|
||||
let source = Some(DialectKind::Postgres);
|
||||
assert_eq!(rewrite_column_type("character varying", DatabaseType::Mysql, source), "character varying(255)");
|
||||
assert_eq!(rewrite_column_type("varchar", DatabaseType::Mysql, source), "varchar(255)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_leaves_bare_char_untouched() {
|
||||
// Unlike VARCHAR, a bare CHAR/CHARACTER is already valid MySQL
|
||||
// syntax (implicitly CHAR(1)); defaulting it to 255 would silently
|
||||
// change correct output into a different, wrong length.
|
||||
let source = Some(DialectKind::Postgres);
|
||||
assert_eq!(rewrite_column_type("character", DatabaseType::Mysql, source), "character");
|
||||
assert_eq!(rewrite_column_type("char", DatabaseType::Mysql, source), "char");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_keeps_explicit_length_on_cross_dialect_varchar() {
|
||||
let source = Some(DialectKind::Postgres);
|
||||
assert_eq!(
|
||||
rewrite_column_type("character varying(120)", DatabaseType::Mysql, source),
|
||||
"character varying(120)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_same_dialect_varchar_is_untouched_regardless_of_length() {
|
||||
// Same-dialect metadata (or unknown source dialect) must never be
|
||||
// rewritten — only a genuine cross-dialect gap should trigger the
|
||||
// missing-length default.
|
||||
assert_eq!(rewrite_column_type("varchar(255)", DatabaseType::Mysql, Some(DialectKind::Mysql)), "varchar(255)");
|
||||
assert_eq!(rewrite_column_type("varchar(255)", DatabaseType::Mysql, None), "varchar(255)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_len_params_strips_oracle_char_unit() {
|
||||
assert_eq!(normalize_len_params("(6 CHAR)"), "(6)");
|
||||
|
||||
Reference in new issue
Block a user