fix(dameng): wrap identity columns when pasting table data

This commit is contained in:
弈秋
2026-09-21 19:04:29 +08:00
committed by GitHub
parent be055a60cc
commit 0c6fc610cd
4 changed files with 58 additions and 1 deletions
@@ -334,6 +334,7 @@ export interface CopyTableDataSqlOptions {
columns?: string[];
postgresOverridingSystemValue?: boolean;
sqlserverIdentityInsert?: boolean;
damengIdentityInsert?: boolean;
normalizeNewTargetName?: boolean;
/** Quote character reported by the connected server, for types whose quote is not fixed by the
* database type alone (Cloud Spanner's two dialects differ). Mirrors `identifierQuote` on the
@@ -18,6 +18,7 @@ export interface TableDataCopyColumnOptions {
columns: string[];
postgresOverridingSystemValue: boolean;
sqlserverIdentityInsert: boolean;
damengIdentityInsert: boolean;
}
export interface TablePasteFeedback {
@@ -88,6 +89,7 @@ export function tableDataCopyColumnOptions(databaseType: DatabaseType | undefine
columns: writableColumns.map((column) => column.name),
postgresOverridingSystemValue: databaseType === "postgres" && writableColumns.some(isIdentityColumn),
sqlserverIdentityInsert: databaseType === "sqlserver" && writableColumns.some(isIdentityColumn),
damengIdentityInsert: databaseType === "dameng" && writableColumns.some(isIdentityColumn),
};
}
+43 -1
View File
@@ -241,6 +241,8 @@ pub struct CopyTableDataSqlOptions {
#[serde(default)]
pub sqlserver_identity_insert: bool,
#[serde(default)]
pub dameng_identity_insert: bool,
#[serde(default)]
pub normalize_new_target_name: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identifier_quote: Option<String>,
@@ -895,7 +897,10 @@ pub fn build_copy_table_data_sql(options: CopyTableDataSqlOptions) -> String {
let insert_sql = format!(
"INSERT INTO {target} ({target_column_list}){postgres_override} SELECT {source_column_list} FROM {source};"
);
if options.sqlserver_identity_insert && options.database_type == Some(DatabaseType::SqlServer) {
let needs_identity_insert = (options.sqlserver_identity_insert
&& options.database_type == Some(DatabaseType::SqlServer))
|| (options.dameng_identity_insert && options.database_type == Some(DatabaseType::Dameng));
if needs_identity_insert {
return format!("SET IDENTITY_INSERT {target} ON;\n{insert_sql}\nSET IDENTITY_INSERT {target} OFF;");
}
insert_sql
@@ -2554,6 +2559,7 @@ mod tests {
columns: None,
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: false,
identifier_quote: None,
}),
@@ -2568,6 +2574,7 @@ mod tests {
columns: Some(vec!["id".to_string(), "name".to_string()]),
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: false,
identifier_quote: None,
}),
@@ -2582,6 +2589,7 @@ mod tests {
columns: Some(vec!["id".to_string(), "name".to_string()]),
postgres_overriding_system_value: true,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: false,
identifier_quote: None,
}),
@@ -2596,11 +2604,40 @@ mod tests {
columns: Some(vec!["id".to_string(), "name".to_string()]),
postgres_overriding_system_value: false,
sqlserver_identity_insert: true,
dameng_identity_insert: false,
normalize_new_target_name: false,
identifier_quote: None,
}),
"SET IDENTITY_INSERT [dbo].[users_copy] ON;\nINSERT INTO [dbo].[users_copy] ([id], [name]) SELECT [id], [name] FROM [dbo].[users];\nSET IDENTITY_INSERT [dbo].[users_copy] OFF;"
);
{
let dameng = build_copy_table_data_sql(CopyTableDataSqlOptions {
database_type: Some(DatabaseType::Dameng),
schema: Some("DCSS".to_string()),
source_name: "users".to_string(),
target_name: "users_copy".to_string(),
columns: Some(vec!["id".to_string(), "name".to_string()]),
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: true,
normalize_new_target_name: false,
identifier_quote: None,
});
assert!(dameng.starts_with("SET IDENTITY_INSERT "), "dameng copy should enable identity insert: {dameng}");
assert!(dameng.ends_with("OFF;"), "dameng copy should disable identity insert: {dameng}");
assert!(
dameng.contains("INSERT INTO ") && dameng.contains(" SELECT "),
"dameng copy should carry an INSERT..SELECT: {dameng}"
);
// Dameng rejects assigning an identity column unless a column list is specified.
assert!(
dameng.contains("(\"id\", \"name\")")
|| dameng.contains("(`id`, `name`)")
|| dameng.contains("(id, name)")
|| dameng.contains("([id], [name])"),
"dameng copy must use an explicit column list: {dameng}"
);
}
assert_eq!(
build_copy_table_data_sql(CopyTableDataSqlOptions {
database_type: Some(DatabaseType::Dameng),
@@ -2610,6 +2647,7 @@ mod tests {
columns: None,
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: true,
identifier_quote: None,
}),
@@ -2624,6 +2662,7 @@ mod tests {
columns: None,
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: false,
identifier_quote: None,
}),
@@ -2638,6 +2677,7 @@ mod tests {
columns: Some(vec!["user_id".to_string(), "userName".to_string(), "order total".to_string()]),
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: true,
identifier_quote: None,
}),
@@ -2652,6 +2692,7 @@ mod tests {
columns: Some(vec!["user_id".to_string()]),
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: false,
identifier_quote: None,
}),
@@ -2711,6 +2752,7 @@ mod tests {
columns: Some(vec!["user_id".to_string(), "userName".to_string()]),
postgres_overriding_system_value: false,
sqlserver_identity_insert: false,
dameng_identity_insert: false,
normalize_new_target_name: true,
identifier_quote: None,
});
+12
View File
@@ -66,16 +66,25 @@ test("table data copy uses only writable columns for first-class databases", ()
columns: ["id", "name"],
postgresOverridingSystemValue: false,
sqlserverIdentityInsert: true,
damengIdentityInsert: false,
});
assert.deepEqual(tableDataCopyColumnOptions("dameng", columns), {
columns: ["id", "name"],
postgresOverridingSystemValue: false,
sqlserverIdentityInsert: false,
damengIdentityInsert: true,
});
assert.deepEqual(tableDataCopyColumnOptions("postgres", [{ ...columns[0], extra: "generated always as identity" }, { ...columns[1] }, { ...columns[2], extra: "generated always as (name) stored" }]), {
columns: ["id", "name"],
postgresOverridingSystemValue: true,
sqlserverIdentityInsert: false,
damengIdentityInsert: false,
});
assert.deepEqual(tableDataCopyColumnOptions("mysql", [{ ...columns[0], extra: "auto_increment" }, { ...columns[1] }, { ...columns[2], extra: "STORED GENERATED" }, column("expr_default", "DEFAULT_GENERATED"), column("expr_default_on_update", "DEFAULT_GENERATED on update CURRENT_TIMESTAMP"), column("virtual_full_name", "VIRTUAL GENERATED"), column("stored_full_name", "GENERATED ALWAYS AS (concat(first_name, ' ', last_name)) STORED")]), {
columns: ["id", "name", "expr_default", "expr_default_on_update"],
postgresOverridingSystemValue: false,
sqlserverIdentityInsert: false,
damengIdentityInsert: false,
});
});
@@ -94,15 +103,18 @@ test("table data copy skips only SQL Server rowversion types", () => {
columns: ["id", "name", "fixed_binary", "variable_binary"],
postgresOverridingSystemValue: false,
sqlserverIdentityInsert: true,
damengIdentityInsert: false,
});
assert.deepEqual(tableDataCopyColumnOptions("postgres", [column("updated_at", "timestamp")]), {
columns: ["updated_at"],
postgresOverridingSystemValue: false,
sqlserverIdentityInsert: false,
damengIdentityInsert: false,
});
assert.deepEqual(tableDataCopyColumnOptions("mysql", [column("updated_at", "timestamp")]), {
columns: ["updated_at"],
postgresOverridingSystemValue: false,
sqlserverIdentityInsert: false,
damengIdentityInsert: false,
});
});