1 parent
d29c2d85cb
commit
6cf8d8e035
2 files changed
+394
-11
No files matched your search
@@ -6,7 +6,7 @@ use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::models::connection::DatabaseType;
|
||||
use crate::sql_dialect::ddl_profile::{profile_for, DdlDialectProfile};
|
||||
use crate::sql_dialect::ddl_profile::{profile_for, ColumnDdlSyntax, CommentDdlSyntax, DdlDialectProfile};
|
||||
use crate::sql_dialect::descriptor::DialectKind;
|
||||
use crate::sql_dialect::inference::{ColumnType, DefaultTypeInferenceEngine, TypeInferenceEngine};
|
||||
use crate::sql_dialect::type_rewrite::{
|
||||
@@ -1164,15 +1164,13 @@ impl RollbackGraph {
|
||||
}
|
||||
|
||||
fn invert_change_string(ch: &str) -> String {
|
||||
if let Some(_pos) = ch.find(" → ") {
|
||||
let parts: Vec<&str> = ch.split(" → ").collect();
|
||||
if parts.len() == 2 {
|
||||
format!("{} → {}", parts[1], parts[0])
|
||||
} else {
|
||||
ch.to_string()
|
||||
}
|
||||
let Some((before, after)) = ch.split_once(" → ") else {
|
||||
return ch.to_string();
|
||||
};
|
||||
if let Some((kind, value)) = before.split_once(": ") {
|
||||
format!("{kind}: {after} → {value}")
|
||||
} else {
|
||||
ch.to_string()
|
||||
format!("{after} → {before}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3424,12 +3422,18 @@ fn column_comment_sql(
|
||||
if profile.column_comment_via_modify_only {
|
||||
return format!("-- Column comment for {column_name}: use ALTER TABLE ... MODIFY COLUMN to set comment");
|
||||
}
|
||||
if profile.comment_ddl == CommentDdlSyntax::SqlServerExtendedProperty {
|
||||
return sqlserver_column_comment_sql(table_name, column_name, comment, db_type, schema);
|
||||
}
|
||||
let table = qualified_name(table_name, db_type, schema);
|
||||
format!("COMMENT ON COLUMN {table}.{} IS {};", quote_id(column_name, db_type), comment_literal(comment))
|
||||
}
|
||||
|
||||
fn table_comment_sql(table_name: &str, comment: &str, db_type: DatabaseType, schema: Option<&str>) -> String {
|
||||
let profile = profile_for(db_type);
|
||||
if profile.comment_ddl == CommentDdlSyntax::SqlServerExtendedProperty {
|
||||
return sqlserver_table_comment_sql(table_name, comment, db_type, schema);
|
||||
}
|
||||
let table = qualified_name(table_name, db_type, schema);
|
||||
if profile.table_comment_via_alter {
|
||||
format!("ALTER TABLE {table} COMMENT = {};", comment_literal(comment))
|
||||
@@ -3438,6 +3442,102 @@ fn table_comment_sql(table_name: &str, comment: &str, db_type: DatabaseType, sch
|
||||
}
|
||||
}
|
||||
|
||||
fn sqlserver_schema_name(schema: Option<&str>) -> &str {
|
||||
schema.map(str::trim).filter(|name| !name.is_empty()).unwrap_or("dbo")
|
||||
}
|
||||
|
||||
fn sqlserver_column_comment_sql(
|
||||
table_name: &str,
|
||||
column_name: &str,
|
||||
comment: &str,
|
||||
db_type: DatabaseType,
|
||||
schema: Option<&str>,
|
||||
) -> String {
|
||||
let table = qualified_name(table_name, db_type, schema);
|
||||
let object_id = table.replace('\'', "''");
|
||||
let schema = sqlserver_schema_name(schema).replace('\'', "''");
|
||||
let table_name = table_name.replace('\'', "''");
|
||||
let column_name = column_name.replace('\'', "''");
|
||||
let exists = format!(
|
||||
"EXISTS (SELECT 1 FROM sys.extended_properties WHERE major_id = OBJECT_ID(N'{object_id}') AND minor_id = COLUMNPROPERTY(OBJECT_ID(N'{object_id}'), N'{column_name}', 'ColumnId') AND name = N'MS_Description')"
|
||||
);
|
||||
let levels = format!(
|
||||
"@level0type=N'SCHEMA', @level0name=N'{schema}', @level1type=N'TABLE', @level1name=N'{table_name}', @level2type=N'COLUMN', @level2name=N'{column_name}'"
|
||||
);
|
||||
|
||||
if comment.is_empty() {
|
||||
format!("IF {exists} EXEC sys.sp_dropextendedproperty @name=N'MS_Description', {levels};")
|
||||
} else {
|
||||
let comment = comment.replace('\'', "''");
|
||||
format!(
|
||||
"IF {exists} EXEC sys.sp_updateextendedproperty @name=N'MS_Description', @value=N'{comment}', {levels} ELSE EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{comment}', {levels};"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn sqlserver_table_comment_sql(table_name: &str, comment: &str, db_type: DatabaseType, schema: Option<&str>) -> String {
|
||||
let table = qualified_name(table_name, db_type, schema);
|
||||
let object_id = table.replace('\'', "''");
|
||||
let schema = sqlserver_schema_name(schema).replace('\'', "''");
|
||||
let table_name = table_name.replace('\'', "''");
|
||||
let exists = format!(
|
||||
"EXISTS (SELECT 1 FROM sys.extended_properties WHERE major_id = OBJECT_ID(N'{object_id}') AND minor_id = 0 AND name = N'MS_Description')"
|
||||
);
|
||||
let levels =
|
||||
format!("@level0type=N'SCHEMA', @level0name=N'{schema}', @level1type=N'TABLE', @level1name=N'{table_name}'");
|
||||
|
||||
if comment.is_empty() {
|
||||
format!("IF {exists} EXEC sys.sp_dropextendedproperty @name=N'MS_Description', {levels};")
|
||||
} else {
|
||||
let comment = comment.replace('\'', "''");
|
||||
format!(
|
||||
"IF {exists} EXEC sys.sp_updateextendedproperty @name=N'MS_Description', @value=N'{comment}', {levels} ELSE EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{comment}', {levels};"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn sqlserver_exec_batch(sql: &str) -> String {
|
||||
format!("EXEC sys.sp_executesql N'{}';", sql.replace('\'', "''"))
|
||||
}
|
||||
|
||||
fn sqlserver_has_default(column: Option<&ColumnInfo>) -> bool {
|
||||
column.and_then(|column| column.column_default.as_deref()).is_some_and(|default| {
|
||||
let default = default.trim();
|
||||
!default.is_empty() && !default.eq_ignore_ascii_case("null")
|
||||
})
|
||||
}
|
||||
|
||||
fn sqlserver_drop_default_constraint_sql(table: &str, column_name: &str) -> String {
|
||||
let table_literal = table.replace('\'', "''");
|
||||
let column_literal = column_name.replace('\'', "''");
|
||||
sqlserver_exec_batch(&format!(
|
||||
"DECLARE @dbx_default_sql NVARCHAR(MAX); \
|
||||
SELECT TOP (1) @dbx_default_sql = N'ALTER TABLE {table_literal} DROP CONSTRAINT ' + QUOTENAME(dc.name) \
|
||||
FROM sys.default_constraints AS dc \
|
||||
WHERE dc.parent_object_id = OBJECT_ID(N'{table_literal}') AND dc.parent_column_id = COLUMNPROPERTY(OBJECT_ID(N'{table_literal}'), N'{column_literal}', 'ColumnId'); \
|
||||
IF @dbx_default_sql IS NOT NULL EXEC sys.sp_executesql @dbx_default_sql;"
|
||||
))
|
||||
}
|
||||
|
||||
fn sqlserver_alter_column_preserving_default_sql(table: &str, column_name: &str, column: &ColumnInfo) -> String {
|
||||
let table_literal = table.replace('\'', "''");
|
||||
let column_literal = column_name.replace('\'', "''");
|
||||
let quoted_column = quote_id(column_name, DatabaseType::SqlServer);
|
||||
let quoted_column_literal = quoted_column.replace('\'', "''");
|
||||
let nullability = if column.is_nullable { "NULL" } else { "NOT NULL" };
|
||||
|
||||
sqlserver_exec_batch(&format!(
|
||||
"DECLARE @dbx_default_sql NVARCHAR(MAX), @dbx_default_name sysname, @dbx_default_definition NVARCHAR(MAX); \
|
||||
SELECT TOP (1) @dbx_default_name = dc.name, @dbx_default_definition = dc.definition \
|
||||
FROM sys.default_constraints AS dc \
|
||||
WHERE dc.parent_object_id = OBJECT_ID(N'{table_literal}') AND dc.parent_column_id = COLUMNPROPERTY(OBJECT_ID(N'{table_literal}'), N'{column_literal}', 'ColumnId'); \
|
||||
IF @dbx_default_name IS NOT NULL BEGIN SET @dbx_default_sql = N'ALTER TABLE {table_literal} DROP CONSTRAINT ' + QUOTENAME(@dbx_default_name); EXEC sys.sp_executesql @dbx_default_sql; END; \
|
||||
ALTER TABLE {table} ALTER COLUMN {quoted_column} {data_type} {nullability}; \
|
||||
IF @dbx_default_name IS NOT NULL BEGIN SET @dbx_default_sql = N'ALTER TABLE {table_literal} ADD CONSTRAINT ' + QUOTENAME(@dbx_default_name) + N' DEFAULT ' + @dbx_default_definition + N' FOR {quoted_column_literal}'; EXEC sys.sp_executesql @dbx_default_sql; END;",
|
||||
data_type = column.data_type,
|
||||
))
|
||||
}
|
||||
|
||||
fn create_trigger_sql(
|
||||
profile: &crate::sql_dialect::ddl_profile::DdlDialectProfile,
|
||||
name: &str,
|
||||
@@ -4084,8 +4184,10 @@ fn generate_schema_sync_sql_inner(
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let add_column =
|
||||
if profile.column_ddl == ColumnDdlSyntax::SqlServer { "ADD" } else { "ADD COLUMN" };
|
||||
parts.push(format!(
|
||||
" ADD COLUMN {}{}",
|
||||
" {add_column} {}{}",
|
||||
column_def(&convert_col(source), db_type, source_dialect),
|
||||
position
|
||||
));
|
||||
@@ -4097,7 +4199,51 @@ fn generate_schema_sync_sql_inner(
|
||||
"modified" => {
|
||||
if let Some(source) = &column.source {
|
||||
let mapped = convert_col(source);
|
||||
if profile.alter_uses_modify_column {
|
||||
if profile.column_ddl == ColumnDdlSyntax::SqlServer {
|
||||
let name = quote_id(&column.name, db_type);
|
||||
let definition_changed = column
|
||||
.changes
|
||||
.iter()
|
||||
.any(|change| change.starts_with("type:") || change.starts_with("nullable:"));
|
||||
let default_changed =
|
||||
column.changes.iter().any(|change| change.starts_with("default:"));
|
||||
let had_default = sqlserver_has_default(column.target.as_ref());
|
||||
|
||||
if definition_changed && had_default && !default_changed {
|
||||
standalone_statements.push(sqlserver_alter_column_preserving_default_sql(
|
||||
&table,
|
||||
&column.name,
|
||||
&mapped,
|
||||
));
|
||||
} else {
|
||||
if definition_changed && had_default {
|
||||
standalone_statements
|
||||
.push(sqlserver_drop_default_constraint_sql(&table, &column.name));
|
||||
}
|
||||
if definition_changed {
|
||||
let nullability = if source.is_nullable { "NULL" } else { "NOT NULL" };
|
||||
parts.push(format!(" ALTER COLUMN {name} {} {nullability}", mapped.data_type));
|
||||
}
|
||||
}
|
||||
|
||||
if default_changed {
|
||||
if had_default && !definition_changed {
|
||||
standalone_statements
|
||||
.push(sqlserver_drop_default_constraint_sql(&table, &column.name));
|
||||
}
|
||||
if let Some(default) = &source.column_default {
|
||||
parts.push(format!(
|
||||
" ADD DEFAULT {} FOR {name}",
|
||||
default_literal(
|
||||
default,
|
||||
&mapped.data_type,
|
||||
effective_source_dialect(source_dialect, db_type),
|
||||
source.extra.as_deref()
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
} else if profile.alter_uses_modify_column {
|
||||
if column.changes.iter().any(|change| !change.starts_with("order:")) {
|
||||
parts.push(format!(
|
||||
" MODIFY COLUMN {}",
|
||||
@@ -4828,6 +4974,208 @@ mod tests {
|
||||
assert!(!sql.contains('`'), "SQL Server no backticks: {sql}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_schema_diff_adds_columns_and_unicode_comments_with_tsql() {
|
||||
let source = vec![ColumnInfo {
|
||||
is_nullable: true,
|
||||
comment: Some("厂家追溯码's".to_string()),
|
||||
..column("manufacture_trace_code", "varchar(200)", None)
|
||||
}];
|
||||
let diffs = diff_columns_with_options(&source, &[], false, false, false, 0.5);
|
||||
let sql = generate_schema_sync_sql(
|
||||
&[wrap_table_diff("inter_putaway_sub", diffs)],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
DatabaseType::SqlServer,
|
||||
Some("dbo"),
|
||||
false,
|
||||
Some(DialectKind::SqlServer),
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(
|
||||
sql.contains("ALTER TABLE \"dbo\".\"inter_putaway_sub\" ADD \"manufacture_trace_code\" varchar(200);"),
|
||||
"SQL Server ADD syntax: {sql}"
|
||||
);
|
||||
assert!(sql.contains("sys.sp_addextendedproperty"), "SQL Server comment add: {sql}");
|
||||
assert!(sql.contains("@value=N'厂家追溯码''s'"), "Unicode comment escaping: {sql}");
|
||||
assert!(!sql.contains("ADD COLUMN"), "SQL Server must not emit ADD COLUMN: {sql}");
|
||||
assert!(!sql.contains("COMMENT ON"), "SQL Server must not emit COMMENT ON: {sql}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_schema_diff_renders_default_constraint_transitions_as_single_batches() {
|
||||
let transitions =
|
||||
[(None, Some("((0))"), true), (Some("((0))"), Some("((1))"), true), (Some("((0))"), None, false)];
|
||||
|
||||
for (old_default, new_default, expects_add) in transitions {
|
||||
let source = vec![ColumnInfo {
|
||||
column_default: new_default.map(str::to_string),
|
||||
..column("frozen_status", "int", None)
|
||||
}];
|
||||
let target = vec![ColumnInfo {
|
||||
column_default: old_default.map(str::to_string),
|
||||
..column("frozen_status", "int", None)
|
||||
}];
|
||||
let diffs = diff_columns_with_options(&source, &target, false, false, false, 0.5);
|
||||
let sql = generate_schema_sync_sql(
|
||||
&[wrap_table_diff("wbs_flat_package_data_sub", diffs)],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
DatabaseType::SqlServer,
|
||||
Some("dbo"),
|
||||
false,
|
||||
Some(DialectKind::SqlServer),
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(!sql.contains(" SET DEFAULT "), "SQL Server must not emit SET DEFAULT: {sql}");
|
||||
assert!(!sql.contains(" DROP DEFAULT"), "SQL Server must not emit DROP DEFAULT: {sql}");
|
||||
if old_default.is_some() {
|
||||
assert!(sql.contains("sys.default_constraints"), "default lookup: {sql}");
|
||||
assert!(sql.contains("DROP CONSTRAINT"), "default drop: {sql}");
|
||||
assert!(sql.contains("EXEC sys.sp_executesql N'"), "drop batch wrapper: {sql}");
|
||||
}
|
||||
if expects_add {
|
||||
assert!(
|
||||
sql.contains(&format!("ADD DEFAULT {} FOR \"frozen_status\"", new_default.unwrap())),
|
||||
"default add: {sql}"
|
||||
);
|
||||
} else {
|
||||
assert!(!sql.contains("ADD DEFAULT"), "default removal must not re-add: {sql}");
|
||||
}
|
||||
|
||||
let statements = crate::sql::split_sql_statements_for_database(&sql, DatabaseType::SqlServer);
|
||||
assert!(
|
||||
statements.iter().all(|statement| !statement.trim_start().starts_with("DECLARE ")),
|
||||
"SQL Server splitter must keep helper batches together: {statements:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_schema_diff_alters_full_column_definition_and_preserves_default() {
|
||||
let source = vec![ColumnInfo {
|
||||
is_nullable: false,
|
||||
column_default: Some("((0))".to_string()),
|
||||
..column("amount", "bigint", None)
|
||||
}];
|
||||
let target = vec![ColumnInfo {
|
||||
is_nullable: true,
|
||||
column_default: Some("((0))".to_string()),
|
||||
..column("amount", "int", None)
|
||||
}];
|
||||
let diffs = diff_columns_with_options(&source, &target, false, false, false, 0.5);
|
||||
let sql = generate_schema_sync_sql(
|
||||
&[wrap_table_diff("payments", diffs)],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
DatabaseType::SqlServer,
|
||||
Some("billing"),
|
||||
false,
|
||||
Some(DialectKind::SqlServer),
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(
|
||||
sql.contains("ALTER TABLE \"billing\".\"payments\" ALTER COLUMN \"amount\" bigint NOT NULL"),
|
||||
"full SQL Server column definition: {sql}"
|
||||
);
|
||||
assert!(sql.contains("dc.definition"), "existing default definition must be captured: {sql}");
|
||||
assert!(sql.contains("ADD CONSTRAINT"), "existing default constraint must be restored: {sql}");
|
||||
assert!(!sql.contains(" ALTER COLUMN \"amount\" TYPE "), "no PostgreSQL TYPE syntax: {sql}");
|
||||
assert!(!sql.contains(" SET NOT NULL"), "no PostgreSQL nullability syntax: {sql}");
|
||||
|
||||
let statements = crate::sql::split_sql_statements_for_database(&sql, DatabaseType::SqlServer);
|
||||
assert_eq!(statements.len(), 1, "preserve-default batch must stay atomic: {statements:?}");
|
||||
assert!(statements[0].trim_start().starts_with("-- Alter table: payments\nEXEC sys.sp_executesql N'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_schema_diff_upserts_and_drops_escaped_column_comments() {
|
||||
let cases = [
|
||||
(None, Some("owner's 新值"), true, true, false),
|
||||
(Some("旧值"), Some("owner's 新值"), true, true, false),
|
||||
(Some("旧值"), None, false, false, true),
|
||||
];
|
||||
|
||||
for (old_comment, new_comment, expects_add, expects_update, expects_drop) in cases {
|
||||
let source = vec![column("display'name", "nvarchar(80)", new_comment)];
|
||||
let target = vec![column("display'name", "nvarchar(80)", old_comment)];
|
||||
let diffs = diff_columns_with_options(&source, &target, false, false, false, 0.5);
|
||||
let sql = generate_schema_sync_sql(
|
||||
&[wrap_table_diff("user's", diffs)],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
DatabaseType::SqlServer,
|
||||
Some("app's"),
|
||||
false,
|
||||
Some(DialectKind::SqlServer),
|
||||
&[],
|
||||
);
|
||||
|
||||
assert_eq!(sql.contains("sys.sp_addextendedproperty"), expects_add, "comment add: {sql}");
|
||||
assert_eq!(sql.contains("sys.sp_updateextendedproperty"), expects_update, "comment update: {sql}");
|
||||
assert_eq!(sql.contains("sys.sp_dropextendedproperty"), expects_drop, "comment drop: {sql}");
|
||||
assert!(sql.contains("N'app''s'"), "schema escaping: {sql}");
|
||||
assert!(sql.contains("N'user''s'"), "table escaping: {sql}");
|
||||
assert!(sql.contains("N'display''name'"), "column escaping: {sql}");
|
||||
if new_comment.is_some() {
|
||||
assert!(sql.contains("N'owner''s 新值'"), "comment escaping: {sql}");
|
||||
}
|
||||
assert!(!sql.contains("COMMENT ON"), "SQL Server must not emit COMMENT ON: {sql}");
|
||||
|
||||
let statements = crate::sql::split_sql_statements_for_database(&sql, DatabaseType::SqlServer);
|
||||
assert_eq!(statements.len(), 1, "comment transition must be one T-SQL statement: {statements:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_schema_diff_rollback_uses_the_same_tsql_strategy() {
|
||||
let source = vec![ColumnInfo {
|
||||
column_default: Some("((1))".to_string()),
|
||||
comment: Some("new".to_string()),
|
||||
..column("status", "int", None)
|
||||
}];
|
||||
let target = vec![ColumnInfo {
|
||||
column_default: Some("((0))".to_string()),
|
||||
comment: Some("old".to_string()),
|
||||
..column("status", "int", None)
|
||||
}];
|
||||
let diffs = diff_columns_with_options(&source, &target, false, false, false, 0.5);
|
||||
let plan = generate_schema_sync_sql_plan(
|
||||
&[wrap_table_diff("orders", diffs)],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
DatabaseType::SqlServer,
|
||||
Some("dbo"),
|
||||
false,
|
||||
Some(DialectKind::SqlServer),
|
||||
&[],
|
||||
true,
|
||||
);
|
||||
let rollback = plan.rollback_sync_sql.expect("rollback SQL");
|
||||
|
||||
for (direction, sql) in [("forward", &plan.sync_sql), ("rollback", &rollback)] {
|
||||
assert!(sql.contains("sys.default_constraints"), "{direction} default constraint strategy: {sql}");
|
||||
assert!(sql.contains("sys.sp_updateextendedproperty"), "{direction} comment strategy: {sql}");
|
||||
assert!(!sql.contains(" SET DEFAULT "), "{direction} no PostgreSQL default syntax: {sql}");
|
||||
assert!(!sql.contains("COMMENT ON"), "{direction} no PostgreSQL comment syntax: {sql}");
|
||||
}
|
||||
assert!(plan.sync_sql.contains("ADD DEFAULT ((1)) FOR \"status\""), "forward: {}", plan.sync_sql);
|
||||
assert!(rollback.contains("ADD DEFAULT ((0)) FOR \"status\""), "rollback: {rollback}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn h2_rename_uses_alter_column_rename_to() {
|
||||
let diffs = make_col_diffs(
|
||||
|
||||
@@ -78,6 +78,23 @@ pub enum RenameColumnSyntax {
|
||||
SqlServerSpRename,
|
||||
}
|
||||
|
||||
/// Column DDL that cannot use the generic `ADD COLUMN` / `ALTER COLUMN` forms.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ColumnDdlSyntax {
|
||||
Generic,
|
||||
/// SQL Server uses `ADD` without `COLUMN`, a full definition for
|
||||
/// `ALTER COLUMN`, and separate default-constraint objects.
|
||||
SqlServer,
|
||||
}
|
||||
|
||||
/// Standalone table and column comment representation.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CommentDdlSyntax {
|
||||
Generic,
|
||||
/// SQL Server stores comments in the `MS_Description` extended property.
|
||||
SqlServerExtendedProperty,
|
||||
}
|
||||
|
||||
/// DDL generation profile for one [`DatabaseType`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DdlDialectProfile {
|
||||
@@ -108,8 +125,10 @@ pub struct DdlDialectProfile {
|
||||
pub table_comment_via_alter: bool,
|
||||
/// Standalone column comment SQL is unsupported (MySQL needs MODIFY COLUMN).
|
||||
pub column_comment_via_modify_only: bool,
|
||||
pub comment_ddl: CommentDdlSyntax,
|
||||
pub trigger_template: TriggerTemplate,
|
||||
pub rename_column: RenameColumnSyntax,
|
||||
pub column_ddl: ColumnDdlSyntax,
|
||||
/// MySQL `MODIFY COLUMN` vs ANSI `ALTER COLUMN … TYPE/SET`.
|
||||
pub alter_uses_modify_column: bool,
|
||||
/// Batch multiple alter clauses in one `ALTER TABLE` statement.
|
||||
@@ -303,8 +322,10 @@ fn mysql_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
index_supports_comment: true,
|
||||
table_comment_via_alter: true,
|
||||
column_comment_via_modify_only: true,
|
||||
comment_ddl: CommentDdlSyntax::Generic,
|
||||
trigger_template: TriggerTemplate::MysqlStyle,
|
||||
rename_column: RenameColumnSyntax::MysqlChangeColumn,
|
||||
column_ddl: ColumnDdlSyntax::Generic,
|
||||
prefers_native_source_ddl: true,
|
||||
grant_uses_mysql_user_syntax: true,
|
||||
warn_fk_needs_table_rebuild: false,
|
||||
@@ -346,8 +367,10 @@ fn postgres_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
index_supports_comment: false,
|
||||
table_comment_via_alter: false,
|
||||
column_comment_via_modify_only: false,
|
||||
comment_ddl: CommentDdlSyntax::Generic,
|
||||
trigger_template: TriggerTemplate::PostgresStyle,
|
||||
rename_column: RenameColumnSyntax::RenameColumn,
|
||||
column_ddl: ColumnDdlSyntax::Generic,
|
||||
prefers_native_source_ddl: false,
|
||||
grant_uses_mysql_user_syntax: false,
|
||||
warn_fk_needs_table_rebuild: false,
|
||||
@@ -389,8 +412,10 @@ fn oracle_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
index_supports_comment: false,
|
||||
table_comment_via_alter: false,
|
||||
column_comment_via_modify_only: false,
|
||||
comment_ddl: CommentDdlSyntax::Generic,
|
||||
trigger_template: TriggerTemplate::GenericRowBody,
|
||||
rename_column: RenameColumnSyntax::RenameColumn,
|
||||
column_ddl: ColumnDdlSyntax::Generic,
|
||||
prefers_native_source_ddl: false,
|
||||
grant_uses_mysql_user_syntax: false,
|
||||
warn_fk_needs_table_rebuild: false,
|
||||
@@ -433,8 +458,10 @@ fn sqlserver_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
index_supports_comment: false,
|
||||
table_comment_via_alter: false,
|
||||
column_comment_via_modify_only: false,
|
||||
comment_ddl: CommentDdlSyntax::SqlServerExtendedProperty,
|
||||
trigger_template: TriggerTemplate::SqlServerStyle,
|
||||
rename_column: RenameColumnSyntax::SqlServerSpRename,
|
||||
column_ddl: ColumnDdlSyntax::SqlServer,
|
||||
prefers_native_source_ddl: false,
|
||||
grant_uses_mysql_user_syntax: false,
|
||||
warn_fk_needs_table_rebuild: false,
|
||||
@@ -476,8 +503,10 @@ fn sqlite_family(db: DatabaseType) -> DdlDialectProfile {
|
||||
index_supports_comment: false,
|
||||
table_comment_via_alter: false,
|
||||
column_comment_via_modify_only: false,
|
||||
comment_ddl: CommentDdlSyntax::Generic,
|
||||
trigger_template: TriggerTemplate::GenericRowBody,
|
||||
rename_column: RenameColumnSyntax::RenameColumn,
|
||||
column_ddl: ColumnDdlSyntax::Generic,
|
||||
prefers_native_source_ddl: false,
|
||||
grant_uses_mysql_user_syntax: false,
|
||||
warn_fk_needs_table_rebuild: true,
|
||||
@@ -519,8 +548,10 @@ fn access_profile() -> DdlDialectProfile {
|
||||
index_supports_comment: false,
|
||||
table_comment_via_alter: false,
|
||||
column_comment_via_modify_only: false,
|
||||
comment_ddl: CommentDdlSyntax::Generic,
|
||||
trigger_template: TriggerTemplate::GenericRowBody,
|
||||
rename_column: RenameColumnSyntax::RenameColumn,
|
||||
column_ddl: ColumnDdlSyntax::Generic,
|
||||
prefers_native_source_ddl: false,
|
||||
grant_uses_mysql_user_syntax: false,
|
||||
warn_fk_needs_table_rebuild: false,
|
||||
@@ -562,8 +593,10 @@ fn h2_profile() -> DdlDialectProfile {
|
||||
index_supports_comment: false,
|
||||
table_comment_via_alter: false,
|
||||
column_comment_via_modify_only: false,
|
||||
comment_ddl: CommentDdlSyntax::Generic,
|
||||
trigger_template: TriggerTemplate::GenericRowBody,
|
||||
rename_column: RenameColumnSyntax::AlterColumnRenameTo,
|
||||
column_ddl: ColumnDdlSyntax::Generic,
|
||||
prefers_native_source_ddl: false,
|
||||
grant_uses_mysql_user_syntax: false,
|
||||
warn_fk_needs_table_rebuild: false,
|
||||
@@ -605,8 +638,10 @@ fn conservative_ansi(db: DatabaseType) -> DdlDialectProfile {
|
||||
index_supports_comment: false,
|
||||
table_comment_via_alter: false,
|
||||
column_comment_via_modify_only: false,
|
||||
comment_ddl: CommentDdlSyntax::Generic,
|
||||
trigger_template: TriggerTemplate::GenericRowBody,
|
||||
rename_column: RenameColumnSyntax::RenameColumn,
|
||||
column_ddl: ColumnDdlSyntax::Generic,
|
||||
prefers_native_source_ddl: false,
|
||||
grant_uses_mysql_user_syntax: false,
|
||||
warn_fk_needs_table_rebuild: false,
|
||||
|
||||
Reference in new issue
Block a user