mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-18 21:04:16 +08:00
🐛 fix(query-editor): 修复 Oracle 长脚本执行卡死
- 线性屏蔽 SQL 注释和字符串,避免 PL/SQL 定义检测发生正则灾难性回溯 - 仅为顶层 SELECT 解析结果表定位,避免匿名块内部查询触发元数据加载 - 新增长注释匿名块与多结果回归,验证 Oracle 过程和 SQLPlus 分隔符兼容性
This commit is contained in:
@@ -916,6 +916,63 @@ describe('QueryEditor external SQL save', () => {
|
||||
renderer?.unmount();
|
||||
});
|
||||
|
||||
it('executes a long commented Oracle anonymous block without blocking the UI thread', async () => {
|
||||
storeState.appearance.uiVersion = 'v2';
|
||||
storeState.connections[0].config.type = 'oracle';
|
||||
storeState.connections[0].config.database = 'ORCLPDB1';
|
||||
const columns = Array.from(
|
||||
{ length: 42 },
|
||||
(_, index) => ` column_${index + 1} VARCHAR2(100) DEFAULT 'value_${index + 1}'`,
|
||||
).join(',\n');
|
||||
const sql = [
|
||||
'-- ------------------------------------------------------------',
|
||||
'-- Long Oracle anonymous setup block',
|
||||
'-- ------------------------------------------------------------',
|
||||
'DECLARE',
|
||||
' v_cnt NUMBER;',
|
||||
'BEGIN',
|
||||
' SELECT COUNT(1) INTO v_cnt',
|
||||
' FROM user_tables',
|
||||
" WHERE table_name = 'GONAVI_REPRO_TABLE';",
|
||||
' IF v_cnt = 0 THEN',
|
||||
" EXECUTE IMMEDIATE '\n CREATE TABLE gonavi_repro_table (\n" + columns + "\n )\n ';",
|
||||
' END IF;',
|
||||
'END;',
|
||||
'/',
|
||||
].join('\n');
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: Array.from({ length: 52 }, (_, index) => ({
|
||||
statementIndex: index + 1,
|
||||
columns: ['affectedRows'],
|
||||
rows: [{ affectedRows: 0 }],
|
||||
})),
|
||||
});
|
||||
|
||||
let renderer!: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'ORCLPDB1', query: sql })} />);
|
||||
});
|
||||
|
||||
const runButton = findByClassName(renderer, 'gn-v2-query-toolbar-run-action');
|
||||
await act(async () => {
|
||||
await runButton.props.onClick();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
const resultTabs = renderer.root.findAll((node) =>
|
||||
node.type === 'button' && String(node.props?.['data-tab-key'] || '').startsWith('result-'),
|
||||
);
|
||||
|
||||
expect(backendApp.DBQueryMulti).toHaveBeenCalledOnce();
|
||||
expect(backendApp.DBGetColumns).not.toHaveBeenCalled();
|
||||
expect(backendApp.DBGetIndexes).not.toHaveBeenCalled();
|
||||
expect(resultTabs).toHaveLength(52);
|
||||
await act(async () => {
|
||||
renderer.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
it('runs the whole Oracle procedure when the cursor is in the exception tail', async () => {
|
||||
storeState.connections[0].config.type = 'oracle';
|
||||
storeState.connections[0].config.database = 'ORCLPDB1';
|
||||
|
||||
@@ -167,6 +167,7 @@ import {
|
||||
queryCompletionMetadataRowsBySpecs,
|
||||
readSidebarSqlDropText,
|
||||
matchLeadingSelectTableReference,
|
||||
maskQueryEditorSqlLiteralsAndComments,
|
||||
materializeBoundedQueryEditorCompletionBatches,
|
||||
resolveNewQueryDefaultTemplate,
|
||||
resolveEventTargetNode,
|
||||
@@ -6253,7 +6254,9 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
};
|
||||
|
||||
const containsOraclePlsqlDefinition = (statements: string[]): boolean => (
|
||||
statements.some((statement) => /^\s*(?:(?:--[^\n]*|\/\*[\s\S]*?\*\/)\s*)*CREATE\s+(?:OR\s+REPLACE\s+)?(?:EDITIONABLE\s+|NONEDITIONABLE\s+)?(?:PROCEDURE|FUNCTION|PACKAGE|TRIGGER)\b/i.test(statement))
|
||||
statements.some((statement) => /^\s*CREATE\s+(?:OR\s+REPLACE\s+)?(?:EDITIONABLE\s+|NONEDITIONABLE\s+)?(?:PROCEDURE|FUNCTION|PACKAGE|TRIGGER)\b/i.test(
|
||||
maskQueryEditorSqlLiteralsAndComments(statement),
|
||||
))
|
||||
);
|
||||
|
||||
const normalizeOracleSqlPlusSlashTerminators = (sql: string): string => (
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
type EditRowLocator,
|
||||
} from '../../utils/rowLocator';
|
||||
import { getQueryTabDraft, hasQueryTabDraft } from '../../utils/sqlFileTabDrafts';
|
||||
import { resolveSqlEditorOperationKeyword } from '../../utils/sqlEditorTransaction';
|
||||
import { getColumnDefinitionKey, getColumnDefinitionName } from '../../utils/columnDefinition';
|
||||
import { resolveUniqueKeyGroupsFromIndexes } from '../dataGridCopyInsert';
|
||||
import { t as translate } from '../../i18n';
|
||||
@@ -2709,6 +2710,9 @@ export const resolveQueryLocatorPlan = async ({
|
||||
executedSql: statement,
|
||||
pkColumns: [],
|
||||
};
|
||||
if (resolveSqlEditorOperationKeyword(statement) !== 'select') {
|
||||
return plan;
|
||||
}
|
||||
const defaultSchema = isOracleLikeDialect(dbType)
|
||||
? resolveOracleLikeExecutionSchemaName(config, currentDb)
|
||||
: '';
|
||||
|
||||
Reference in New Issue
Block a user