mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-22 00:42:47 +08:00
🐛 修复达梦系统视图查询失败 (#783)
背景:达梦查询 DBA_TAB_PRIVS、USER_COL_COMMENTS 等系统视图时,元数据查询可能成功但返回空字段列表,GoNavi 随后仍按可编辑结果处理并自动注入 ROWID,导致 SQL 执行失败(Issue #780)。 修改: - 空字段元数据结果按只读结果处理,阻止 Oracle/Dameng 定位器注入 ROWID。 - 保留普通业务表主键、唯一键和 ROWID 编辑逻辑不变。 - 增加 USER_COL_COMMENTS 和 DBA_TAB_PRIVS 回归测试。 验证: - QueryEditor 测试:285/285 通过 - npm run build 通过(仅保留仓库已有的 chunk size warning) Fixes #780
This commit is contained in:
@@ -9509,6 +9509,87 @@ describe('QueryEditor external SQL save', () => {
|
||||
expect(messageApi.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('keeps Dameng USER_COL_COMMENTS queries read-only without injecting ROWID', async () => {
|
||||
storeState.connections[0].config.type = 'dameng';
|
||||
storeState.connections[0].config.database = 'APP';
|
||||
const sql = `SELECT T.TABLE_NAME, T.COLUMN_NAME, T.COMMENTS
|
||||
FROM USER_COL_COMMENTS T
|
||||
WHERE T.TABLE_NAME = 'MEITUAN_COMMENT_INFO';`;
|
||||
backendApp.DBGetColumns.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBGetIndexes.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [{
|
||||
columns: ['TABLE_NAME', 'COLUMN_NAME', 'COMMENTS'],
|
||||
rows: [{
|
||||
TABLE_NAME: 'MEITUAN_COMMENT_INFO',
|
||||
COLUMN_NAME: 'CONTENT',
|
||||
COMMENTS: '评论内容',
|
||||
}],
|
||||
}],
|
||||
});
|
||||
|
||||
let renderer: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'APP', query: sql })} />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await findButton(renderer!, '运行').props.onClick();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const executedSql = String(backendApp.DBQueryMulti.mock.calls[0][2]);
|
||||
expect(executedSql).toContain('FROM USER_COL_COMMENTS T');
|
||||
expect(executedSql).not.toMatch(/\bROWID\b/i);
|
||||
expect(dataGridState.latestProps?.editLocator).toMatchObject({ readOnly: true });
|
||||
expect(dataGridState.latestProps?.readOnly).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps Dameng DBA_TAB_PRIVS queries read-only without injecting ROWID', async () => {
|
||||
storeState.connections[0].config.type = 'dameng';
|
||||
storeState.connections[0].config.database = 'APP';
|
||||
const sql = `SELECT *
|
||||
FROM DBA_TAB_PRIVS
|
||||
WHERE GRANTEE = 'APPUSER';`;
|
||||
backendApp.DBGetColumns.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBGetIndexes.mockResolvedValueOnce({ success: true, data: [] });
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [{
|
||||
columns: ['GRANTEE', 'OWNER', 'TABLE_NAME', 'PRIVILEGE'],
|
||||
rows: [{
|
||||
GRANTEE: 'APPUSER',
|
||||
OWNER: 'APPUSER',
|
||||
TABLE_NAME: 'MEITUAN_COMMENT_INFO',
|
||||
PRIVILEGE: 'SELECT',
|
||||
}],
|
||||
}],
|
||||
});
|
||||
|
||||
let renderer: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'APP', query: sql })} />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await findButton(renderer!, '运行').props.onClick();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const executedSql = String(backendApp.DBQueryMulti.mock.calls[0][2]);
|
||||
expect(executedSql).toContain('FROM DBA_TAB_PRIVS');
|
||||
expect(executedSql).not.toMatch(/\bROWID\b/i);
|
||||
expect(dataGridState.latestProps?.editLocator).toMatchObject({ readOnly: true });
|
||||
expect(dataGridState.latestProps?.readOnly).toBe(true);
|
||||
});
|
||||
|
||||
it('uses Oracle login user as default schema for unqualified query result metadata', async () => {
|
||||
storeState.connections[0].config.type = 'oracle';
|
||||
storeState.connections[0].config.user = 'dev';
|
||||
|
||||
@@ -2771,6 +2771,10 @@ export const resolveQueryLocatorPlan = async ({
|
||||
|
||||
const tableColumns = resCols.data as ColumnDefinition[];
|
||||
const tableColumnNames = tableColumns.map(getColumnDefinitionName).filter(Boolean);
|
||||
if (tableColumnNames.length === 0) {
|
||||
plan.editLocator = buildQueryReadOnlyLocator(translate('query_editor.message.read_only_system_metadata'));
|
||||
return plan;
|
||||
}
|
||||
let executableStatement = statement;
|
||||
if (isOracleLikeDialect(dbType) && selectInfo.selectsAll) {
|
||||
const rewritten = rewriteOracleDuplicateSelectColumns(executableStatement, tableColumnNames);
|
||||
|
||||
Reference in New Issue
Block a user