fix(dameng): preserve cloned column comments

This commit is contained in:
zipg authored and GitHub committed 2026-07-27 14:53:13 +08:00
1 parent d5e0cde7e2
commit 0b4b1f956f
4 files changed
+146 -31

No files matched your search

@@ -60,7 +60,18 @@ import { supportsSchemaDiagram, supportsTableImport, supportsTableStructureEditi
import { codeMirrorSqlDialect, connectionObjectTreeNodeSchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import { getTableMetadataCapabilities, type TableMetadataCapabilities } from "@/lib/table/tableMetadataCapabilities";
import { buildTableSelectSql } from "@/lib/table/tableSelectSql";
import { buildDropObjectSql, buildDropTableSql, buildDuplicateTableStructureSql, buildCopyTableDataSql, buildEmptyTableSql, buildTruncateTableSql, supportsDropTableCascade, supportsTruncateTableCascade, type TableAdminSqlOptions } from "@/lib/database/dbAdminSql";
import {
buildDropObjectSql,
buildDropTableSql,
buildDuplicateTableStructureSql,
buildCopyTableDataSql,
buildEmptyTableSql,
buildTruncateTableSql,
collectDuplicateTableColumnComments,
supportsDropTableCascade,
supportsTruncateTableCascade,
type TableAdminSqlOptions,
} from "@/lib/database/dbAdminSql";
import { useToast } from "@/composables/useToast";
import { buildExecutableObjectSourceStatements, buildRoutineRenameObjectSourceStatements, executeObjectSourceSave, formatObjectSourceSaveError, supportsSourceBackedRoutineRename } from "@/lib/table/objectSourceEditor";
import { buildRenameObjectSql, supportsObjectRename } from "@/lib/table/objectRenameSql";
@@ -1809,6 +1820,30 @@ function requestDuplicateStructure(row: ObjectBrowserRow) {
showDuplicateDialog.value = true;
}
async function buildDuplicateStructurePlan(sourceName: string, targetName: string, schema: string | undefined, sourceColumns?: ColumnInfo[]) {
let columns = sourceColumns;
if (effectiveDatabaseType.value === "dameng" && !columns) {
try {
columns = await api.getColumns(props.connection.id, props.database, schema || "", sourceName, props.catalog);
} catch (error) {
console.warn(`Failed to load Dameng column comments for table clone: ${sourceName}`, error);
}
}
const columnComments = effectiveDatabaseType.value === "dameng" ? collectDuplicateTableColumnComments(columns ?? []) : [];
const sql = await buildDuplicateTableStructureSql({
databaseType: effectiveDatabaseType.value,
schema,
sourceName,
targetName,
columnComments,
});
return { sql, sourceColumns: columns, executeAsScript: columnComments.length > 0 };
}
function executeDuplicateStructurePlan(plan: { sql: string; executeAsScript: boolean }, schema: string | undefined) {
return plan.executeAsScript ? api.executeScript(props.connection.id, props.database, plan.sql, schema) : api.executeQuery(props.connection.id, props.database, plan.sql, schema);
}
async function confirmDuplicateStructure() {
const row = duplicateTarget.value;
const newName = duplicateTableName.value.trim();
@@ -1816,13 +1851,8 @@ async function confirmDuplicateStructure() {
showDuplicateDialog.value = false;
try {
const schema = row.schema || selectedSchema.value;
const sql = await buildDuplicateTableStructureSql({
databaseType: effectiveDatabaseType.value,
schema,
sourceName: row.name,
targetName: newName,
});
const executed = await executeObjectBrowserSqlWithProductionGuard(sql, () => api.executeQuery(props.connection.id, props.database, sql, schema));
const plan = await buildDuplicateStructurePlan(row.name, newName, schema);
const executed = await executeObjectBrowserSqlWithProductionGuard(plan.sql, () => executeDuplicateStructurePlan(plan, schema));
if (!executed) return;
toast(t("contextMenu.duplicateStructureSuccess", { name: newName }));
await reload();
@@ -1919,18 +1949,15 @@ async function confirmPasteTable() {
const targetName = entry.targetName.trim();
const schema = entry.schema || selectedSchema.value;
try {
let sourceColumns: ColumnInfo[] | undefined;
if (mode === "structure-and-data" || mode === "structure-only") {
const structureSql = await buildDuplicateTableStructureSql({
databaseType: effectiveDatabaseType.value,
schema,
sourceName: entry.sourceName,
targetName,
});
const executed = await executeObjectBrowserSqlWithProductionGuard(structureSql, () => api.executeQuery(props.connection.id, props.database, structureSql, schema));
const plan = await buildDuplicateStructurePlan(entry.sourceName, targetName, schema, sourceColumns);
sourceColumns = plan.sourceColumns;
const executed = await executeObjectBrowserSqlWithProductionGuard(plan.sql, () => executeDuplicateStructurePlan(plan, schema));
if (!executed) return;
}
if (copyData) {
const sourceColumns = await api.getColumns(props.connection.id, props.database, schema || "", entry.sourceName, props.catalog);
sourceColumns ??= await api.getColumns(props.connection.id, props.database, schema || "", entry.sourceName, props.catalog);
const dataCopyColumnOptions = tableDataCopyColumnOptions(effectiveDatabaseType.value, sourceColumns);
if (dataCopyColumnOptions.columns.length === 0) {
throw new Error("No writable columns available for table data copy.");
@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
import { collectDuplicateTableColumnComments } from "@/lib/database/dbAdminSql";
describe("collectDuplicateTableColumnComments", () => {
it("preserves meaningful whitespace and excludes whitespace-only comments", () => {
expect(
collectDuplicateTableColumnComments([
{ name: "LEADING", comment: " leading" },
{ name: "TRAILING", comment: "trailing " },
{ name: "BOTH", comment: " Owner's; display name " },
{ name: "WHITESPACE_ONLY", comment: " \t\n" },
{ name: "EMPTY", comment: "" },
{ name: "NULL", comment: null },
]),
).toEqual([
{ name: "LEADING", comment: " leading" },
{ name: "TRAILING", comment: "trailing " },
{ name: "BOTH", comment: " Owner's; display name " },
]);
});
});
+9 -1
View File
@@ -1,4 +1,4 @@
import type { DatabaseObjectType, DatabaseType } from "@/types/database";
import type { ColumnInfo, DatabaseObjectType, DatabaseType } from "@/types/database";
import * as api from "@/lib/backend/api";
export interface DropObjectSqlOptions {
@@ -55,6 +55,14 @@ export interface DuplicateTableStructureSqlOptions {
schema?: string | null;
sourceName: string;
targetName: string;
columnComments?: Array<{ name: string; comment: string }>;
}
export function collectDuplicateTableColumnComments(columns: readonly Pick<ColumnInfo, "name" | "comment">[]): Array<{ name: string; comment: string }> {
return columns.flatMap((column) => {
const comment = column.comment;
return comment?.trim() ? [{ name: column.name, comment }] : [];
});
}
export interface CopyTableDataSqlOptions {