diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx
index 237e7dd..d11c380 100644
--- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx
+++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx
@@ -1656,7 +1656,7 @@ describe('QueryEditor external SQL save', () => {
});
});
- expect(storeState.setActiveContext).toHaveBeenCalledWith({ connectionId: 'conn-1', dbName: 'analytics' });
+ expect(storeState.setActiveContext).not.toHaveBeenCalled();
expect(storeState.addTab).toHaveBeenCalledWith({
id: 'conn-1-analytics-table-events',
title: 'events',
@@ -1666,7 +1666,7 @@ describe('QueryEditor external SQL save', () => {
tableName: 'events',
objectType: 'table',
});
- expect((window as any).dispatchEvent).toHaveBeenCalledWith(expect.objectContaining({
+ expect((window as any).dispatchEvent).not.toHaveBeenCalledWith(expect.objectContaining({
type: 'gonavi:locate-sidebar-object',
}));
expect(preventDefault).toHaveBeenCalled();
@@ -1703,7 +1703,7 @@ describe('QueryEditor external SQL save', () => {
});
});
- expect(storeState.setActiveContext).toHaveBeenCalledWith({ connectionId: 'conn-1', dbName: 'mkefu_location_dev_local' });
+ expect(storeState.setActiveContext).not.toHaveBeenCalled();
expect(storeState.addTab).toHaveBeenCalledWith(expect.objectContaining({
type: 'table',
connectionId: 'conn-1',
@@ -1711,6 +1711,79 @@ describe('QueryEditor external SQL save', () => {
tableName: 'fs_mkefu_regist_record',
objectType: 'table',
}));
+ expect((window as any).dispatchEvent).not.toHaveBeenCalledWith(expect.objectContaining({
+ type: 'gonavi:locate-sidebar-object',
+ }));
+ expect(preventDefault).toHaveBeenCalled();
+ expect(stopPropagation).toHaveBeenCalled();
+ });
+
+ it('opens a routine object-edit tab on ctrl click without locating the sidebar tree', async () => {
+ storeState.connections[0].config.type = 'postgres';
+ editorState.value = 'call reporting.refresh_stats();';
+ autoFetchState.visible = true;
+ backendApp.DBGetDatabases.mockResolvedValueOnce({ success: true, data: [{ Database: 'main' }] });
+ backendApp.DBGetTables.mockResolvedValueOnce({ success: true, data: [] });
+ backendApp.DBGetAllColumns.mockResolvedValueOnce({ success: true, data: [] });
+ backendApp.DBQuery.mockImplementation(async (_config: any, _dbName: string, sql: string) => {
+ const text = String(sql || '');
+ if (text.includes('pg_get_functiondef')) {
+ return {
+ success: true,
+ data: [{
+ routine_definition: 'CREATE OR REPLACE PROCEDURE reporting.refresh_stats() LANGUAGE plpgsql AS $$ BEGIN NULL; END; $$;',
+ }],
+ };
+ }
+ if (text.includes('FROM pg_proc') || text.includes('information_schema.routines')) {
+ return {
+ success: true,
+ data: [{ schema_name: 'reporting', routine_name: 'refresh_stats', routine_type: 'PROCEDURE' }],
+ };
+ }
+ return { success: true, data: [] };
+ });
+
+ await act(async () => {
+ create();
+ });
+ await act(async () => {
+ for (let i = 0; i < 12; i += 1) {
+ await Promise.resolve();
+ }
+ });
+
+ const preventDefault = vi.fn();
+ const stopPropagation = vi.fn();
+ await act(async () => {
+ editorState.mouseDownListeners[0]?.({
+ target: { position: { lineNumber: 1, column: 21 } },
+ event: {
+ browserEvent: { button: 0, buttons: 1 },
+ leftButton: true,
+ ctrlKey: true,
+ metaKey: false,
+ preventDefault,
+ stopPropagation,
+ },
+ });
+ for (let i = 0; i < 8; i += 1) {
+ await Promise.resolve();
+ }
+ });
+
+ expect(storeState.setActiveContext).not.toHaveBeenCalled();
+ expect(storeState.addTab).toHaveBeenCalledWith(expect.objectContaining({
+ title: expect.stringContaining('refresh_stats'),
+ type: 'query',
+ connectionId: 'conn-1',
+ dbName: 'main',
+ queryMode: 'object-edit',
+ query: expect.stringContaining('CREATE OR REPLACE PROCEDURE reporting.refresh_stats()'),
+ }));
+ expect((window as any).dispatchEvent).not.toHaveBeenCalledWith(expect.objectContaining({
+ type: 'gonavi:locate-sidebar-object',
+ }));
expect(preventDefault).toHaveBeenCalled();
expect(stopPropagation).toHaveBeenCalled();
});
@@ -3691,7 +3764,7 @@ describe('QueryEditor external SQL save', () => {
});
});
- expect(storeState.setActiveContext).toHaveBeenCalledWith({ connectionId: 'conn-1', dbName: 'main' });
+ expect(storeState.setActiveContext).not.toHaveBeenCalled();
expect(storeState.addTab).toHaveBeenCalledWith({
id: 'view-def-conn-1-main-active_users',
title: '视图:active_users',
@@ -3703,13 +3776,8 @@ describe('QueryEditor external SQL save', () => {
schemaName: 'reporting',
sidebarLocateKey: 'conn-1-main-view-active_users',
});
- expect((window as any).dispatchEvent).toHaveBeenCalledWith(expect.objectContaining({
+ expect((window as any).dispatchEvent).not.toHaveBeenCalledWith(expect.objectContaining({
type: 'gonavi:locate-sidebar-object',
- detail: expect.objectContaining({
- tabId: 'conn-1-main-view-active_users',
- schemaName: 'reporting',
- objectGroup: 'views',
- }),
}));
});
@@ -3775,34 +3843,17 @@ describe('QueryEditor external SQL save', () => {
schemaName: 'audit',
sidebarLocateKey: 'conn-1-main-trigger-audit.users_bi-audit.users',
});
- expect(storeState.addTab).toHaveBeenCalledWith({
- id: 'routine-def-conn-1-main-reporting.refresh_stats',
- title: '存储过程:reporting.refresh_stats',
- type: 'routine-def',
+ expect(storeState.addTab).toHaveBeenCalledWith(expect.objectContaining({
+ id: expect.stringMatching(/^query-edit-routine-conn-1-main-reporting\.refresh_stats-\d+$/),
+ title: '编辑 存储过程:reporting.refresh_stats',
+ type: 'query',
connectionId: 'conn-1',
dbName: 'main',
- routineName: 'reporting.refresh_stats',
- routineType: 'PROCEDURE',
- schemaName: 'reporting',
- sidebarLocateKey: 'conn-1-main-routine-reporting.refresh_stats',
- });
- expect((window as any).dispatchEvent).toHaveBeenCalledWith(expect.objectContaining({
- type: 'gonavi:locate-sidebar-object',
- detail: expect.objectContaining({
- tabId: 'conn-1-main-trigger-audit.users_bi-audit.users',
- triggerName: 'audit.users_bi',
- schemaName: 'audit',
- objectGroup: 'triggers',
- }),
+ queryMode: 'object-edit',
+ query: expect.stringContaining('CREATE OR REPLACE PROCEDURE reporting.refresh_stats()'),
}));
- expect((window as any).dispatchEvent).toHaveBeenCalledWith(expect.objectContaining({
+ expect((window as any).dispatchEvent).not.toHaveBeenCalledWith(expect.objectContaining({
type: 'gonavi:locate-sidebar-object',
- detail: expect.objectContaining({
- tabId: 'conn-1-main-routine-reporting.refresh_stats',
- routineName: 'reporting.refresh_stats',
- schemaName: 'reporting',
- objectGroup: 'routines',
- }),
}));
});
@@ -3879,23 +3930,8 @@ describe('QueryEditor external SQL save', () => {
schemaName: 'billing',
sidebarLocateKey: 'package-def-conn-1-main-billing.pkg_order',
});
- expect((window as any).dispatchEvent).toHaveBeenCalledWith(expect.objectContaining({
+ expect((window as any).dispatchEvent).not.toHaveBeenCalledWith(expect.objectContaining({
type: 'gonavi:locate-sidebar-object',
- detail: expect.objectContaining({
- tabId: 'sequence-def-conn-1-main-billing.order_seq',
- sequenceName: 'billing.order_seq',
- schemaName: 'billing',
- objectGroup: 'sequences',
- }),
- }));
- expect((window as any).dispatchEvent).toHaveBeenCalledWith(expect.objectContaining({
- type: 'gonavi:locate-sidebar-object',
- detail: expect.objectContaining({
- tabId: 'package-def-conn-1-main-billing.pkg_order',
- packageName: 'billing.pkg_order',
- schemaName: 'billing',
- objectGroup: 'packages',
- }),
}));
});
@@ -4003,9 +4039,10 @@ describe('QueryEditor external SQL save', () => {
type: 'trigger',
}));
expect(storeState.addTab).toHaveBeenCalledWith(expect.objectContaining({
- id: 'routine-def-conn-1-main-reporting.refresh_stats',
- title: 'Procedure: reporting.refresh_stats',
- type: 'routine-def',
+ id: expect.stringMatching(/^query-edit-routine-conn-1-main-reporting\.refresh_stats-\d+$/),
+ title: 'Edit Procedure: reporting.refresh_stats',
+ type: 'query',
+ queryMode: 'object-edit',
}));
});
@@ -7743,7 +7780,7 @@ describe('QueryEditor external SQL save', () => {
});
});
- expect(storeState.setActiveContext).toHaveBeenCalledWith({ connectionId: 'conn-1', dbName: 'front_end_sys' });
+ expect(storeState.setActiveContext).not.toHaveBeenCalled();
expect(storeState.addTab).toHaveBeenCalledWith(expect.objectContaining({
type: 'table',
connectionId: 'conn-1',
@@ -7825,7 +7862,7 @@ describe('QueryEditor external SQL save', () => {
});
});
- expect(storeState.setActiveContext).toHaveBeenCalledWith({ connectionId: 'conn-1', dbName: 'front_end_sys' });
+ expect(storeState.setActiveContext).not.toHaveBeenCalled();
expect(storeState.addTab).toHaveBeenCalledWith(expect.objectContaining({
type: 'table',
connectionId: 'conn-1',
diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx
index dd8c54f..c8ded0c 100644
--- a/frontend/src/components/QueryEditor.tsx
+++ b/frontend/src/components/QueryEditor.tsx
@@ -35,6 +35,7 @@ import { resolveUniqueKeyGroupsFromIndexes } from './dataGridCopyInsert';
import { t as translate } from '../i18n';
import { buildSqlAnalysisWorkbenchTab } from '../utils/sqlAnalysisTab';
import { isLocalizedUntitledQueryTitle } from '../utils/queryTabTitle';
+import { buildSqlServerObjectDefinitionQueries } from '../utils/sqlServerObjectDefinition';
import {
DUCKDB_ROWID_LOCATOR_COLUMN,
ORACLE_ROWID_LOCATOR_COLUMN,
@@ -63,6 +64,7 @@ import {
type CompletionTableMeta,
type CompletionTriggerMeta,
type CompletionViewMeta,
+ type QueryEditorNavigationTarget,
type QueryStatementPlan,
QUERY_EDITOR_HOVER_DELAY_MS,
QUERY_EDITOR_LIVE_DECORATION_MAX_TEXT_LENGTH,
@@ -88,7 +90,6 @@ import {
clearQueryEditorObjectDecorations,
collectQueryEditorObjectDecorationCandidates,
collectQueryEditorReferencedDatabaseNames,
- dispatchQueryEditorSidebarLocate,
getCaseInsensitiveValue,
getFirstRowValue,
getNormalizedPositionAtOffset,
@@ -135,6 +136,52 @@ const buildQueryEditorMonacoActionLabel = (key: string): string =>
const QUERY_EDITOR_SQL_PROMPT_PLACEHOLDER = '{SQL}';
+const escapeQueryEditorObjectEditSqlLiteral = (value: unknown): string => (
+ String(value || '').replace(/'/g, "''")
+);
+
+const getQueryEditorObjectEditRawValue = (row: Record, candidateKeys: string[]): any => {
+ const keyMap = new Map();
+ Object.keys(row || {}).forEach((key) => keyMap.set(key.toLowerCase(), row[key]));
+ for (const key of candidateKeys) {
+ if (keyMap.has(key.toLowerCase())) {
+ const value = keyMap.get(key.toLowerCase());
+ if (value !== undefined && value !== null) return value;
+ }
+ }
+ return undefined;
+};
+
+const normalizeQueryEditorRoutineDefinitionForEdit = (
+ definition: string,
+ routineName: string,
+ routineType: string,
+): string => {
+ const text = String(definition || '').trim();
+ if (!text) return '';
+ if (/^\s*create\b/i.test(text)) return text;
+ if (/^\s*(function|procedure)\b/i.test(text)) {
+ return `CREATE OR REPLACE ${text}`;
+ }
+ const normalizedType = String(routineType || 'FUNCTION').trim().toUpperCase().includes('PROC')
+ ? 'PROCEDURE'
+ : 'FUNCTION';
+ return `CREATE OR REPLACE ${normalizedType} ${routineName}\n${text}`;
+};
+
+const buildQueryEditorRoutineEditFallbackSql = (
+ routineName: string,
+ routineType: string,
+): string => {
+ const normalizedType = String(routineType || 'FUNCTION').trim().toUpperCase().includes('PROC')
+ ? 'PROCEDURE'
+ : 'FUNCTION';
+ if (normalizedType === 'PROCEDURE') {
+ return `CREATE OR REPLACE PROCEDURE ${routineName}()\nBEGIN\n -- TODO: edit procedure body\nEND;`;
+ }
+ return `CREATE OR REPLACE FUNCTION ${routineName}()\nRETURNS INTEGER\nBEGIN\n -- TODO: edit function body\n RETURN 0;\nEND;`;
+};
+
const buildQueryEditorAiContextPrompt = (connection: any, database: string): string => {
if (!connection) {
return '';
@@ -1390,6 +1437,154 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
};
}, [cancelEditorResizeFrame, handleMouseMove, handleMouseUp]);
+ const openRoutineObjectEditTab = useCallback(async (
+ navigationTarget: Extract,
+ connectionId: string,
+ targetDbName: string,
+ ) => {
+ const targetRoutineName = String(navigationTarget.routineName || '').trim();
+ if (!targetRoutineName) return;
+
+ const normalizedRoutineType = String(navigationTarget.routineType || 'FUNCTION').trim().toUpperCase().includes('PROC')
+ ? 'PROCEDURE'
+ : 'FUNCTION';
+ const routineTypeLabel = normalizedRoutineType === 'PROCEDURE'
+ ? translate('sidebar.object.procedure')
+ : translate('sidebar.object.function');
+ const sqlTemplateHeader = `-- ${translate('sidebar.sql_template.edit_routine', {
+ type: routineTypeLabel,
+ name: targetRoutineName,
+ })}`;
+ let editSql = `${sqlTemplateHeader}\n-- ${translate('sidebar.sql_template.modify_then_execute')}\n${buildQueryEditorRoutineEditFallbackSql(targetRoutineName, normalizedRoutineType)}`;
+
+ const conn = connectionsRef.current.find((item) => item.id === connectionId);
+ if (conn) {
+ const dialect = normalizeMetadataDialect(conn);
+ const parsedRoutine = splitSidebarQualifiedName(targetRoutineName);
+ const routineObjectName = parsedRoutine.objectName || targetRoutineName;
+ const routineSchemaName = String(navigationTarget.schemaName || parsedRoutine.schemaName || '').trim();
+ const safeName = escapeQueryEditorObjectEditSqlLiteral(routineObjectName);
+ const safeSchema = escapeQueryEditorObjectEditSqlLiteral(routineSchemaName);
+ const safeDbName = escapeQueryEditorObjectEditSqlLiteral(targetDbName);
+ const config = {
+ ...conn.config,
+ port: Number(conn.config?.port),
+ password: conn.config?.password || '',
+ database: conn.config?.database || '',
+ useSSH: conn.config?.useSSH || false,
+ ssh: conn.config?.ssh || { host: '', port: 22, user: '', password: '', keyPath: '' },
+ };
+ const queries = (() => {
+ switch (dialect) {
+ case 'mysql':
+ case 'starrocks':
+ return [
+ `SHOW CREATE ${normalizedRoutineType} \`${routineObjectName.replace(/`/g, '``')}\``,
+ safeDbName
+ ? `SELECT ROUTINE_DEFINITION AS routine_definition FROM information_schema.routines WHERE routine_schema = '${safeDbName}' AND routine_name = '${safeName}' AND UPPER(routine_type) = '${normalizedRoutineType}' LIMIT 1`
+ : '',
+ ].filter(Boolean);
+ case 'postgres':
+ case 'kingbase':
+ case 'highgo':
+ case 'vastbase':
+ case 'opengauss':
+ case 'gaussdb': {
+ const schemaRef = safeSchema || 'public';
+ return [`SELECT pg_get_functiondef(p.oid) AS routine_definition FROM pg_proc p JOIN pg_namespace n ON p.pronamespace = n.oid WHERE n.nspname = '${schemaRef}' AND p.proname = '${safeName}' LIMIT 1`];
+ }
+ case 'sqlserver':
+ return buildSqlServerObjectDefinitionQueries('routine', targetRoutineName, targetDbName, 'routine_definition');
+ case 'oracle':
+ case 'dm':
+ case 'dameng': {
+ const owner = safeSchema || safeDbName;
+ return [
+ owner
+ ? `SELECT TEXT FROM ALL_SOURCE WHERE OWNER = '${owner.toUpperCase()}' AND NAME = '${safeName.toUpperCase()}' AND TYPE = '${normalizedRoutineType}' ORDER BY LINE`
+ : `SELECT TEXT FROM USER_SOURCE WHERE NAME = '${safeName.toUpperCase()}' AND TYPE = '${normalizedRoutineType}' ORDER BY LINE`,
+ ];
+ }
+ case 'duckdb': {
+ const schemaRef = safeSchema || 'main';
+ return [
+ `SELECT schema_name, function_name, parameters, macro_definition FROM duckdb_functions() WHERE internal = false AND lower(function_type) = 'macro' AND schema_name = '${schemaRef}' AND function_name = '${safeName}' LIMIT 1`,
+ ];
+ }
+ default:
+ return [];
+ }
+ })();
+
+ for (const queryText of queries) {
+ try {
+ const result = await DBQuery(buildRpcConnectionConfig(config) as any, targetDbName, queryText);
+ if (!result.success || !Array.isArray(result.data) || result.data.length === 0) {
+ continue;
+ }
+ let definition = '';
+ if (dialect === 'oracle' || dialect === 'dm' || dialect === 'dameng') {
+ definition = result.data.map((row: any) => row.text || row.TEXT || Object.values(row)[0] || '').join('');
+ } else if (dialect === 'duckdb') {
+ const row = result.data[0] as Record;
+ const schemaName = String(getQueryEditorObjectEditRawValue(row, ['schema_name']) || routineSchemaName || '').trim();
+ const functionName = String(getQueryEditorObjectEditRawValue(row, ['function_name', 'routine_name', 'name']) || routineObjectName || '').trim();
+ const parametersRaw = getQueryEditorObjectEditRawValue(row, ['parameters']);
+ const macroDefinition = String(getQueryEditorObjectEditRawValue(row, ['macro_definition']) || '').trim();
+ const parameters = Array.isArray(parametersRaw)
+ ? parametersRaw.map((item) => String(item ?? '').trim()).filter(Boolean).join(', ')
+ : String(parametersRaw ?? '').replace(/^\[|\]$/g, '').trim();
+ const qualifiedName = schemaName ? `${schemaName}.${functionName}` : functionName;
+ if (qualifiedName && macroDefinition) {
+ definition = macroDefinition.startsWith('(')
+ ? `CREATE OR REPLACE MACRO ${qualifiedName}(${parameters}) AS ${macroDefinition};`
+ : `CREATE OR REPLACE MACRO ${qualifiedName}(${parameters}) AS TABLE ${macroDefinition};`;
+ }
+ } else if (dialect === 'sqlserver') {
+ definition = result.data
+ .map((row: any) => getQueryEditorObjectEditRawValue(row, ['routine_definition', 'definition', 'text', 'Text']) ?? '')
+ .map((value) => String(value))
+ .join('');
+ } else {
+ const row = result.data[0] as Record;
+ const direct = getQueryEditorObjectEditRawValue(row, ['routine_definition', 'definition']);
+ if (direct !== undefined && direct !== null && String(direct).trim()) {
+ definition = String(direct);
+ } else {
+ const createKey = Object.keys(row).find((key) => /create\s+(function|procedure)/i.test(key));
+ definition = createKey ? String(row[createKey] || '') : '';
+ }
+ }
+
+ const normalizedDefinition = normalizeQueryEditorRoutineDefinitionForEdit(
+ definition,
+ targetRoutineName,
+ normalizedRoutineType,
+ );
+ if (normalizedDefinition) {
+ editSql = `${sqlTemplateHeader}\n${normalizedDefinition}`;
+ break;
+ }
+ } catch {
+ // 查询最新定义失败时保留可编辑模板。
+ }
+ }
+ }
+
+ addTab({
+ id: `query-edit-routine-${connectionId}-${targetDbName}-${targetRoutineName}-${Date.now()}`,
+ title: translate('sidebar.tab.edit_routine', {
+ type: routineTypeLabel,
+ name: targetRoutineName,
+ }),
+ type: 'query',
+ connectionId,
+ dbName: targetDbName,
+ query: editSql,
+ queryMode: 'object-edit',
+ });
+ }, [addTab]);
+
// Setup Autocomplete and Editor
const handleEditorDidMount: OnMount = (editor, monaco) => {
editorRef.current = editor;
@@ -1716,9 +1911,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
return;
}
- setCurrentDb(targetDbName);
- currentDbRef.current = targetDbName;
- setActiveContext({ connectionId, dbName: targetDbName });
if (navigationTarget.type === 'table') {
const targetTableName = String(navigationTarget.tableName || '').trim();
if (!targetTableName) return;
@@ -1731,13 +1923,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
tableName: targetTableName,
objectType: 'table',
});
- dispatchQueryEditorSidebarLocate({
- connectionId,
- dbName: targetDbName,
- tableName: targetTableName,
- schemaName: navigationTarget.schemaName,
- objectGroup: 'tables',
- });
return;
}
@@ -1762,15 +1947,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
schemaName: targetSchemaName || undefined,
sidebarLocateKey,
});
- dispatchQueryEditorSidebarLocate({
- tabId: sidebarLocateKey,
- connectionId,
- dbName: targetDbName,
- viewName: targetViewName,
- tableName: targetViewName,
- schemaName: targetSchemaName,
- objectGroup: navigationTarget.type === 'materialized-view' ? 'materializedViews' : 'views',
- });
return;
}
@@ -1791,15 +1967,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
schemaName: targetSchemaName || undefined,
sidebarLocateKey,
});
- dispatchQueryEditorSidebarLocate({
- tabId: sidebarLocateKey,
- connectionId,
- dbName: targetDbName,
- triggerName: targetTriggerName,
- tableName: targetTriggerName,
- schemaName: targetSchemaName,
- objectGroup: 'triggers',
- });
return;
}
@@ -1818,15 +1985,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
schemaName: targetSchemaName || undefined,
sidebarLocateKey,
});
- dispatchQueryEditorSidebarLocate({
- tabId: sidebarLocateKey,
- connectionId,
- dbName: targetDbName,
- sequenceName: targetSequenceName,
- tableName: targetSequenceName,
- schemaName: targetSchemaName,
- objectGroup: 'sequences',
- });
return;
}
@@ -1845,48 +2003,10 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
schemaName: targetSchemaName || undefined,
sidebarLocateKey,
});
- dispatchQueryEditorSidebarLocate({
- tabId: sidebarLocateKey,
- connectionId,
- dbName: targetDbName,
- packageName: targetPackageName,
- tableName: targetPackageName,
- schemaName: targetSchemaName,
- objectGroup: 'packages',
- });
return;
}
- const targetRoutineName = String(navigationTarget.routineName || '').trim();
- if (!targetRoutineName) return;
- const routineTypeLabel = navigationTarget.routineType === 'PROCEDURE'
- ? translate('sidebar.object.procedure')
- : translate('sidebar.object.function');
- const targetSchemaName = String(navigationTarget.schemaName || '').trim();
- const sidebarLocateKey = `${connectionId}-${targetDbName}-routine-${targetRoutineName}`;
- addTab({
- id: `routine-def-${connectionId}-${targetDbName}-${targetRoutineName}`,
- title: translate('sidebar.tab.routine_definition', {
- type: routineTypeLabel,
- name: targetRoutineName,
- }),
- type: 'routine-def',
- connectionId,
- dbName: targetDbName,
- routineName: targetRoutineName,
- routineType: navigationTarget.routineType,
- schemaName: targetSchemaName || undefined,
- sidebarLocateKey,
- });
- dispatchQueryEditorSidebarLocate({
- tabId: sidebarLocateKey,
- connectionId,
- dbName: targetDbName,
- routineName: targetRoutineName,
- tableName: targetRoutineName,
- schemaName: targetSchemaName,
- objectGroup: 'routines',
- });
+ void openRoutineObjectEditTab(navigationTarget, connectionId, targetDbName);
});
editor.onDidDispose?.(() => {