feat(query): 优化多条查询语句结果展示与语句匹配问题 (#2697)

This commit is contained in:
二丫讲梵 2026-07-06 18:39:36 +08:00 committed by GitHub
parent 095cb867a5
commit ff8e53d47b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 172 additions and 44 deletions

View File

@ -54,7 +54,7 @@ import { useConnectionStore } from "@/stores/connectionStore";
import { TABLE_FONT_SIZE_MAX, TABLE_FONT_SIZE_MIN, useSettingsStore, type DataGridSearchMode } from "@/stores/settingsStore";
import { useToast } from "@/composables/useToast";
import { canCancelQueryExecution, queryExecutionLabelKey } from "@/lib/sql/queryExecutionState";
import { databaseDisplayNameForTab, executionSummaryItems, nextExecutionSummaryView, resultGridCacheKey, resultRunItems, tabularResultItems } from "@/lib/tabs/tabPresentation";
import { databaseDisplayNameForTab, executionSummaryItems, nextExecutionSummaryView, resultGridCacheKey, resultRunItems, resultSqlForGrid, tabularResultItems } from "@/lib/tabs/tabPresentation";
import { defaultQueryResultArchiveFileName } from "@/lib/query/queryResultArchive";
import { saveQueryResultArchiveFile } from "@/lib/query/queryResultArchiveFile";
import { isTableDataEditable } from "@/lib/table/tableEditing";
@ -295,6 +295,7 @@ const allResultExportSheets = computed(() =>
const resultRuns = computed(() => resultRunItems(props.activeTab));
const activeResultRunItem = computed(() => resultRuns.value.find((run) => run.active));
const activeResultGridCacheKey = computed(() => resultGridCacheKey(props.activeTab));
const activeResultSql = computed(() => resultSqlForGrid(props.activeTab));
const resultArchiveExporting = ref(false);
const canExportResultArchive = computed(() => props.activeTab.mode === "query" && (!!props.activeTab.result || !!props.activeTab.results?.length || !!props.activeTab.resultRuns?.length));
const resultAutoSave = computed(() => props.activeTab.resultAutoSave === true);
@ -775,7 +776,7 @@ defineExpose({ focusSearch, refreshData, handleModRTarget, requestQueryEditorExe
size="sm"
:variant="activeOutputView === 'result' && (activeTab.activeResultIndex ?? 0) === item.index ? 'default' : 'ghost'"
class="h-6 max-w-48 shrink-0 overflow-hidden text-ellipsis whitespace-nowrap px-2 text-xs"
:title="item.label || t('tabs.resultN', { n: item.n })"
:title="item.title || item.label || t('tabs.resultN', { n: item.n })"
@click="
queryStore.setActiveResultIndex(activeTab.id, item.index);
emit('update:activeOutputView', 'result');
@ -994,7 +995,7 @@ defineExpose({ focusSearch, refreshData, handleModRTarget, requestQueryEditorExe
:sort-direction="activeTab.resultSortDirection"
:sort-mode="activeTab.resultSortMode"
:initial-order-by-input="activeTab.orderByInput"
:sql="activeTab.lastExecutedSql || activeTab.sql"
:sql="activeResultSql"
:loading="activeTab.isExecuting"
:editable="!!activeTab.queryAnalysis || !!mongoQueryResultSaveHandler"
:source-columns="activeTab.querySourceColumns"

View File

@ -138,12 +138,24 @@ export function tabTooltipLines(tab: QueryTab, t: Translate): { label: string; v
return lines;
}
export function tabularResultItems(results: QueryResult[] | undefined): { result: QueryResult; index: number; n: number; label?: string }[] {
export function queryResultStatementLabel(result: Pick<QueryResult, "sourceLabel" | "sourceStatement">, maxLength = 48): string | undefined {
if (result.sourceLabel) return result.sourceLabel;
const statement = result.sourceStatement?.replace(/\s+/g, " ").trim();
if (!statement) return undefined;
if (statement.length <= maxLength) return statement;
return `${statement.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
}
export function resultSqlForGrid(tab: Pick<QueryTab, "result" | "resultBaseSql" | "lastExecutedSql" | "sql">): string {
return tab.result?.sourceStatement || tab.resultBaseSql || tab.lastExecutedSql || tab.sql;
}
export function tabularResultItems(results: QueryResult[] | undefined): { result: QueryResult; index: number; n: number; label?: string; title?: string }[] {
if (!results) return [];
return results
.map((result, index) => ({ result, index }))
.filter((item) => item.result.columns.length > 0)
.map((item, ordinal) => ({ ...item, n: ordinal + 1, label: item.result.sourceLabel }));
.map((item, ordinal) => ({ ...item, n: ordinal + 1, label: queryResultStatementLabel(item.result), title: item.result.sourceStatement }));
}
export function activeResultRun(tab: Pick<QueryTab, "resultRuns" | "activeResultRunId">) {

View File

@ -20,7 +20,7 @@ import {
mongoUseToQueryResult,
mongoVersionToQueryResult,
mongoWriteToQueryResult,
splitMongoCommands,
splitMongoCommandRanges,
type MongoAggregateSafetyOptions,
} from "@/lib/mongo/mongoShellCommand";
import { redisCommandResultToQueryResult } from "@/lib/redis/redisQueryResult";
@ -124,15 +124,19 @@ function annotateQueryResultSources(results: QueryResult[], sql: string, databas
let statementIndex = 0;
for (const result of results) {
const statement = statements[statementIndex++];
if (result.columns.length === 0) continue;
if (!statement) continue;
result.sourceStatement = statement.sql;
const label = queryResultSourceLabel(statement.sql, database);
if (label) result.sourceLabel = label;
annotateQueryResultSource(result, statement.sql, database);
}
return results;
}
function annotateQueryResultSource(result: QueryResult, sourceStatement: string, database?: string): QueryResult {
result.sourceStatement = sourceStatement;
const label = queryResultSourceLabel(sourceStatement, database);
if (label) result.sourceLabel = label;
return result;
}
async function withFrontendQueryTimeout<T>(promise: Promise<T>, timeoutSecs: number, message: string): Promise<T> {
if (timeoutSecs === 0) return promise;
@ -1994,7 +1998,7 @@ export const useQueryStore = defineStore("query", () => {
try {
const connStore = useConnectionStore();
let conn = connStore.getConfig(tab.connectionId);
const parsedMongoCommands = conn?.db_type === "mongodb" ? splitMongoCommands(sql) : undefined;
const parsedMongoCommands = conn?.db_type === "mongodb" ? splitMongoCommandRanges(sql) : undefined;
let mongoCommands = parsedMongoCommands ?? [];
const mongoNeedsConnection = mongoCommands.some(({ command }) => command.kind !== "use");
@ -2009,7 +2013,7 @@ export const useQueryStore = defineStore("query", () => {
}
conn = connStore.getConfig(tab.connectionId);
if (parsedMongoCommands === undefined && conn?.db_type === "mongodb") {
mongoCommands = splitMongoCommands(sql);
mongoCommands = splitMongoCommandRanges(sql);
}
const effectiveDbType = effectiveDatabaseTypeForConnection(conn);
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type);
@ -2036,7 +2040,7 @@ export const useQueryStore = defineStore("query", () => {
for (const command of commands) {
try {
const result = await api.redisExecuteCommand(tab.connectionId, currentDb, command, skipSafety);
allResults.push(markQueryResultRowsRaw(redisCommandResultToQueryResult(result.value, performance.now() - startedAt, result.command)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(redisCommandResultToQueryResult(result.value, performance.now() - startedAt, result.command), command)));
// Track db switches from SELECT N so later commands in the same batch run on the right db.
currentDb = nextRedisCommandDb(currentDb, command, result.value);
// Write commands (SET/DEL/...) mutate the key set — drop the cached key-name completion
@ -2046,7 +2050,7 @@ export const useQueryStore = defineStore("query", () => {
connStore.invalidateCompletionCache(tab.connectionId, String(currentDb));
}
} catch (e: any) {
allResults.push({ columns: ["Error"], rows: [[e?.message ?? String(e)]], affected_rows: 0, execution_time_ms: 0 });
allResults.push(annotateQueryResultSource({ columns: ["Error"], rows: [[e?.message ?? String(e)]], affected_rows: 0, execution_time_ms: 0 }, command));
}
}
console.info("[DBX][executeTabSql:redis:done]", { traceId, commandCount: commands.length, elapsed: elapsed() });
@ -2099,13 +2103,14 @@ export const useQueryStore = defineStore("query", () => {
for (const parsedCommand of mongoCommands) {
const mongoCommand = parsedCommand.command;
const sourceStatement = parsedCommand.text;
const commandStartedAt = performance.now();
try {
switch (mongoCommand.kind) {
case "find": {
console.info("[DBX][executeTabSql:mongo-find:start]", { traceId, collection: mongoCommand.collection, database: currentDatabase });
const result = await api.mongoFindDocuments(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.skip, mongoCommand.limit, mongoCommand.filter, mongoCommand.projection, mongoCommand.sort, executionId);
const queryResult = markQueryResultRowsRaw(mongoDocumentsToQueryResult(result.documents, performance.now() - commandStartedAt, result.total));
const queryResult = markQueryResultRowsRaw(annotateQueryResultSource(mongoDocumentsToQueryResult(result.documents, performance.now() - commandStartedAt, result.total), sourceStatement));
allResults.push(queryResult);
mongoEditTarget = mongoCommands.length === 1 && queryResult.columns.includes("_id") ? { collection: mongoCommand.collection, idColumn: "_id" } : undefined;
console.info("[DBX][executeTabSql:mongo-find:done]", {
@ -2121,7 +2126,7 @@ export const useQueryStore = defineStore("query", () => {
case "version": {
console.info("[DBX][executeTabSql:mongo-version:start]", { traceId, database: currentDatabase });
const version = await api.mongoServerVersion(tab.connectionId, currentDatabase, executionId);
allResults.push(markQueryResultRowsRaw(mongoVersionToQueryResult(version, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoVersionToQueryResult(version, performance.now() - commandStartedAt), sourceStatement)));
mongoEditTarget = undefined;
console.info("[DBX][executeTabSql:mongo-version:done]", {
traceId,
@ -2134,7 +2139,7 @@ export const useQueryStore = defineStore("query", () => {
case "countDocuments": {
console.info("[DBX][executeTabSql:mongo-count:start]", { traceId, collection: mongoCommand.collection, database: currentDatabase });
const result = await api.mongoFindDocuments(tab.connectionId, currentDatabase, mongoCommand.collection, 0, 1, mongoCommand.filter, undefined, undefined, executionId);
allResults.push(markQueryResultRowsRaw(mongoCountToQueryResult(result.total, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoCountToQueryResult(result.total, performance.now() - commandStartedAt), sourceStatement)));
mongoEditTarget = undefined;
console.info("[DBX][executeTabSql:mongo-count:done]", {
traceId,
@ -2153,7 +2158,7 @@ export const useQueryStore = defineStore("query", () => {
console.info("[DBX][executeTabSql:mongo-aggregate:start]", { traceId, collection: mongoCommand.collection, database: currentDatabase });
const aggregateMaxRows = normalizeResultPageSize(pageLimit ?? options?.pagination?.limit ?? settingsStore.editorSettings.pageSize);
const result = await api.mongoAggregateDocuments(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.pipeline, aggregateMaxRows, executionId);
allResults.push(markQueryResultRowsRaw(mongoDocumentsToQueryResult(result.documents, performance.now() - commandStartedAt, result.total)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoDocumentsToQueryResult(result.documents, performance.now() - commandStartedAt, result.total), sourceStatement)));
mongoEditTarget = undefined;
console.info("[DBX][executeTabSql:mongo-aggregate:done]", {
traceId,
@ -2168,7 +2173,7 @@ export const useQueryStore = defineStore("query", () => {
case "getIndexes": {
console.info("[DBX][executeTabSql:mongo-indexes:start]", { traceId, collection: mongoCommand.collection, database: currentDatabase });
const indexes = await api.listIndexes(tab.connectionId, currentDatabase, "", mongoCommand.collection);
allResults.push(markQueryResultRowsRaw(mongoIndexesToQueryResult(indexes, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoIndexesToQueryResult(indexes, performance.now() - commandStartedAt), sourceStatement)));
mongoEditTarget = undefined;
console.info("[DBX][executeTabSql:mongo-indexes:done]", {
traceId,
@ -2187,7 +2192,7 @@ export const useQueryStore = defineStore("query", () => {
database: currentDatabase,
});
const stats = await api.mongoCollectionStats(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.scale, executionId);
allResults.push(markQueryResultRowsRaw(mongoCollectionStatsToQueryResult(mongoCommand.metric, stats as unknown as Record<string, unknown>, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoCollectionStatsToQueryResult(mongoCommand.metric, stats as unknown as Record<string, unknown>, performance.now() - commandStartedAt), sourceStatement)));
mongoEditTarget = undefined;
console.info("[DBX][executeTabSql:mongo-collection-stats:done]", {
traceId,
@ -2217,19 +2222,19 @@ export const useQueryStore = defineStore("query", () => {
mongoEditTarget = undefined;
if (mongoCommand.kind === "insert") {
const result = await api.mongoInsertDocuments(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.docsJson);
allResults.push(markQueryResultRowsRaw(mongoWriteToQueryResult(result.affected_rows, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoWriteToQueryResult(result.affected_rows, performance.now() - commandStartedAt), sourceStatement)));
} else if (mongoCommand.kind === "update") {
const result = await api.mongoUpdateDocuments(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.filter, mongoCommand.update, mongoCommand.many);
allResults.push(markQueryResultRowsRaw(mongoWriteToQueryResult(result.affected_rows, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoWriteToQueryResult(result.affected_rows, performance.now() - commandStartedAt), sourceStatement)));
} else if (mongoCommand.kind === "createIndex") {
const result = await api.mongoCreateIndex(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.keys, mongoCommand.options);
allResults.push(markQueryResultRowsRaw(mongoCreateIndexToQueryResult(result.name, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoCreateIndexToQueryResult(result.name, performance.now() - commandStartedAt), sourceStatement)));
} else if (mongoCommand.kind === "dropIndex" || mongoCommand.kind === "dropIndexes") {
const result = await api.mongoDropIndexes(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.kind === "dropIndex" ? mongoCommand.index : mongoCommand.indexes, mongoCommand.kind === "dropIndex");
allResults.push(markQueryResultRowsRaw(mongoDroppedIndexesToQueryResult(result.dropped_names, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoDroppedIndexesToQueryResult(result.dropped_names, performance.now() - commandStartedAt), sourceStatement)));
} else {
const result = await api.mongoDeleteDocuments(tab.connectionId, currentDatabase, mongoCommand.collection, mongoCommand.filter, mongoCommand.many);
allResults.push(markQueryResultRowsRaw(mongoWriteToQueryResult(result.affected_rows, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoWriteToQueryResult(result.affected_rows, performance.now() - commandStartedAt), sourceStatement)));
}
console.info("[DBX][executeTabSql:mongo-write:done]", {
traceId,
@ -2242,7 +2247,7 @@ export const useQueryStore = defineStore("query", () => {
}
case "use": {
currentDatabase = mongoCommand.database;
allResults.push(markQueryResultRowsRaw(mongoUseToQueryResult(currentDatabase, performance.now() - commandStartedAt)));
allResults.push(markQueryResultRowsRaw(annotateQueryResultSource(mongoUseToQueryResult(currentDatabase, performance.now() - commandStartedAt), sourceStatement)));
mongoEditTarget = undefined;
console.info("[DBX][executeTabSql:mongo-use:done]", {
traceId,
@ -2255,7 +2260,7 @@ export const useQueryStore = defineStore("query", () => {
} catch (error: any) {
// Surface per-command failures inline and continue collecting results
// for the rest of the batch, matching the grouped-result UX.
allResults.push(toErrorResult(error));
allResults.push(annotateQueryResultSource(toErrorResult(error), sourceStatement));
mongoEditTarget = undefined;
}
}

View File

@ -3,6 +3,7 @@ import { test } from "vitest";
import { createPinia, setActivePinia } from "pinia";
import { isReactive } from "vue";
import { decodeQueryResultArchive } from "../../apps/desktop/src/lib/query/queryResultArchive.ts";
import { resultSqlForGrid } from "../../apps/desktop/src/lib/tabs/tabPresentation.ts";
import { useConnectionStore } from "../../apps/desktop/src/stores/connectionStore.ts";
import { useQueryStore } from "../../apps/desktop/src/stores/queryStore.ts";
import { useSettingsStore } from "../../apps/desktop/src/stores/settingsStore.ts";
@ -2487,6 +2488,101 @@ test("mongo multi-command execution runs writes sequentially and keeps grouped r
assert.equal(tab?.results?.length, 2);
assert.equal(tab?.activeResultIndex, 0);
assert.equal(tab?.result?.affected_rows, 1);
assert.deepEqual(
tab?.results?.map((result) => result.sourceStatement),
['db.users.insertOne({ name: "Ada" })', 'db.users.insertOne({ name: "Grace" })'],
);
} finally {
globalThis.fetch = originalFetch;
restoreStorage();
}
});
test("redis multi-command execution records source statements for each result", async () => {
const restoreStorage = installMemoryStorage();
setActivePinia(createPinia());
const connectionStore = useConnectionStore();
const store = useQueryStore();
const originalFetch = globalThis.fetch;
const commandBodies: any[] = [];
connectionStore.addEphemeralConnection({
...conn("redis-1"),
db_type: "redis",
port: 6379,
});
globalThis.fetch = withConnectionHealthMock(async (input, init) => {
const url = String(input);
if (url === "/api/redis/execute-command") {
const body = JSON.parse(String(init?.body ?? "{}"));
commandBodies.push(body);
if (body.command === "BAD") return new Response("bad command", { status: 500 });
return new Response(JSON.stringify({ command: body.command, safety: "allowed", value: body.command === "GET user:1" ? "Ada" : "OK" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
return new Response("unexpected request", { status: 500 });
});
try {
const tabId = store.createTab("redis-1", "0", "Redis", "query", "");
await store.executeTabSql(tabId, "GET user:1\nBAD\nPING");
const tab = store.tabs.find((item) => item.id === tabId);
assert.deepEqual(
commandBodies.map((body) => body.command),
["GET user:1", "BAD", "PING"],
);
assert.deepEqual(
tab?.results?.map((result) => result.sourceStatement),
["GET user:1", "BAD", "PING"],
);
assert.deepEqual(tab?.results?.[1]?.columns, ["Error"]);
} finally {
globalThis.fetch = originalFetch;
restoreStorage();
}
});
test("mongo multi-command execution records source statements for error results", async () => {
const restoreStorage = installMemoryStorage();
setActivePinia(createPinia());
const connectionStore = useConnectionStore();
const store = useQueryStore();
const originalFetch = globalThis.fetch;
let insertCount = 0;
connectionStore.addEphemeralConnection({
...conn("mongo-1"),
db_type: "mongodb",
port: 27017,
});
globalThis.fetch = withConnectionHealthMock(async (input) => {
const url = String(input);
if (url === "/api/mongo/insert-documents") {
insertCount += 1;
if (insertCount === 2) return new Response("duplicate key", { status: 500 });
return new Response(JSON.stringify({ affected_rows: 1 }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
return new Response("unexpected request", { status: 500 });
});
try {
const tabId = store.createTab("mongo-1", "accounting", "Query", "query", "");
await store.executeTabSql(tabId, 'db.users.insertOne({ name: "Ada" });\ndb.users.insertOne({ name: "Ada" });');
const tab = store.tabs.find((item) => item.id === tabId);
assert.deepEqual(
tab?.results?.map((result) => result.sourceStatement),
['db.users.insertOne({ name: "Ada" })', 'db.users.insertOne({ name: "Ada" })'],
);
assert.deepEqual(tab?.results?.[1]?.columns, ["Error"]);
} finally {
globalThis.fetch = originalFetch;
restoreStorage();
@ -3453,6 +3549,10 @@ test("query results keep readable table source labels with active database conte
tab?.results?.map((result) => result.sourceStatement),
["select * from users", "select * from orders"],
);
assert.equal(resultSqlForGrid(tab!), "select * from users");
store.setActiveResultIndex(tabId, 1);
tab = store.tabs.find((item) => item.id === tabId);
assert.equal(resultSqlForGrid(tab!), "select * from orders");
await store.executeTabSql(defaultDatabaseTabId, "SELECT *\nFROM apis AS ap\nLIMIT 10;\n\nSELECT *\nFROM menus AS mn\nLIMIT 10;");
tab = store.tabs.find((item) => item.id === defaultDatabaseTabId);
@ -3473,6 +3573,7 @@ test("query results keep readable table source labels with active database conte
await store.executeTabSql(tabId, "update users set active = true; select * from users");
tab = store.tabs.find((item) => item.id === tabId);
assert.equal(tab?.results?.[0]?.sourceLabel, undefined);
assert.equal(tab?.results?.[0]?.sourceStatement, "update users set active = true");
assert.equal(tab?.results?.[1]?.sourceLabel, "db.users");
assert.equal(tab?.results?.[1]?.sourceStatement, "select * from users");
} finally {

View File

@ -1,17 +1,7 @@
import { strict as assert } from "node:assert";
import { test } from "vitest";
import { createPinia, setActivePinia } from "pinia";
import {
activeResultRun,
databaseDisplayNameForTab,
executionSummaryItems,
nextExecutionSummaryView,
resultGridCacheKey,
resultRunItems,
tabDisplayTitle,
tabModeLabel,
tabularResultItems,
} from "../../apps/desktop/src/lib/tabs/tabPresentation.ts";
import { activeResultRun, databaseDisplayNameForTab, executionSummaryItems, nextExecutionSummaryView, resultGridCacheKey, resultRunItems, resultSqlForGrid, tabDisplayTitle, tabModeLabel, tabularResultItems } from "../../apps/desktop/src/lib/tabs/tabPresentation.ts";
import { useConnectionStore } from "../../apps/desktop/src/stores/connectionStore.ts";
import type { ConnectionConfig, QueryResult, QueryTab } from "../../apps/desktop/src/types/database.ts";
@ -189,16 +179,32 @@ test("tabular result items hide statement results without returned columns", ()
});
test("tabular result items expose source labels when available", () => {
const results = [result([]), result(["id"], { sourceLabel: "public.users" }), result(["name"])];
const results = [result([]), result(["id"], { sourceLabel: "public.users", sourceStatement: "select * from public.users" }), result(["name"], { sourceStatement: "select id, name, email, created_at from users where active = true order by created_at desc" })];
assert.deepEqual(
tabularResultItems(results).map((item) => ({ index: item.index, n: item.n, label: item.label })),
tabularResultItems(results).map((item) => ({ index: item.index, n: item.n, label: item.label, title: item.title })),
[
{ index: 1, n: 1, label: "public.users" },
{ index: 2, n: 2, label: undefined },
{ index: 1, n: 1, label: "public.users", title: "select * from public.users" },
{ index: 2, n: 2, label: "select id, name, email, created_at from users...", title: "select id, name, email, created_at from users where active = true order by created_at desc" },
],
);
assert.deepEqual(tabularResultItems([result(["id"], { sourceLabel: "db.users" })]).map((item) => item.label), ["db.users"]);
assert.deepEqual(
tabularResultItems([result(["id"], { sourceLabel: "db.users" })]).map((item) => item.label),
["db.users"],
);
});
test("resultSqlForGrid prefers the active result source statement", () => {
const tab = queryTab({
sql: "select * from users; select * from orders",
lastExecutedSql: "select * from users; select * from orders",
resultBaseSql: "select * from users; select * from orders",
result: result(["id"], { sourceStatement: "select * from orders" }),
});
assert.equal(resultSqlForGrid(tab), "select * from orders");
assert.equal(resultSqlForGrid(queryTab({ sql: "select 1", resultBaseSql: "select 2" })), "select 2");
assert.equal(resultSqlForGrid(queryTab({ sql: "select 1", lastExecutedSql: "select 3" })), "select 3");
});
test("result run items expose ordered labels and active state", () => {
@ -229,7 +235,10 @@ test("result run items expose ordered labels and active state", () => {
{ id: "run-2", title: "Run 2", sequence: 2, active: true },
]);
assert.equal(activeResultRun(tab)?.id, "run-2");
assert.deepEqual(resultRunItems(queryTab()).map((item) => item.title), []);
assert.deepEqual(
resultRunItems(queryTab()).map((item) => item.title),
[],
);
});
test("result grid cache key includes result run id and statement result index", () => {