🐛 fix(query-editor): 修复 Oracle 查询结果编辑提交失败

- 规范化 Oracle/Dameng 未加引号表名大小写
- 按元数据列名映射查询结果可写字段
- 补充查询结果编辑提交回归测试
Refs #464
This commit is contained in:
Syngnat
2026-05-15 21:02:00 +08:00
parent b9f9a8fca2
commit acb119d80e
4 changed files with 104 additions and 15 deletions

View File

@@ -334,6 +334,47 @@ describe('QueryEditor external SQL save', () => {
expect(messageApi.warning).not.toHaveBeenCalled();
});
it('normalizes unquoted lowercase Oracle identifiers before committing query result edits', async () => {
storeState.connections[0].config.type = 'oracle';
storeState.connections[0].config.database = 'ORCLPDB1';
backendApp.DBQueryMulti.mockResolvedValueOnce({
success: true,
data: [{ columns: ['NAME', '__gonavi_locator_1_ID'], rows: [{ NAME: 'old-name', __gonavi_locator_1_ID: 7 }] }],
});
backendApp.DBGetColumns.mockResolvedValueOnce({
success: true,
data: [{ name: 'ID', key: 'PRI' }, { name: 'NAME', key: '' }],
});
let renderer: ReactTestRenderer;
await act(async () => {
renderer = create(<QueryEditor tab={createTab({ dbName: 'anonymous', query: 'select name from mycimled.edc_log' })} />);
});
await act(async () => {
await findButton(renderer!, '运行').props.onClick();
});
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
expect(backendApp.DBGetColumns).toHaveBeenCalledWith(expect.anything(), 'MYCIMLED', 'EDC_LOG');
expect(dataGridState.latestProps?.tableName).toBe('MYCIMLED.EDC_LOG');
expect(dataGridState.latestProps?.editLocator).toMatchObject({
strategy: 'primary-key',
columns: ['ID'],
valueColumns: ['__gonavi_locator_1_ID'],
hiddenColumns: ['__gonavi_locator_1_ID'],
writableColumns: {
name: 'NAME',
},
readOnly: false,
});
expect(dataGridState.latestProps?.readOnly).toBe(false);
expect(messageApi.warning).not.toHaveBeenCalled();
});
it('uses a unique index locator for query results without primary keys', async () => {
storeState.connections[0].config.type = 'oracle';
storeState.connections[0].config.database = 'ORCLPDB1';
@@ -579,7 +620,8 @@ describe('QueryEditor external SQL save', () => {
await Promise.resolve();
});
expect(dataGridState.latestProps?.tableName).toBe(forceReadOnlyQueryResult ? undefined : 'users');
const expectedTableName = dbType === 'oracle' || dbType === 'dameng' ? 'USERS' : 'users';
expect(dataGridState.latestProps?.tableName).toBe(forceReadOnlyQueryResult ? undefined : expectedTableName);
expect(dataGridState.latestProps?.editLocator).toBeUndefined();
expect(dataGridState.latestProps?.readOnly).toBe(true);
expect(backendApp.DBGetColumns).not.toHaveBeenCalled();

View File

@@ -448,6 +448,13 @@ const findWritableResultColumnForSource = (writableColumns: Record<string, strin
))?.[0];
};
const resolveMetadataColumnName = (tableColumnNames: string[], sourceColumn: string): string => {
const normalizedSource = String(sourceColumn || '').trim();
if (!normalizedSource) return '';
return tableColumnNames.find((column) => String(column || '').trim().toLowerCase() === normalizedSource.toLowerCase())
|| normalizedSource;
};
const buildQueryLocatorAlias = (column: string, index: number): string => {
const normalized = String(column || '').trim().replace(/[^A-Za-z0-9_]/g, '_').slice(0, 48) || 'column';
return `${QUERY_LOCATOR_ALIAS_PREFIX}${index}_${normalized}`;
@@ -520,7 +527,8 @@ const resolveQueryLocatorPlan = async ({
? Object.fromEntries(tableColumnNames.map((column) => [column, column]))
: {};
Object.entries(selectInfo.writableColumns).forEach(([resultColumn, sourceColumn]) => {
writableColumns[resultColumn] = sourceColumn;
const metadataColumn = resolveMetadataColumnName(tableColumnNames, sourceColumn);
if (metadataColumn) writableColumns[resultColumn] = metadataColumn;
});
const appendExpressions: string[] = [];
const hiddenColumns: string[] = [];