🐛 fix(dameng): 修复系统视图查询被注入 ROWID

- 达梦系统视图字段元数据为空时按只读结果处理\n- 避免 DBA_TAB_PRIVS 和 USER_COL_COMMENTS 查询追加 ROWID\n- 增加两个达梦系统视图查询回归测试
This commit is contained in:
AutumnNazi
2026-07-29 17:22:36 +08:00
parent 61358b1a62
commit 5a4c1e4c4e
2 changed files with 85 additions and 0 deletions

View File

@@ -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';

View File

@@ -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);