feat(export): honor exclude-primary-keys in SQL script exports

This commit is contained in:
高桥凉介 authored and GitHub committed 2026-09-17 14:13:36 +08:00
1 parent 137a1a4161
commit d17ce46626
16 files changed
+464 -68

No files matched your search

+112
View File
@@ -4,6 +4,10 @@ import { beforeEach, test, vi } from "vitest";
import { createPinia, setActivePinia } from "pinia";
import { useSettingsStore } from "../../apps/desktop/src/stores/settingsStore.ts";
import type { DataGridTableMeta } from "../../apps/desktop/src/lib/dataGrid/dataGridSql.ts";
import {
DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS,
type DataGridExtractorOptions,
} from "../../apps/desktop/src/lib/dataGrid/dataGridCopyExtractor.ts";
import type { DatabaseType, QueryResult } from "../../apps/desktop/src/types/database.ts";
const apiMock = vi.hoisted(() => ({
@@ -109,6 +113,7 @@ function buildExportHarness(
sourceColumns?: Array<string | undefined>;
databaseType?: DatabaseType;
context?: "results" | "table-data";
extractorOptions?: DataGridExtractorOptions;
} = {},
) {
const exportColumns = options.columns ?? ["id", "name"];
@@ -166,6 +171,7 @@ function buildExportHarness(
connectionId: computed(() => "conn-1"),
database: computed(() => "db"),
context: computed(() => options.context ?? "results"),
extractorOptions: computed(() => options.extractorOptions),
sourceColumns: computed(() => options.sourceColumns),
columnTypes: computed(() => options.columnTypes),
allColumnTypes: computed(() => options.allColumnTypes),
@@ -634,6 +640,112 @@ test("table data SQL export keeps source column names", async () => {
}
});
function sqlExclusionOptions(): DataGridExtractorOptions {
return {
...DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS,
sql: { ...DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS.sql, excludePrimaryKeysFromInsert: true },
};
}
test("table data SQL export passes primary key exclusion to the backend", async () => {
runtimeMock.isTauri = true;
dialogMock.save.mockResolvedValue("/tmp/users.sql");
const { composable } = buildExportHarness({
context: "table-data",
tableMeta: {
tableName: "users",
primaryKeys: ["id"],
columns: [
{ name: "id", data_type: "integer", is_nullable: false, is_primary_key: true, extra: "auto_increment" },
{ name: "name", data_type: "text", is_nullable: true },
],
},
extractorOptions: sqlExclusionOptions(),
});
await composable.exportSql();
assert.equal(apiMock.startTableExport.mock.calls[0][0].excludePrimaryKeys, true);
});
test("query result SQL export passes primary key exclusion and keys to the backend", async () => {
runtimeMock.isTauri = true;
dialogMock.save.mockResolvedValue("/tmp/query-result.sql");
const { composable } = buildExportHarness({
context: "results",
tableMeta: {
tableName: "users",
primaryKeys: ["id"],
columns: [
{ name: "id", data_type: "integer", is_nullable: false, is_primary_key: true, extra: "auto_increment" },
{ name: "name", data_type: "text", is_nullable: true },
],
},
extractorOptions: sqlExclusionOptions(),
});
await composable.exportSql();
assert.equal(apiMock.startQueryResultExport.mock.calls[0][0].excludePrimaryKeys, true);
assert.deepEqual(apiMock.startQueryResultExport.mock.calls[0][0].primaryKeys, ["id"]);
});
test("table data SQL export keeps manually-assigned primary keys", async () => {
runtimeMock.isTauri = true;
dialogMock.save.mockResolvedValue("/tmp/users-manual-pk.sql");
const { composable } = buildExportHarness({
context: "table-data",
tableMeta: {
tableName: "users",
primaryKeys: ["user_code"],
columns: [{ name: "user_code", data_type: "varchar", is_nullable: false, is_primary_key: true }],
},
extractorOptions: sqlExclusionOptions(),
});
await composable.exportSql();
assert.equal(apiMock.startTableExport.mock.calls[0][0].excludePrimaryKeys, undefined);
});
test("local SQL export asks the backend to drop primary key columns", async () => {
const download = installTextDownloadCapture();
const completeLocalResult: QueryResult = {
columns: ["id", "name"],
column_types: ["int4", "text"],
rows: [[1, "Ada"]],
affected_rows: 0,
execution_time_ms: 1,
truncated: false,
has_more: false,
};
apiMock.buildExportSqlInsert.mockResolvedValueOnce("INSERT INTO \"users\" (\"name\") VALUES ('Ada');");
try {
const { composable } = buildExportHarness({
columns: completeLocalResult.columns,
rows: completeLocalResult.rows,
completeLocalResult,
tableMeta: {
tableName: "users",
primaryKeys: ["id"],
columns: [
{ name: "id", data_type: "int4", is_nullable: false, is_primary_key: true, extra: "auto_increment" },
{ name: "name", data_type: "text", is_nullable: true },
],
},
extractorOptions: sqlExclusionOptions(),
});
await composable.exportSql();
assert.deepEqual(apiMock.buildExportSqlInsert.mock.calls[0][0].excludeColumns, ["id"]);
assert.deepEqual(apiMock.buildExportSqlInsert.mock.calls[0][0].columns, ["id", "name"]);
} finally {
download.restore();
}
});
test("complete local query result export removes only internal hidden columns", async () => {
const completeLocalResult: QueryResult = {
columns: ["id", "name", "__DBX_PK_id"],