mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-08 03:21:32 +08:00
🐛 fix(oracle): 修复裸表查询结果字段元数据缺失
- Oracle-like 裸表查询默认使用连接用户名作为 schema 加载字段元数据 - 修复未写 schema 时结果表头不显示字段类型和注释的问题 - 保持显式 schema.table 查询优先级不变 - 补充查询结果表引用解析和 QueryEditor 回归测试
This commit is contained in:
@@ -3046,6 +3046,57 @@ describe('QueryEditor external SQL save', () => {
|
||||
expect(messageApi.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
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';
|
||||
storeState.connections[0].config.database = 'ORCLPDB1';
|
||||
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [{
|
||||
columns: ['PID', 'BUSILOG_ID', 'ZJLX_ID', ORACLE_ROWID_LOCATOR_COLUMN],
|
||||
rows: [{
|
||||
PID: '200005000000010',
|
||||
BUSILOG_ID: '00000000000000000000',
|
||||
ZJLX_ID: '01',
|
||||
[ORACLE_ROWID_LOCATOR_COLUMN]: 'AAATestAABAAABrXAAA',
|
||||
}],
|
||||
}],
|
||||
});
|
||||
backendApp.DBGetColumns.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [
|
||||
{ name: 'PID', type: 'CHAR(15)', comment: '个人标识', key: '' },
|
||||
{ name: 'BUSILOG_ID', type: 'VARCHAR2(20)', comment: '业务日志编号', key: '' },
|
||||
{ name: 'ZJLX_ID', type: 'CHAR(2)', comment: '证件类型', key: '' },
|
||||
],
|
||||
});
|
||||
|
||||
let renderer: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<QueryEditor tab={createTab({ dbName: 'ORCLPDB1', query: 'select * from per_cert_info' })} />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await findButton(renderer!, '运行').props.onClick();
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(backendApp.DBGetColumns).toHaveBeenCalledWith(expect.anything(), 'DEV', 'PER_CERT_INFO');
|
||||
expect(dataGridState.latestProps?.tableName).toBe('DEV.PER_CERT_INFO');
|
||||
expect(dataGridState.latestProps?.editLocator).toMatchObject({
|
||||
strategy: 'oracle-rowid',
|
||||
columns: ['ROWID'],
|
||||
valueColumns: [ORACLE_ROWID_LOCATOR_COLUMN],
|
||||
readOnly: false,
|
||||
});
|
||||
expect(dataGridState.latestProps?.readOnly).toBe(false);
|
||||
expect(String(backendApp.DBQueryMulti.mock.calls[0][2])).toContain('gonavi_query_source.ROWID');
|
||||
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';
|
||||
|
||||
@@ -1794,7 +1794,8 @@ const resolveQueryLocatorPlan = async ({
|
||||
};
|
||||
if (forceReadOnly) return plan;
|
||||
|
||||
const tableRef = extractQueryResultTableRef(statement, dbType, currentDb);
|
||||
const defaultSchema = isOracleLikeDialect(dbType) ? String(config?.user || '').trim() : '';
|
||||
let tableRef = extractQueryResultTableRef(statement, dbType, currentDb, defaultSchema);
|
||||
if (!tableRef) return plan;
|
||||
plan.tableRef = tableRef;
|
||||
if (isSystemMetadataQueryResult(tableRef, dbType)) {
|
||||
@@ -1811,6 +1812,14 @@ const resolveQueryLocatorPlan = async ({
|
||||
return plan;
|
||||
}
|
||||
|
||||
if (isOracleLikeDialect(dbType) && defaultSchema && !String(tableRef.tableName || '').includes('.')) {
|
||||
tableRef = {
|
||||
...tableRef,
|
||||
tableName: `${tableRef.metadataDbName}.${tableRef.metadataTableName}`,
|
||||
};
|
||||
plan.tableRef = tableRef;
|
||||
}
|
||||
|
||||
try {
|
||||
const [resCols, resIndexes] = await Promise.all([
|
||||
DBGetColumns(buildRpcConnectionConfig(config) as any, tableRef.metadataDbName, tableRef.metadataTableName),
|
||||
|
||||
@@ -39,6 +39,15 @@ describe('extractQueryResultTableRef', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the login user as Oracle default schema when the current db is a service name', () => {
|
||||
expect(extractQueryResultTableRef('SELECT * FROM per_cert_info', 'oracle', 'ORCLPDB1', 'dev'))
|
||||
.toEqual({
|
||||
tableName: 'PER_CERT_INFO',
|
||||
metadataDbName: 'DEV',
|
||||
metadataTableName: 'PER_CERT_INFO',
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps existing simple table behavior for MySQL-style qualified names', () => {
|
||||
expect(extractQueryResultTableRef('SELECT * FROM app.users LIMIT 500', 'mysql', 'app'))
|
||||
.toEqual({
|
||||
|
||||
@@ -61,6 +61,7 @@ export const extractQueryResultTableRef = (
|
||||
sql: string,
|
||||
dialect: string,
|
||||
currentDb: string,
|
||||
defaultSchema = '',
|
||||
): QueryResultTableRef | undefined => {
|
||||
const text = String(sql || '').trim();
|
||||
if (!text) return undefined;
|
||||
@@ -77,10 +78,16 @@ export const extractQueryResultTableRef = (
|
||||
if (!metadataTableName) return undefined;
|
||||
|
||||
const owner = parts.length >= 2 ? parts[parts.length - 2] : '';
|
||||
const metadataDbName = owner || normalizeCurrentDbName(currentDb, dialect);
|
||||
const tableName = (isOracleLikeDialect(dialect) || keepsQualifiedTableNameForMetadata(dialect)) && owner
|
||||
const defaultOracleSchema = isOracleLikeDialect(dialect)
|
||||
? normalizeCurrentDbName(defaultSchema, dialect)
|
||||
: '';
|
||||
const fallbackSchema = isOracleLikeDialect(dialect)
|
||||
? defaultOracleSchema || normalizeCurrentDbName(currentDb, dialect)
|
||||
: normalizeCurrentDbName(currentDb, dialect);
|
||||
const metadataDbName = owner || fallbackSchema;
|
||||
const tableName = isOracleLikeDialect(dialect) && owner
|
||||
? `${owner}.${metadataTableName}`
|
||||
: metadataTableName;
|
||||
: (keepsQualifiedTableNameForMetadata(dialect) && owner ? `${owner}.${metadataTableName}` : metadataTableName);
|
||||
const resolvedMetadataTableName = keepsQualifiedTableNameForMetadata(dialect) && owner
|
||||
? `${owner}.${metadataTableName}`
|
||||
: metadataTableName;
|
||||
|
||||
Reference in New Issue
Block a user