From 7aaf36ac2ce70dcc9782762640340b8c2ba5456e Mon Sep 17 00:00:00 2001 From: mango <1711456624@qq.com> Date: Tue, 28 Jul 2026 19:01:39 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=E4=BF=AE=E5=A4=8D=20Issu?= =?UTF-8?q?e=20#762=20(Closes=20#762)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QueryEditor.external-sql-save.test.tsx | 26 +++++++++++++++++++ frontend/src/components/QueryEditor.tsx | 13 +++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx index 7883eebf..2d2d5d8e 100644 --- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx +++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx @@ -4550,6 +4550,32 @@ describe('QueryEditor external SQL save', () => { expect(messageApi.success).toHaveBeenCalledWith('已还原到美化前 SQL'); }); + it('formats OceanBase Oracle SQL with parameter placeholders', async () => { + let renderer!: ReactTestRenderer; + storeState.connections[0].config.type = 'oceanbase'; + (storeState.connections[0].config as any).oceanBaseProtocol = 'oracle'; + const oracleSql = 'select * from users where id = #{id,jdbcType=NUMBER} and tenant = ${tenant} and status = :status and code = ?'; + + 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.stringMatching(/#\{id,jdbcType=NUMBER\}[\s\S]*\$\{tenant\}[\s\S]*:status[\s\S]*\?/), + }), + ]), + ); + }); + it('formats postgres window-function SQL with cast syntax through Monaco edits', async () => { let renderer!: ReactTestRenderer; storeState.connections[0].config.type = 'postgres'; diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx index 6f4d18a5..f096cd86 100644 --- a/frontend/src/components/QueryEditor.tsx +++ b/frontend/src/components/QueryEditor.tsx @@ -221,6 +221,13 @@ const QUERY_EDITOR_MAC_FIND_WITH_SELECTION_GUARD_ACTION_ID = 'gonavi.suppressMac const QUERY_EDITOR_AI_INLINE_DEBOUNCE_MS = 220; const QUERY_EDITOR_AI_INLINE_CONTEXT_KEY = 'gonaviAiInlineSuggestionVisible'; const QUERY_EDITOR_IME_FALLBACK_DELAY_MS = 80; +const QUERY_EDITOR_FORMAT_PARAM_TYPES = { + positional: true, + custom: [ + { regex: String.raw`#\{[^{}]+\}` }, + { regex: String.raw`\$\{[^{}]+\}` }, + ], +}; const EMPTY_QUERY_EDITOR_SQL_LOGS: SqlLog[] = []; const isOceanBaseOracleConnection = (config: any): boolean => { @@ -6149,7 +6156,11 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc const formatSelection = !!selection && !!selectedRaw.trim(); const fullQuery = getCurrentQuery(); const sourceSql = formatSelection ? selectedRaw : fullQuery; - const formatted = format(sourceSql, { language: formatterLanguage, keywordCase: sqlFormatOptions.keywordCase }); + const formatted = format(sourceSql, { + language: formatterLanguage, + keywordCase: sqlFormatOptions.keywordCase, + paramTypes: QUERY_EDITOR_FORMAT_PARAM_TYPES, + }); if (sourceSql === formatted) { return; }