From 8ac435dc6dc6650dbd39209891d6889f772b85e3 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Mon, 29 Jun 2026 20:43:15 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(sidebar):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=BE=BE=E6=A2=A6=E8=A1=A8=E5=88=86=E7=BB=84=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #412 --- .../SidebarDamengTableCount.test.ts | 23 +++++++ .../sidebar/sidebarMetadataLoaders.ts | 2 +- .../sidebar/useSidebarTreeLoaders.tsx | 65 +++++++++++++++---- 3 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 frontend/src/components/SidebarDamengTableCount.test.ts diff --git a/frontend/src/components/SidebarDamengTableCount.test.ts b/frontend/src/components/SidebarDamengTableCount.test.ts new file mode 100644 index 00000000..4caeea99 --- /dev/null +++ b/frontend/src/components/SidebarDamengTableCount.test.ts @@ -0,0 +1,23 @@ +import { readFileSync } from 'node:fs'; +import { describe, expect, it } from 'vitest'; + +import { buildSidebarTableStatusSQL } from './sidebar/sidebarMetadataLoaders'; + +const loaderSource = readFileSync(new URL('./sidebar/useSidebarTreeLoaders.tsx', import.meta.url), 'utf8'); + +describe('Sidebar Dameng table count regression', () => { + it('keeps oracle-like table metadata schema-aware for schema-grouped sidebars', () => { + const damengSql = buildSidebarTableStatusSQL({ config: { type: 'dameng' } } as any, 'APP'); + const oracleSql = buildSidebarTableStatusSQL({ config: { type: 'oracle' } } as any, 'APP'); + + expect(damengSql).toContain('owner AS schema_name'); + expect(damengSql).toContain('comments AS table_comment'); + expect(oracleSql).toContain('owner AS schema_name'); + + expect(loaderSource).toContain('const tableSchemaMap = new Map();'); + expect(loaderSource).toContain("getCaseInsensitiveValue(row, ['schema_name', 'SCHEMA_NAME', 'owner', 'OWNER'])"); + expect(loaderSource).toContain('const normalizedSchemaRows = schemaRows'); + expect(loaderSource).toContain('normalizedSchemaRows.length !== 1'); + expect(loaderSource).toContain('schemaName: mappedSchemaName'); + }); +}); diff --git a/frontend/src/components/sidebar/sidebarMetadataLoaders.ts b/frontend/src/components/sidebar/sidebarMetadataLoaders.ts index 67d4056b..7013254c 100644 --- a/frontend/src/components/sidebar/sidebarMetadataLoaders.ts +++ b/frontend/src/components/sidebar/sidebarMetadataLoaders.ts @@ -308,7 +308,7 @@ const buildSidebarTableStatusSQL = ( case "dm": { const owner = escapeSQLLiteral(dbName).toUpperCase(); return [ - "SELECT table_name, comments AS table_comment, num_rows AS table_rows", + "SELECT owner AS schema_name, table_name, comments AS table_comment, num_rows AS table_rows", "FROM all_tab_comments JOIN all_tables USING (table_name, owner)", `WHERE owner = '${owner}'`, "ORDER BY table_name", diff --git a/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx b/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx index 0cef9837..33a2da8b 100644 --- a/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx +++ b/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx @@ -487,14 +487,25 @@ export const useSidebarTreeLoaders = ({ : { success: false, data: [] as any[] }; const tableRowCountMap = new Map(); const tableCommentMap = new Map(); - const putTableComment = (rawTableName: string, rawComment: string) => { + const tableSchemaMap = new Map(); + const buildTableMetadataKeys = (rawTableName: string, rawSchemaName = ''): string[] => { const tableName = String(rawTableName || '').trim(); - const comment = String(rawComment || '').trim(); - if (!tableName || !comment) return; - const keys = new Set([tableName.toLowerCase()]); + if (!tableName) return []; const parsed = splitQualifiedName(tableName); - if (parsed.objectName) keys.add(parsed.objectName.toLowerCase()); - keys.forEach((metadataKey) => tableCommentMap.set(metadataKey, comment)); + const schemaName = String(rawSchemaName || parsed.schemaName || '').trim(); + const objectName = String(parsed.objectName || tableName).trim(); + const keys = new Set([tableName.toLowerCase()]); + if (objectName) keys.add(objectName.toLowerCase()); + const qualifiedName = buildQualifiedName(schemaName, objectName || tableName); + if (qualifiedName) keys.add(qualifiedName.toLowerCase()); + return Array.from(keys); + }; + const putTableComment = (rawTableName: string, rawComment: string, rawSchemaName = '') => { + const comment = String(rawComment || '').trim(); + if (!comment) return; + buildTableMetadataKeys(rawTableName, rawSchemaName).forEach((metadataKey) => { + tableCommentMap.set(metadataKey, comment); + }); }; if (tableStatsResult?.success && Array.isArray(tableStatsResult.data)) { tableStatsResult.data.forEach((row: Record) => { @@ -504,6 +515,12 @@ export const useSidebarTreeLoaders = ({ || '' ).trim(); if (!rawTableName) return; + const rawSchemaName = getCaseInsensitiveValue(row, ['schema_name', 'SCHEMA_NAME', 'owner', 'OWNER']); + buildTableMetadataKeys(rawTableName, rawSchemaName).forEach((metadataKey) => { + if (rawSchemaName) { + tableSchemaMap.set(metadataKey, rawSchemaName); + } + }); putTableComment(rawTableName, getCaseInsensitiveValue(row, [ 'table_comment', 'TABLE_COMMENT', @@ -512,15 +529,22 @@ export const useSidebarTreeLoaders = ({ 'comments', 'COMMENTS', 'MS_Description', - ])); + ]), rawSchemaName); const rowCount = parseMetadataRowCount(row); if (rowCount === undefined) return; - tableRowCountMap.set(rawTableName.toLowerCase(), rowCount); + buildTableMetadataKeys(rawTableName, rawSchemaName).forEach((metadataKey) => { + tableRowCountMap.set(metadataKey, rowCount); + }); }); } const tableEntries = tableRows.map((row: any) => { const tableName = Object.values(row)[0] as string; const parsed = splitQualifiedName(tableName); + const metadataKeys = buildTableMetadataKeys(tableName); + const rowSchemaName = getCaseInsensitiveValue(row, ['schema_name', 'SCHEMA_NAME', 'owner', 'OWNER']); + const mappedSchemaName = rowSchemaName + || metadataKeys.map((metadataKey) => tableSchemaMap.get(metadataKey)).find((value): value is string => !!value) + || parsed.schemaName; const rowComment = getCaseInsensitiveValue(row, [ 'table_comment', 'TABLE_COMMENT', @@ -531,12 +555,15 @@ export const useSidebarTreeLoaders = ({ ]); return { tableName, - schemaName: parsed.schemaName, + schemaName: mappedSchemaName, displayName: getSidebarTableDisplayName(conn, tableName), - rowCount: tableRowCountMap.get(String(tableName || '').trim().toLowerCase()), + rowCount: metadataKeys + .map((metadataKey) => tableRowCountMap.get(metadataKey)) + .find((value) => value !== undefined), tableComment: rowComment - || tableCommentMap.get(String(tableName || '').trim().toLowerCase()) - || tableCommentMap.get(String(parsed.objectName || '').trim().toLowerCase()) + || metadataKeys + .map((metadataKey) => tableCommentMap.get(metadataKey)) + .find((value) => !!value) || '', }; }); @@ -559,6 +586,18 @@ export const useSidebarTreeLoaders = ({ const packageRows: any[] = Array.isArray(packagesResult.packages) ? packagesResult.packages : []; const eventRows: any[] = Array.isArray(eventsResult.events) ? eventsResult.events : []; const schemaRows: string[] = Array.isArray(schemasResult.schemas) ? schemasResult.schemas : []; + const normalizedSchemaRows = schemaRows + .map((schemaName) => String(schemaName || '').trim()) + .filter((schemaName) => schemaName !== ''); + const normalizedTableEntries = tableEntries.map((entry) => { + if (entry.schemaName || normalizedSchemaRows.length !== 1) { + return entry; + } + return { + ...entry, + schemaName: normalizedSchemaRows[0], + }; + }); const viewEntries = viewRows.map((entry: SidebarViewMetadataEntry) => { const parsed = splitQualifiedName(entry.viewName); @@ -666,7 +705,7 @@ export const useSidebarTreeLoaders = ({ const sortPreferenceKey = `${conn.id}-${conn.dbName}`; const sortBy = currentTableSortPreference[sortPreferenceKey] || 'name'; - const sortedTableEntries = sortSidebarTableEntries(tableEntries, { + const sortedTableEntries = sortSidebarTableEntries(normalizedTableEntries, { connectionId: conn.id, dbName: conn.dbName, sortBy,