diff --git a/crates/dbx-core/src/table_structure_sql/column_alter.rs b/crates/dbx-core/src/table_structure_sql/column_alter.rs index 09d69583f..b1a68e2d2 100644 --- a/crates/dbx-core/src/table_structure_sql/column_alter.rs +++ b/crates/dbx-core/src/table_structure_sql/column_alter.rs @@ -77,7 +77,11 @@ pub fn build_single_column_alter_sql(options: SingleColumnAlterSqlOptions) -> Ta StructureDialect::Doris => statements.extend(build_doris_existing_column_sql(&table, &options.column, "")), StructureDialect::Postgres => statements.extend(build_postgres_existing_column_sql(&table, &options.column)), StructureDialect::Oracle | StructureDialect::Dameng => { - statements.extend(build_oracle_like_existing_column_sql(dialect, &table, &options.column)) + if options.database_type == Some(crate::models::connection::DatabaseType::Xugu) { + statements.extend(build_xugu_existing_column_sql(&table, &options.column)); + } else { + statements.extend(build_oracle_like_existing_column_sql(dialect, &table, &options.column)) + } } StructureDialect::H2 => statements.extend(build_h2_existing_column_sql(&table, &options.column)), StructureDialect::ClickHouse => { @@ -287,6 +291,19 @@ pub(super) fn build_doris_existing_column_sql( } pub(super) fn build_postgres_existing_column_sql(table: &str, column: &EditableStructureColumn) -> Vec { + build_postgres_like_existing_column_sql(table, column, false) +} + +pub(super) fn build_xugu_existing_column_sql(table: &str, column: &EditableStructureColumn) -> Vec { + // Xugu shares PostgreSQL's per-attribute ALTER flow, but its type clause omits TYPE entirely. + build_postgres_like_existing_column_sql(table, column, true) +} + +fn build_postgres_like_existing_column_sql( + table: &str, + column: &EditableStructureColumn, + use_xugu_type_syntax: bool, +) -> Vec { let Some(original) = &column.original else { return Vec::new(); }; @@ -300,11 +317,10 @@ pub(super) fn build_postgres_existing_column_sql(table: &str, column: &EditableS )); } if column.data_type.trim() != original.data_type.trim() { - statements.push(format!( - "ALTER TABLE {table} ALTER COLUMN {} TYPE {};", - quote_ident(StructureDialect::Postgres, current_name), - column_data_type(StructureDialect::Postgres, column) - )); + let column_name = quote_ident(StructureDialect::Postgres, current_name); + let data_type = column_data_type(StructureDialect::Postgres, column); + let type_clause = if use_xugu_type_syntax { data_type } else { format!("TYPE {data_type}") }; + statements.push(format!("ALTER TABLE {table} ALTER COLUMN {column_name} {type_clause};")); } if column.is_nullable != original.is_nullable { let action = if column.is_nullable { "DROP NOT NULL" } else { "SET NOT NULL" }; diff --git a/crates/dbx-core/src/table_structure_sql/columns.rs b/crates/dbx-core/src/table_structure_sql/columns.rs index fa10bf6cc..b73943b0e 100644 --- a/crates/dbx-core/src/table_structure_sql/columns.rs +++ b/crates/dbx-core/src/table_structure_sql/columns.rs @@ -2,7 +2,8 @@ use super::column_alter::{ build_clickhouse_existing_column_sql, build_doris_existing_column_sql, build_h2_existing_column_sql, build_informix_existing_column_sql, build_mysql_existing_column_sql, build_oracle_like_existing_column_sql, build_postgres_existing_column_sql, build_questdb_existing_column_sql, build_sqlite_existing_column_sql, - build_sqlserver_existing_column_sql, has_column_extra_change, has_existing_column_attribute_change, + build_sqlserver_existing_column_sql, build_xugu_existing_column_sql, has_column_extra_change, + has_existing_column_attribute_change, }; use super::column_format::{ column_definition, has_dameng_identity, is_dameng_identity_compatible_type, is_mysql_character_data_type, @@ -160,7 +161,11 @@ pub(super) fn build_column_sql(options: &TableStructureSqlOptions, warnings: &mu StructureDialect::Doris => statements.extend(build_doris_existing_column_sql(&table, column, "")), StructureDialect::Postgres => statements.extend(build_postgres_existing_column_sql(&table, column)), StructureDialect::Oracle | StructureDialect::Dameng => { - statements.extend(build_oracle_like_existing_column_sql(dialect, &table, column)) + if options.database_type == Some(crate::models::connection::DatabaseType::Xugu) { + statements.extend(build_xugu_existing_column_sql(&table, column)); + } else { + statements.extend(build_oracle_like_existing_column_sql(dialect, &table, column)) + } } StructureDialect::H2 => statements.extend(build_h2_existing_column_sql(&table, column)), StructureDialect::ClickHouse => statements.extend(build_clickhouse_existing_column_sql( diff --git a/crates/dbx-core/src/table_structure_sql/tests.rs b/crates/dbx-core/src/table_structure_sql/tests.rs index 8983aeec7..c97bc8b56 100644 --- a/crates/dbx-core/src/table_structure_sql/tests.rs +++ b/crates/dbx-core/src/table_structure_sql/tests.rs @@ -120,6 +120,83 @@ fn builds_mysql_column_and_index_changes() { ); } +#[test] +fn builds_xugu_type_change_with_native_syntax() { + let mut code = column("code"); + code.data_type = "bigint".to_string(); + code.original = Some(ColumnInfo { + name: "code".to_string(), + data_type: "integer".to_string(), + is_nullable: true, + column_default: None, + is_primary_key: false, + extra: None, + comment: None, + ..Default::default() + }); + + let result = build_single_column_alter_sql(SingleColumnAlterSqlOptions { + database_type: Some(DatabaseType::Xugu), + schema: Some("public".to_string()), + table_name: "info_x".to_string(), + column: code, + }); + + assert_eq!(result.warnings, Vec::::new()); + assert_eq!(result.statements, vec!["ALTER TABLE \"public\".\"info_x\" ALTER COLUMN \"code\" bigint;"]); + + let mut code = column("code"); + code.data_type = "bigint".to_string(); + code.original = Some(ColumnInfo { + name: "code".to_string(), + data_type: "integer".to_string(), + is_nullable: true, + column_default: None, + is_primary_key: false, + extra: None, + comment: None, + ..Default::default() + }); + let result = build_table_structure_change_sql(TableStructureSqlOptions { + database_type: Some(DatabaseType::Xugu), + schema: Some("public".to_string()), + table_name: "info_x".to_string(), + columns: vec![code], + indexes: Vec::new(), + foreign_keys: Vec::new(), + triggers: Vec::new(), + table_comment: None, + original_table_comment: None, + }); + + assert_eq!(result.warnings, Vec::::new()); + assert_eq!(result.statements, vec!["ALTER TABLE \"public\".\"info_x\" ALTER COLUMN \"code\" bigint;"]); + + let mut postgres_code = column("code"); + postgres_code.data_type = "integer".to_string(); + postgres_code.original = Some(ColumnInfo { + name: "code".to_string(), + data_type: "varchar(20)".to_string(), + is_nullable: true, + column_default: None, + is_primary_key: false, + extra: None, + comment: None, + ..Default::default() + }); + let postgres_result = build_single_column_alter_sql(SingleColumnAlterSqlOptions { + database_type: Some(DatabaseType::Postgres), + schema: Some("public".to_string()), + table_name: "info_x".to_string(), + column: postgres_code, + }); + + assert_eq!( + postgres_result.statements, + vec!["ALTER TABLE \"public\".\"info_x\" ALTER COLUMN \"code\" TYPE integer;"] + ); +} + #[test] fn builds_mysql_unsigned_integer_column_with_length_before_attribute() { let mut score = column("score");