feat(sql-file-execution): 外部SQL执行切换工作台并优化大文件链路

- 前端将运行外部SQL改为工作台Tab并复用导出式进度视图

- 后端新增仅选择文件元数据接口并优化流式读取与批量拼接

- 补充执行Runner、国际化文案和定向测试
This commit is contained in:
Syngnat
2026-07-03 22:11:49 +08:00
parent 3bf42a169c
commit b388b08226
17 changed files with 1386 additions and 138 deletions

View File

@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest';
import {
buildSQLFileExecutionWorkbenchTab,
resolveSQLFileExecutionWorkbenchTabId,
} from './sqlFileExecutionTab';
describe('sqlFileExecutionTab', () => {
it('builds stable workbench ids by connection, database, and normalized file path', () => {
expect(resolveSQLFileExecutionWorkbenchTabId('conn-1', 'demo', 'D:\\sql\\seed.sql')).toBe(
'sql-file-execution-conn-1-demo-D:/sql/seed.sql',
);
});
it('builds sql file execution workbench tabs with execution metadata', () => {
const tab = buildSQLFileExecutionWorkbenchTab({
connectionId: 'conn-1',
dbName: 'demo',
filePath: 'D:\\sql\\seed.sql',
fileName: 'seed.sql',
fileSizeMB: '512.5',
requestKey: 'job-1',
});
expect(tab).toEqual(expect.objectContaining({
id: 'sql-file-execution-conn-1-demo-D:/sql/seed.sql',
title: 'seed.sql',
type: 'sql-file-execution',
connectionId: 'conn-1',
dbName: 'demo',
filePath: 'D:/sql/seed.sql',
sqlFileExecutionFileSizeMB: '512.5',
sqlFileExecutionRequestKey: 'job-1',
}));
});
});

View File

@@ -0,0 +1,46 @@
import type { TabData } from '../types';
import { t } from '../i18n';
const normalizePathToken = (value: string): string =>
value.replace(/\\/g, '/').trim();
export const resolveSQLFileExecutionWorkbenchTabId = (
connectionId: string,
dbName: string | undefined,
filePath: string,
): string => {
const normalizedConnectionId = String(connectionId || '').trim() || 'none';
const normalizedDbName = String(dbName || '').trim() || 'default';
const normalizedFilePath = normalizePathToken(String(filePath || '')) || 'file';
return `sql-file-execution-${normalizedConnectionId}-${normalizedDbName}-${normalizedFilePath}`;
};
type BuildSQLFileExecutionWorkbenchTabInput = {
connectionId: string;
dbName?: string;
filePath: string;
fileName?: string;
fileSizeMB?: string;
requestKey?: string;
};
export const buildSQLFileExecutionWorkbenchTab = (
input: BuildSQLFileExecutionWorkbenchTabInput,
): TabData => {
const connectionId = String(input.connectionId || '').trim();
const dbName = String(input.dbName || '').trim();
const filePath = normalizePathToken(String(input.filePath || ''));
const fileName = String(input.fileName || '').trim();
const defaultTitle = fileName || t('sidebar.sql_file_exec.title');
return {
id: resolveSQLFileExecutionWorkbenchTabId(connectionId, dbName || undefined, filePath),
title: defaultTitle,
type: 'sql-file-execution',
connectionId,
...(dbName ? { dbName } : {}),
filePath,
sqlFileExecutionFileSizeMB: String(input.fileSizeMB || '').trim() || undefined,
sqlFileExecutionRequestKey: String(input.requestKey || `sql-file-execution-${Date.now()}`).trim(),
};
};