fix(query): show SQL Server notices outside result-set tabs
Closes #9166
This commit is contained in:
11 files changed
+188
-12
No files matched your search
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { queryResultMessages } from "@/lib/query/queryResultMessages";
|
||||
import { defaultViewForResult } from "@/lib/query/queryResultDefaultView";
|
||||
import type { QueryResult } from "@/types/database";
|
||||
|
||||
const messageResult: QueryResult = { columns: ["Message"], rows: [["before"], [""]], affected_rows: 0, execution_time_ms: 1, server_message: true };
|
||||
|
||||
describe("SQL Server result messages", () => {
|
||||
it("collects tagged messages around data and errors in execution order", () => {
|
||||
const data: QueryResult = { columns: ["Message"], rows: [["not a notice"]], affected_rows: 0, execution_time_ms: 1 };
|
||||
const error: QueryResult = { ...data, columns: ["Error"], rows: [["failed"]], execution_error: true };
|
||||
const results = [messageResult, data, { ...messageResult, rows: [["between"]] }, error, { ...messageResult, rows: [["after"]] }];
|
||||
|
||||
expect(results.flatMap(queryResultMessages).map(({ message }) => message)).toEqual(["before", "", "between", "after"]);
|
||||
expect(queryResultMessages(data)).toEqual([]);
|
||||
expect(queryResultMessages(error)).toEqual([]);
|
||||
expect(messageResult.rows).toEqual([["before"], [""]]);
|
||||
});
|
||||
|
||||
it("preserves structured metadata without duplicating message rows", () => {
|
||||
const messages = [{ severity: "WARNING", message: "warning", code: "01000", detail: "detail" }];
|
||||
expect(queryResultMessages({ ...messageResult, messages })).toBe(messages);
|
||||
expect(queryResultMessages({ rows: [], messages })).toBe(messages);
|
||||
expect(queryResultMessages({ ...messageResult, rows: [[null], []] })).toEqual([]);
|
||||
});
|
||||
|
||||
it("opens message-only output and preserves other databases' default views", () => {
|
||||
expect(defaultViewForResult(messageResult)).toBe("messages");
|
||||
expect(defaultViewForResult({ ...messageResult, rows: [] })).toBe("summary");
|
||||
expect(defaultViewForResult({ columns: [], rows: [], affected_rows: 0, messages: [{ severity: "NOTICE", message: "postgres" }] })).toBe("messages");
|
||||
expect(defaultViewForResult({ columns: [], rows: [], affected_rows: 1, messages: [{ severity: "INFO", message: "Records: 1" }] })).toBe("summary");
|
||||
expect(defaultViewForResult({ columns: [], rows: [], affected_rows: 0 })).toBe("summary");
|
||||
});
|
||||
});
|
||||
+16
-1
@@ -21,7 +21,7 @@ import {
|
||||
dirtyTabTitleStyle,
|
||||
} from "@/lib/tabs/tabPresentation";
|
||||
import { sqlTextFingerprint } from "@/lib/sql/sqlTextFingerprint";
|
||||
import type { ConnectionConfig, QueryTab } from "@/types/database";
|
||||
import type { ConnectionConfig, QueryResult, QueryTab } from "@/types/database";
|
||||
|
||||
const translations: Record<string, string> = {
|
||||
"tabs.tooltipConnection": "Connection:",
|
||||
@@ -97,6 +97,21 @@ describe("query result SQL selection", () => {
|
||||
});
|
||||
|
||||
describe("query result labels", () => {
|
||||
it("excludes tagged server messages without renumbering storage indexes", () => {
|
||||
const message: QueryResult = { columns: ["Message"], rows: [["notice"]], affected_rows: 0, execution_time_ms: 1, server_message: true };
|
||||
const data: QueryResult = { columns: ["Message"], rows: [["real data"]], affected_rows: 0, execution_time_ms: 1 };
|
||||
const empty: QueryResult = { ...data, rows: [] };
|
||||
const results = [message, data, message, empty, data];
|
||||
|
||||
expect(tabularResultItems(results).map(({ index, n }) => ({ index, n }))).toEqual([
|
||||
{ index: 1, n: 1 },
|
||||
{ index: 3, n: 2 },
|
||||
{ index: 4, n: 3 },
|
||||
]);
|
||||
expect(tabularResultItems([message])).toEqual([]);
|
||||
expect(results).toHaveLength(5);
|
||||
});
|
||||
|
||||
it("preserves both ends when shortening long source labels", () => {
|
||||
expect(middleEllipsis("easy_manager_tool.tool_monitor_data_index_item")).toBe("easy_manage...index_item");
|
||||
expect(middleEllipsis("aaa.apis")).toBe("aaa.apis");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { QueryResult } from "@/types/database";
|
||||
import { queryResultMessages } from "./queryResultMessages";
|
||||
|
||||
/**
|
||||
* Picks the default output view for a result that has no result set.
|
||||
@@ -6,7 +7,8 @@ import type { QueryResult } from "@/types/database";
|
||||
* messages view; routine DML like a MySQL INSERT also carries an INFO message
|
||||
* ("Records: N ...") but keeps the established summary view.
|
||||
*/
|
||||
export function defaultViewForResult(result: Pick<QueryResult, "columns" | "rows" | "affected_rows" | "messages">): "messages" | "summary" {
|
||||
export function defaultViewForResult(result: Pick<QueryResult, "columns" | "rows" | "affected_rows" | "messages" | "server_message">): "messages" | "summary" {
|
||||
if (result.server_message === true && queryResultMessages(result).length > 0) return "messages";
|
||||
const messageOnly = result.columns.length === 0 && result.rows.length === 0 && result.affected_rows === 0 && (result.messages?.length ?? 0) > 0;
|
||||
return messageOnly ? "messages" : "summary";
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { QueryMessage, QueryResult } from "@/types/database";
|
||||
|
||||
export function queryResultMessages(result: Pick<QueryResult, "rows" | "messages" | "server_message">): QueryMessage[] {
|
||||
if (result.messages?.length || result.server_message !== true) return result.messages ?? [];
|
||||
return result.rows.flatMap((row) => (row[0] == null ? [] : [{ severity: "INFO", message: String(row[0]) }]));
|
||||
}
|
||||
@@ -362,7 +362,7 @@ export function tabularResultItems(results: QueryResult[] | undefined): { result
|
||||
if (!results) return [];
|
||||
return results
|
||||
.map((result, index) => ({ result, index }))
|
||||
.filter((item) => item.result.columns.length > 0)
|
||||
.filter((item) => item.result.columns.length > 0 && item.result.server_message !== true)
|
||||
.map((item, ordinal) => {
|
||||
const label = queryResultStatementLabel(item.result);
|
||||
const displayLabel = label ? middleEllipsis(label) : undefined;
|
||||
|
||||
Reference in new issue
Block a user