feat(tabs): show query execution status across tab surfaces

Closes #4986
This commit is contained in:
t8y2 committed 2026-08-25 01:34:00 +00:00
1 parent 277386ec81
commit a2f8824e6a
8 files changed
+163 -5

No files matched your search

@@ -259,6 +259,40 @@ describe("useSqlExecution", () => {
expect(activeOutputView.value).toBe("summary");
});
it("does not change the current output view when a background tab finishes", async () => {
const sql = "SELECT run_side_effect()";
const executionTab = { ...queryTab("app"), sql };
const activeTab = ref<QueryTab | undefined>(executionTab);
const activeConnection = ref<ConnectionConfig | undefined>(connection("mysql"));
const activeOutputView = ref<"result" | "summary" | "explain" | "chart" | "messages">("result");
const queryStore = useQueryStore();
let finishExecution!: () => void;
const executionFinished = new Promise<void>((resolve) => {
finishExecution = resolve;
});
const executeCurrentSql = vi.spyOn(queryStore, "executeCurrentSql").mockImplementation(async () => {
await executionFinished;
executionTab.result = { columns: [], rows: [], affected_rows: 1, execution_time_ms: 1 };
});
vi.spyOn(useHistoryStore(), "add").mockResolvedValue(undefined);
const execution = useSqlExecution({
activeTab: computed(() => activeTab.value),
activeConnection: computed(() => activeConnection.value),
executableSql: computed(() => sql),
activeOutputView,
});
const pending = execution.tryExecute();
await vi.waitFor(() => expect(executeCurrentSql).toHaveBeenCalledOnce());
activeTab.value = { ...queryTab("app"), id: "tab-2" };
activeOutputView.value = "messages";
finishExecution();
await pending;
expect(activeOutputView.value).toBe("messages");
});
it("shows SQL Server PRINT messages instead of leaving them behind the execution summary", async () => {
const sql = `IF 1 = 1
BEGIN
@@ -302,14 +302,15 @@ export function useSqlExecution(deps: {
...(options.editorViewportRequestId !== undefined ? { onExecutionStarted: () => deps.onExecutionStarted?.(options.editorViewportRequestId!) } : {}),
});
if (producedResult === false) return;
const executionTabStillActive = deps.activeTab.value?.id === tab.id;
const sqlServerMessageResultIndex = executionDatabaseType === "sqlserver" ? tab.results?.findIndex((result) => result.server_message === true) : undefined;
if (sqlServerMessageResultIndex !== undefined && sqlServerMessageResultIndex >= 0) {
focusSqlServerDataResult(tab.id, executionDatabaseType, tab);
deps.activeOutputView.value = "result";
if (executionTabStillActive) deps.activeOutputView.value = "result";
} else if (executionDatabaseType === "sqlserver" && tab.result?.server_message === true) {
deps.activeOutputView.value = "result";
if (executionTabStillActive) deps.activeOutputView.value = "result";
} else if (tab.result && !tab.result.columns.length && !tab.results?.some((result) => result.columns.length > 0)) {
deps.activeOutputView.value = statementCount === 1 ? defaultViewForResult(tab.result) : "summary";
if (executionTabStillActive) deps.activeOutputView.value = statementCount === 1 ? defaultViewForResult(tab.result) : "summary";
}
const elapsed = Date.now() - start;
const failure = firstQueryExecutionError(tab);
@@ -481,7 +482,7 @@ export function useSqlExecution(deps: {
// 会让多个 worker 几乎同时改这个共享 ref,导致主视图在 result/summary 间反复
// 跳动(闪烁/竞态)。worker 结果已由 captureMultiDbExecutionWorkerResult 记录
// 到 source tab 的 result run 并通过 projectResultRun 投影显示,无需再切主视图。
if (!workerId) {
if (!workerId && deps.activeTab.value?.id === tab.id) {
deps.activeOutputView.value = success && (latest.result?.columns.length || latest.results?.some((result) => result.columns.length)) ? "result" : "summary";
}
return finish(success ? { status: "success", errorMessage } : { status: "failed", errorMessage });