mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-07 07:03:34 +08:00
🐛 fix(tdengine): 兼容超级表元数据查询结果
- 兼容 stable_name 及不同大小写的超级表字段 - 增加 SHOW STABLES 多种语法回退 - 新增超级表选项解析与去重回归测试
This commit is contained in:
@@ -35,6 +35,7 @@ import {
|
||||
resolveSqlDialect,
|
||||
} from '../utils/sqlDialect';
|
||||
import { splitQualifiedNameLast, stripIdentifierQuotes } from '../utils/qualifiedName';
|
||||
import { buildTDengineStableOptions, buildTDengineStableQueries } from '../utils/tdengineStableMetadata';
|
||||
import {
|
||||
cloneTableDesignerColumnsForPaste,
|
||||
parseTableDesignerColumns,
|
||||
@@ -1681,6 +1682,7 @@ ${selectedTrigger.statement}`;
|
||||
let cancelled = false;
|
||||
const fetchSuperTables = async () => {
|
||||
setTdengineStableOptionsLoading(true);
|
||||
setTdengineStableOptions([]);
|
||||
try {
|
||||
const conn = connections.find(c => c.id === tab.connectionId);
|
||||
if (!conn) return;
|
||||
@@ -1694,17 +1696,17 @@ ${selectedTrigger.statement}`;
|
||||
};
|
||||
const rpcConfig = buildRpcConnectionConfig(config) as any;
|
||||
const dbName = tab.dbName || '';
|
||||
const res = await DBQuery(rpcConfig, dbName, 'SHOW STABLES');
|
||||
if (cancelled) return;
|
||||
if (res.success && Array.isArray(res.data)) {
|
||||
const options = res.data.map((row: any) => {
|
||||
const name = row?.table_name || row?.Table || row?.name || row?.tablename || '';
|
||||
return { label: String(name), value: String(name) };
|
||||
}).filter(opt => opt.value);
|
||||
setTdengineStableOptions(options);
|
||||
} else {
|
||||
setTdengineStableOptions([]);
|
||||
for (const query of buildTDengineStableQueries(dbName)) {
|
||||
const res = await DBQuery(rpcConfig, dbName, query);
|
||||
if (cancelled) return;
|
||||
if (!res?.success || !Array.isArray(res.data)) continue;
|
||||
const options = buildTDengineStableOptions(res.data);
|
||||
if (options.length > 0) {
|
||||
setTdengineStableOptions(options);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!cancelled) setTdengineStableOptions([]);
|
||||
} catch {
|
||||
if (!cancelled) setTdengineStableOptions([]);
|
||||
} finally {
|
||||
|
||||
32
frontend/src/utils/tdengineStableMetadata.test.ts
Normal file
32
frontend/src/utils/tdengineStableMetadata.test.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
buildTDengineStableOptions,
|
||||
buildTDengineStableQueries,
|
||||
} from './tdengineStableMetadata';
|
||||
|
||||
describe('TDengine stable metadata helpers', () => {
|
||||
it('reads stable_name returned by SHOW STABLES and removes duplicate names', () => {
|
||||
expect(buildTDengineStableOptions([
|
||||
{ stable_name: 'meters' },
|
||||
{ STABLE_NAME: 'meters' },
|
||||
{ name: 'weather' },
|
||||
{ stable_name: '' },
|
||||
{ comment: 'not a stable name' },
|
||||
])).toEqual([
|
||||
{ label: 'meters', value: 'meters' },
|
||||
{ label: 'weather', value: 'weather' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps TDengine query fallbacks for context and qualified database syntax', () => {
|
||||
expect(buildTDengineStableQueries('metrics')).toEqual([
|
||||
'SHOW STABLES',
|
||||
'SHOW STABLES FROM `metrics`',
|
||||
'SHOW STABLES FROM metrics',
|
||||
'SHOW metrics.STABLES',
|
||||
]);
|
||||
expect(buildTDengineStableQueries('metrics`prod')).toContain('SHOW STABLES FROM `metrics``prod`');
|
||||
expect(buildTDengineStableQueries('')).toEqual(['SHOW STABLES']);
|
||||
});
|
||||
});
|
||||
65
frontend/src/utils/tdengineStableMetadata.ts
Normal file
65
frontend/src/utils/tdengineStableMetadata.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
export interface TDengineStableOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const STABLE_NAME_KEYS = [
|
||||
'stable_name',
|
||||
'stableName',
|
||||
'table_name',
|
||||
'Table',
|
||||
'name',
|
||||
'tablename',
|
||||
'table',
|
||||
] as const;
|
||||
|
||||
const readText = (value: unknown): string => {
|
||||
if (typeof value !== 'string' && typeof value !== 'number') return '';
|
||||
return String(value).trim();
|
||||
};
|
||||
|
||||
export const resolveTDengineStableName = (row: unknown): string => {
|
||||
if (!row || typeof row !== 'object') return '';
|
||||
const record = row as Record<string, unknown>;
|
||||
|
||||
for (const key of STABLE_NAME_KEYS) {
|
||||
const value = readText(record[key]);
|
||||
if (value) return value;
|
||||
}
|
||||
|
||||
const normalizedKeys = new Set(STABLE_NAME_KEYS.map((key) => key.toLowerCase()));
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
if (!normalizedKeys.has(key.toLowerCase())) continue;
|
||||
const text = readText(value);
|
||||
if (text) return text;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
export const buildTDengineStableOptions = (rows: unknown[]): TDengineStableOption[] => {
|
||||
const seen = new Set<string>();
|
||||
const options: TDengineStableOption[] = [];
|
||||
|
||||
for (const row of rows) {
|
||||
const name = resolveTDengineStableName(row);
|
||||
if (!name || seen.has(name)) continue;
|
||||
seen.add(name);
|
||||
options.push({ label: name, value: name });
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
export const buildTDengineStableQueries = (dbName: string): string[] => {
|
||||
const normalized = String(dbName || '').trim();
|
||||
if (!normalized) return ['SHOW STABLES'];
|
||||
|
||||
const quoted = normalized.replace(/`/g, '``');
|
||||
return [
|
||||
'SHOW STABLES',
|
||||
`SHOW STABLES FROM \`${quoted}\``,
|
||||
`SHOW STABLES FROM ${normalized}`,
|
||||
`SHOW ${normalized}.STABLES`,
|
||||
];
|
||||
};
|
||||
Reference in New Issue
Block a user