fix(dameng): preserve table and column comments when cloning table
This commit is contained in:
parent
61e5073d25
commit
0862e63389
|
|
@ -116,6 +116,7 @@ import {
|
|||
buildCopyTableDataSql,
|
||||
buildEmptyTableSql,
|
||||
buildTruncateTableSql,
|
||||
collectDuplicateTableColumnComments,
|
||||
duplicateTableStructureRequiresScript,
|
||||
supportsDropTableCascade,
|
||||
supportsTruncateTableCascade,
|
||||
|
|
@ -3100,6 +3101,19 @@ function isDuplicateStructureSource(node: TreeNode): node is DuplicateStructureS
|
|||
return node.type === "table" && !!node.connectionId && !!node.database;
|
||||
}
|
||||
|
||||
/** Dameng CTAS does not copy comments; load column comments for COMMENT ON COLUMN. */
|
||||
async function loadDamengDuplicateColumnComments(connectionId: string, database: string, schema: string | undefined, sourceName: string, catalog?: string, sourceColumns?: ColumnInfo[]): Promise<{ columns?: ColumnInfo[]; columnComments: Array<{ name: string; comment: string }> }> {
|
||||
let columns = sourceColumns;
|
||||
if (!columns) {
|
||||
try {
|
||||
columns = await api.getColumns(connectionId, database, schema || "", sourceName, catalog);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to load Dameng column comments for table clone: ${sourceName}`, error);
|
||||
}
|
||||
}
|
||||
return { columns, columnComments: collectDuplicateTableColumnComments(columns ?? []) };
|
||||
}
|
||||
|
||||
async function confirmDuplicateStructure() {
|
||||
const node = duplicateStructureSource.value || (isDuplicateStructureSource(activeNode.value) ? activeNode.value : null);
|
||||
const newName = duplicateTableName.value.trim();
|
||||
|
|
@ -3109,12 +3123,14 @@ async function confirmDuplicateStructure() {
|
|||
try {
|
||||
await connectionStore.ensureConnected(node.connectionId);
|
||||
const databaseType = databaseTypeForNode(node);
|
||||
const columnComments = databaseType === "dameng" ? (await loadDamengDuplicateColumnComments(node.connectionId, node.database, node.schema, node.label, node.catalog)).columnComments : [];
|
||||
const sql = await buildDuplicateTableStructureSql({
|
||||
databaseType,
|
||||
schema: node.schema,
|
||||
sourceName: node.label,
|
||||
targetName: newName,
|
||||
tableComment: node.comment,
|
||||
columnComments,
|
||||
});
|
||||
await executeTreeNodeSqlWithProductionGuard(node, sql, {
|
||||
database: node.database,
|
||||
|
|
@ -3155,13 +3171,21 @@ async function confirmPasteTable() {
|
|||
try {
|
||||
await connectionStore.ensureConnected(entry.connectionId);
|
||||
const databaseType = entry.connectionId ? effectiveDatabaseTypeForConnection(connectionStore.getConfig(entry.connectionId)) : undefined;
|
||||
let sourceColumns: ColumnInfo[] | undefined;
|
||||
if (mode === "structure-and-data" || mode === "structure-only") {
|
||||
let columnComments: Array<{ name: string; comment: string }> = [];
|
||||
if (databaseType === "dameng") {
|
||||
const loaded = await loadDamengDuplicateColumnComments(entry.connectionId, entry.database, entry.schema, entry.sourceName);
|
||||
sourceColumns = loaded.columns;
|
||||
columnComments = loaded.columnComments;
|
||||
}
|
||||
const structureSql = await buildDuplicateTableStructureSql({
|
||||
databaseType,
|
||||
schema: entry.schema,
|
||||
sourceName: entry.sourceName,
|
||||
targetName,
|
||||
tableComment: entry.tableComment,
|
||||
columnComments,
|
||||
});
|
||||
const structureExecuted = await executeTreeNodeSqlWithProductionGuard(entry, structureSql, {
|
||||
database: entry.database,
|
||||
|
|
@ -3176,7 +3200,9 @@ async function confirmPasteTable() {
|
|||
queueRefreshTarget(entry);
|
||||
}
|
||||
if (copyData) {
|
||||
const sourceColumns = await api.getColumns(entry.connectionId, entry.database, entry.schema || "", entry.sourceName);
|
||||
if (!sourceColumns) {
|
||||
sourceColumns = await api.getColumns(entry.connectionId, entry.database, entry.schema || "", entry.sourceName);
|
||||
}
|
||||
const dataCopyColumnOptions = tableDataCopyColumnOptions(databaseType, sourceColumns);
|
||||
if (dataCopyColumnOptions.columns.length === 0) {
|
||||
throw new Error("No writable columns available for table data copy.");
|
||||
|
|
|
|||
|
|
@ -33,4 +33,13 @@ describe("cross-database table paste", () => {
|
|||
expect(runtimeSource).toMatch(/targetName,\s*tableComment: entry\.tableComment/);
|
||||
expect(runtimeSource).toContain("executeAsScript: duplicateTableStructureRequiresScript(structureSql)");
|
||||
});
|
||||
|
||||
it("loads Dameng column comments for sidebar duplicate and paste structure clone", () => {
|
||||
expect(runtimeSource).toContain("collectDuplicateTableColumnComments");
|
||||
expect(runtimeSource).toContain("async function loadDamengDuplicateColumnComments(");
|
||||
expect(runtimeSource).toMatch(/databaseType === "dameng"[\s\S]*?loadDamengDuplicateColumnComments\([\s\S]*?node\.connectionId[\s\S]*?columnComments/);
|
||||
expect(runtimeSource).toMatch(/if \(databaseType === "dameng"\) \{[\s\S]*?loadDamengDuplicateColumnComments\([\s\S]*?entry\.connectionId[\s\S]*?columnComments = loaded\.columnComments/);
|
||||
expect(runtimeSource).toMatch(/tableComment: node\.comment,\s*columnComments,/);
|
||||
expect(runtimeSource).toMatch(/tableComment: entry\.tableComment,\s*columnComments,/);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -794,6 +794,7 @@ fn supports_duplicate_table_comment(database_type: DatabaseType) -> bool {
|
|||
| DatabaseType::Gaussdb
|
||||
| DatabaseType::Kwdb
|
||||
| DatabaseType::OpenGauss
|
||||
| DatabaseType::Dameng
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -875,6 +876,11 @@ fn quote_sql_string(value: &str) -> String {
|
|||
}
|
||||
|
||||
fn quote_duplicate_table_comment(database_type: DatabaseType, value: &str) -> String {
|
||||
// Dameng rejects Postgres E'...' escape strings; keep standard '' escaping
|
||||
// (same as DamengAgent COMMENT ON / COMMENT ON COLUMN generation).
|
||||
if database_type == DatabaseType::Dameng {
|
||||
return quote_sql_string(value);
|
||||
}
|
||||
if !value.contains('\\') && !value.chars().any(|character| character.is_ascii_control()) {
|
||||
return quote_sql_string(value);
|
||||
}
|
||||
|
|
@ -1624,7 +1630,7 @@ mod tests {
|
|||
schema: Some("APP".to_string()),
|
||||
source_name: "USERS".to_string(),
|
||||
target_name: "users_copy".to_string(),
|
||||
table_comment: None,
|
||||
table_comment: Some("测试'克隆".to_string()),
|
||||
column_comments: vec![
|
||||
DuplicateTableColumnComment {
|
||||
name: "DISPLAY\"NAME".to_string(),
|
||||
|
|
@ -1636,16 +1642,31 @@ mod tests {
|
|||
});
|
||||
assert_eq!(
|
||||
dameng_sql,
|
||||
"CREATE TABLE \"APP\".USERS_COPY AS SELECT * FROM \"APP\".\"USERS\" WHERE 1=0;\nCOMMENT ON COLUMN \"APP\".USERS_COPY.\"DISPLAY\"\"NAME\" IS ' Owner''s; display name';\nCOMMENT ON COLUMN \"APP\".USERS_COPY.\"STATUS\" IS 'active ';"
|
||||
"CREATE TABLE \"APP\".USERS_COPY AS SELECT * FROM \"APP\".\"USERS\" WHERE 1=0;\nCOMMENT ON TABLE \"APP\".USERS_COPY IS '测试''克隆';\nCOMMENT ON COLUMN \"APP\".USERS_COPY.\"DISPLAY\"\"NAME\" IS ' Owner''s; display name';\nCOMMENT ON COLUMN \"APP\".USERS_COPY.\"STATUS\" IS 'active ';"
|
||||
);
|
||||
assert_eq!(
|
||||
crate::sql::split_sql_statements_for_database(&dameng_sql, DatabaseType::Dameng),
|
||||
vec![
|
||||
"CREATE TABLE \"APP\".USERS_COPY AS SELECT * FROM \"APP\".\"USERS\" WHERE 1=0".to_string(),
|
||||
"COMMENT ON TABLE \"APP\".USERS_COPY IS '测试''克隆'".to_string(),
|
||||
"COMMENT ON COLUMN \"APP\".USERS_COPY.\"DISPLAY\"\"NAME\" IS ' Owner''s; display name'".to_string(),
|
||||
"COMMENT ON COLUMN \"APP\".USERS_COPY.\"STATUS\" IS 'active '".to_string(),
|
||||
]
|
||||
);
|
||||
// Control chars / backslashes must not switch Dameng to Postgres E'...' literals.
|
||||
let dameng_escape_sql = build_duplicate_table_structure_sql(DuplicateTableStructureSqlOptions {
|
||||
database_type: Some(DatabaseType::Dameng),
|
||||
schema: Some("APP".to_string()),
|
||||
source_name: "USERS".to_string(),
|
||||
target_name: "users_copy".to_string(),
|
||||
table_comment: Some("line1\\path\nline2".to_string()),
|
||||
column_comments: vec![],
|
||||
});
|
||||
assert_eq!(
|
||||
dameng_escape_sql,
|
||||
"CREATE TABLE \"APP\".USERS_COPY AS SELECT * FROM \"APP\".\"USERS\" WHERE 1=0;\nCOMMENT ON TABLE \"APP\".USERS_COPY IS 'line1\\path\nline2';"
|
||||
);
|
||||
assert!(!dameng_escape_sql.contains("E'"));
|
||||
assert_eq!(
|
||||
build_duplicate_table_structure_sql(DuplicateTableStructureSqlOptions {
|
||||
database_type: Some(DatabaseType::Dameng),
|
||||
|
|
|
|||
|
|
@ -1622,6 +1622,15 @@
|
|||
"mergedPullRequests": 1,
|
||||
"firstContributionAt": "2026-06-23T05:56:48Z",
|
||||
"latestContributionAt": "2026-06-23T17:44:46Z"
|
||||
},
|
||||
{
|
||||
"login": "TangTangH",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/31877158?v=4",
|
||||
"profileUrl": "https://github.com/TangTangH",
|
||||
"commits": 0,
|
||||
"mergedPullRequests": 1,
|
||||
"firstContributionAt": "2026-08-04T04:55:17Z",
|
||||
"latestContributionAt": "2026-08-04T18:59:34Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue