fix(export): append semicolons to exported DDL statements

This commit is contained in:
onenewcode 2026-06-30 18:00:44 +08:00 committed by GitHub
parent ba71068bc8
commit aae2cae068
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 47 additions and 2 deletions

View File

@ -60,6 +60,7 @@ import { isTauriRuntime } from "@/lib/tauriRuntime";
import { generateDatabaseExportId } from "@/lib/databaseExport";
import { copyToClipboard } from "@/lib/clipboard";
import { formatSqlInsert } from "@/lib/exportFormats";
import { buildSingleDdlExportFileContent } from "@/lib/ddlExport";
import { fetchTableDataForExport } from "@/lib/tableDataExport";
import { useConnectionStore } from "@/stores/connectionStore";
import { useExportTracker, type ExportTask } from "@/composables/useExportTracker";
@ -799,7 +800,7 @@ async function exportStructure(row: ObjectBrowserRow) {
try {
const schema = row.schema || selectedSchema.value || props.database;
const ddl = await api.getTableDdl(props.connection.id, props.database, schema, row.name, tableDdlObjectType(row.type));
await saveFileContent(ddl + "\n", `${row.name}.sql`, "SQL", "sql");
await saveFileContent(buildSingleDdlExportFileContent(ddl), `${row.name}.sql`, "SQL", "sql");
} catch (e: any) {
console.error("Export structure failed:", e);
}

View File

@ -77,6 +77,7 @@ import { editableRowIdentifierColumns, usesSyntheticRowIdKey } from "@/lib/table
import { supportsDatabaseCreation, supportsDatabaseSearch, supportsFieldLineage, supportsObjectBrowserTreeNode, supportsSchemaDiagram, supportsSqlFileExecution, supportsTableImport, supportsTableTruncate, supportsTableStructureEditing, usesTreeSchemaMode } from "@/lib/databaseCapabilities";
import { copyNameForTreeNode, objectSourceKindForTreeNode, sidebarSelectionCopyAction, treeNodeRowAction, treeNodeRowDoubleClickAction } from "@/lib/treeNodeClick";
import { formatSqlInsert } from "@/lib/exportFormats";
import { joinExportedDdls } from "@/lib/ddlExport";
import { fetchTableDataForExport } from "@/lib/tableDataExport";
import { buildCreateDatabaseSql, buildDuckDbAttachDatabaseSql, duckDbAttachedDatabaseNameFromPath, supportsCreateDatabaseCharset, uniqueDuckDbAttachedDatabaseName } from "@/lib/createDatabaseSql";
import {
@ -2592,7 +2593,7 @@ async function exportStructure() {
const ddl = await api.getTableDdl(target.connectionId, target.database, target.schema || target.database, target.label, tableDdlObjectTypeForNode(target.type));
parts.push(ddl.trim());
}
structurePreviewSql.value = `${parts.filter(Boolean).join("\n\n")}\n`;
structurePreviewSql.value = joinExportedDdls(parts);
} catch (e: any) {
structurePreviewError.value = e?.message || String(e);
console.error("Export structure failed:", e);

View File

@ -0,0 +1,15 @@
export function ensureSqlStatementTerminator(sql: string): string {
const trimmed = sql.trim();
if (!trimmed) return "";
return trimmed.endsWith(";") ? trimmed : `${trimmed};`;
}
export function buildSingleDdlExportFileContent(sql: string): string {
const statement = ensureSqlStatementTerminator(sql);
return statement ? `${statement}\n` : "";
}
export function joinExportedDdls(ddls: readonly string[]): string {
const statements = ddls.map(ensureSqlStatementTerminator).filter(Boolean);
return statements.length ? `${statements.join("\n\n")}\n` : "";
}

View File

@ -0,0 +1,28 @@
import { strict as assert } from "node:assert";
import { test } from "vitest";
import { buildSingleDdlExportFileContent, ensureSqlStatementTerminator, joinExportedDdls } from "../../apps/desktop/src/lib/ddlExport.ts";
test("ensureSqlStatementTerminator appends a trailing semicolon when missing", () => {
assert.equal(ensureSqlStatementTerminator("CREATE TABLE `users` (\n `id` int\n) ENGINE=InnoDB"), "CREATE TABLE `users` (\n `id` int\n) ENGINE=InnoDB;");
});
test("ensureSqlStatementTerminator does not duplicate an existing trailing semicolon", () => {
assert.equal(ensureSqlStatementTerminator("CREATE VIEW v AS SELECT 1;"), "CREATE VIEW v AS SELECT 1;");
});
test("joinExportedDdls separates exported objects with blank lines and terminates each statement", () => {
assert.equal(
joinExportedDdls([
"CREATE TABLE `users` (`id` int)",
"CREATE TABLE `posts` (`id` int);",
]),
"CREATE TABLE `users` (`id` int);\n\nCREATE TABLE `posts` (`id` int);\n",
);
});
test("buildSingleDdlExportFileContent emits a single importable statement with one trailing newline", () => {
assert.equal(
buildSingleDdlExportFileContent(" CREATE TABLE `users` (`id` int) "),
"CREATE TABLE `users` (`id` int);\n",
);
});