From 70b469d34979fc6528ed8939f88efcb49f94944c Mon Sep 17 00:00:00 2001 From: Syngnat Date: Sun, 14 Jun 2026 16:07:52 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(query-editor):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20SQL=20=E7=BE=8E=E5=8C=96=E6=9C=AA=E6=8C=89=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E6=96=B9=E8=A8=80=E9=80=89=E6=8B=A9=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 QueryEditor 美化入口按当前连接类型动态选择 sql-formatter language - 让 postgres、kingbase、highgo、vastbase、opengauss、gaussdb 使用 postgresql 方言 - 让 oracle、dameng 使用 plsql,避免固定 mysql 方言导致语法解析失败 - 补充 PostgreSQL 窗口函数与类型转换 SQL 的美化回归测试 Close #561 --- .../QueryEditor.external-sql-save.test.tsx | 31 ++++++++++++++++ frontend/src/components/QueryEditor.tsx | 37 ++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx index fc5b2b19..8684f308 100644 --- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx +++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx @@ -1408,6 +1408,37 @@ describe('QueryEditor external SQL save', () => { ); }); + it('formats postgres window-function SQL with cast syntax through Monaco edits', async () => { + let renderer!: ReactTestRenderer; + storeState.connections[0].config.type = 'postgres'; + storeState.connections[0].config.database = 'main'; + const pgSql = [ + 'SELECT', + `FLOOR(DATE_PART('epoch', "CREATE_TIME" - LAG("END_TIME") OVER (ORDER BY "CREATE_TIME" asc, "ID" desc))*1000)::int as time_diff_seconds,`, + '*', + `FROM "FAM_RU_BLOCK" WHERE "RU_JOB_ID" = ''`, + ].join('\n'); + + await act(async () => { + renderer = create(); + }); + + const formatButton = findButton(renderer, '美化'); + await act(async () => { + await formatButton.props.onClick(); + }); + + expect(messageApi.error).not.toHaveBeenCalled(); + expect(editorState.editor.executeEdits).toHaveBeenCalledWith( + 'gonavi-format-sql', + expect.arrayContaining([ + expect.objectContaining({ + text: expect.stringContaining(')::int AS time_diff_seconds'), + }), + ]), + ); + }); + it('shows object info via editor ctrl+q action', async () => { editorState.value = 'select users.id from users'; autoFetchState.visible = true; diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx index 7bbd0900..833e721a 100644 --- a/frontend/src/components/QueryEditor.tsx +++ b/frontend/src/components/QueryEditor.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import Editor, { type OnMount } from './MonacoEditor'; import { message, Modal, Input, Form, MenuProps } from 'antd'; -import { format } from 'sql-formatter'; +import { format, type SqlLanguage } from 'sql-formatter'; import { v4 as uuidv4 } from 'uuid'; import { TabData, ColumnDefinition, IndexDefinition } from '../types'; import { useStore } from '../store'; @@ -468,6 +468,38 @@ const normalizeMetadataDialect = (conn: any): string => { return String(dialect || '').toLowerCase(); }; +const resolveQueryEditorFormatterLanguage = (conn: any): SqlLanguage => { + const dialect = normalizeMetadataDialect(conn); + switch (dialect) { + case 'postgres': + case 'kingbase': + case 'highgo': + case 'vastbase': + case 'opengauss': + case 'gaussdb': + return 'postgresql'; + case 'duckdb': + return 'duckdb'; + case 'sqlite': + return 'sqlite'; + case 'sqlserver': + return 'transactsql'; + case 'oracle': + case 'dameng': + return 'plsql'; + case 'clickhouse': + return 'clickhouse'; + case 'mysql': + case 'goldendb': + case 'sphinx': + return 'mysql'; + case 'mariadb': + return 'mariadb'; + default: + return 'sql'; + } +}; + const buildCompletionTableCommentSQL = (dialect: string, dbName: string): string => { const db = String(dbName || '').trim(); const escapedDb = escapeMetadataSqlLiteral(db); @@ -3684,7 +3716,8 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc const handleFormat = () => { try { - const formatted = format(getCurrentQuery(), { language: 'mysql', keywordCase: sqlFormatOptions.keywordCase }); + const formatterLanguage = resolveQueryEditorFormatterLanguage(connections.find(c => c.id === currentConnectionId)); + const formatted = format(getCurrentQuery(), { language: formatterLanguage, keywordCase: sqlFormatOptions.keywordCase }); const editor = editorRef.current; const monaco = monacoRef.current; const model = editor?.getModel?.();