feat(export): support safe background query exports

This commit is contained in:
zhangyaxi authored and GitHub committed 2026-07-29 03:18:06 +08:00
1 parent 2f44d36018
commit a07def6042
17 files changed
+577 -195

No files matched your search

+55 -1
View File
@@ -58,6 +58,7 @@ import * as api from "@/lib/backend/api";
import { useConnectionStore } from "@/stores/connectionStore";
import { useSettingsStore } from "@/stores/settingsStore";
import { useSavedSqlStore } from "@/stores/savedSqlStore";
import { useExportTracker } from "@/composables/useExportTracker";
import { recordQueryCancellationLatency, resourceLifecycleDiagnostics } from "@/lib/diagnostics/resourceLifecycleDiagnostics";
import { appendDebugLog } from "@/lib/backend/debugLog";
import { formatError } from "@/lib/backend/errorUtils";
@@ -4770,7 +4771,7 @@ export const useQueryStore = defineStore("query", () => {
const setupSql = batchStatements[resultStatementIndex!]?.sql === tab.result.sourceStatement ? batchStatements.slice(0, resultStatementIndex).map((statement) => statement.sql) : undefined;
const rowLimit = settings.exportRowLimitEnabled ? settings.exportRowLimit : null;
const totalRows = typeof tab.resultTotalRowCount === "number" ? (rowLimit === null ? tab.resultTotalRowCount : Math.min(tab.resultTotalRowCount, rowLimit)) : null;
const clientSessionId = tabClientSessionId(tab, "export");
const clientSessionId = `${tabClientSessionId(tab, "export")}:${options.exportId}`;
return {
exportId: options.exportId,
@@ -4798,6 +4799,58 @@ export const useQueryStore = defineStore("query", () => {
};
}
async function exportQuerySqlDirect(id: string, sql: string, format: "csv" | "xlsx" | "txt", filePath: string) {
const tab = tabs.value.find((item) => item.id === id);
if (!tab || tab.mode !== "query" || !sql.trim()) return;
const connStore = useConnectionStore();
await connStore.ensureConnected(tab.connectionId);
const conn = connStore.getConfig(tab.connectionId);
const settings = useSettingsStore().editorSettings;
const effectiveDbType = effectiveDatabaseTypeForConnection(conn);
if (!effectiveDbType) return;
const exportId = uuid();
const request: api.QueryResultExportRequest = {
exportId,
connectionId: tab.connectionId,
database: tab.database,
schema: tab.schema,
sql,
queryBaseSql: sql,
databaseType: effectiveDbType,
useAgentCursor: usesAgentCursorForQuery(conn?.db_type),
filePath,
format,
pageSize: settings.exportBatchSize,
rowLimit: settings.exportRowLimitEnabled ? settings.exportRowLimit : null,
totalRows: null,
timeoutSecs: queryTimeoutSecsForConnection(conn),
keysetOptimizationEnabled: settings.queryExportKeysetOptimizationEnabled,
clientSessionId: `${tabClientSessionId(tab, "export")}:${exportId}`,
executionId: uuid(),
numericColumnRightAlign: settings.numericColumnRightAlign,
};
const tracker = useExportTracker();
tracker.addTask("Query Result", format, filePath, request.exportId);
tracker.registerTaskCancelHandler(request.exportId, () => api.cancelQueryResultExport(request.exportId, request.executionId));
void (async () => {
try {
await api.startQueryResultExport(request, (progress) => tracker.updateTableExportTask(request.exportId, progress));
} catch (error: any) {
const task = tracker.tasks.value.find((item) => item.exportId === request.exportId);
if (task) {
task.status = "Error";
task.errorMessage = error?.message || String(error);
}
} finally {
tracker.unregisterTaskCancelHandler(request.exportId);
}
})();
}
return {
tabs,
activeTabId,
@@ -4900,6 +4953,7 @@ export const useQueryStore = defineStore("query", () => {
importResultArchive,
fetchTabResultForExport,
buildQueryResultExportRequest,
exportQuerySqlDirect,
getResourceLifecycleDiagnostics: () => resourceLifecycleDiagnostics(tabs.value),
notifyConnectionMayBeLost,
};