mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-12 01:24:12 +08:00
🐛 fix(kingbase): 修复开表与 ER 图加载缓慢问题
- 解耦 Kingbase 首屏查询与编辑定位元数据加载 - 使用单次全库外键快照替代 ER 图逐表扫描 - 补充大规模表结构和异步元数据回归测试 Fixes #731 Fixes #732
This commit is contained in:
@@ -165,6 +165,52 @@ describe('DataViewer safe editing locator', () => {
|
||||
renderer!.unmount();
|
||||
});
|
||||
|
||||
it('does not block the initial Kingbase table query on edit-locator metadata', async () => {
|
||||
storeState.connections[0].config.type = 'kingbase';
|
||||
storeState.connections[0].config.database = 'ldf_server_dbs_dev';
|
||||
|
||||
let resolveColumns!: (value: any) => void;
|
||||
let resolveIndexes!: (value: any) => void;
|
||||
backendApp.DBGetColumns.mockReturnValue(new Promise((resolve) => {
|
||||
resolveColumns = resolve;
|
||||
}));
|
||||
backendApp.DBGetIndexes.mockReturnValue(new Promise((resolve) => {
|
||||
resolveIndexes = resolve;
|
||||
}));
|
||||
|
||||
let renderer: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<DataViewer tab={createTab({
|
||||
id: 'tab-kingbase-fast-open',
|
||||
dbName: 'ldf_server_dbs_dev',
|
||||
tableName: 'ldf_server.andon_dash_events',
|
||||
title: 'andon_dash_events',
|
||||
})} />);
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(backendApp.DBQuery).toHaveBeenCalled();
|
||||
expect(dataGridState.latestProps?.data).toEqual([
|
||||
expect.objectContaining({ ID: 7, NAME: 'old-name' }),
|
||||
]);
|
||||
|
||||
await act(async () => {
|
||||
resolveColumns({
|
||||
success: true,
|
||||
data: [{ name: 'ID', key: 'PRI' }, { name: 'NAME', key: '' }],
|
||||
});
|
||||
resolveIndexes({ success: true, data: [] });
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(dataGridState.latestProps?.editLocator).toMatchObject({
|
||||
strategy: 'primary-key',
|
||||
columns: ['ID'],
|
||||
readOnly: false,
|
||||
});
|
||||
renderer!.unmount();
|
||||
});
|
||||
|
||||
it('enables table preview editing after primary keys are loaded', async () => {
|
||||
backendApp.DBGetColumns.mockResolvedValue({
|
||||
success: true,
|
||||
|
||||
@@ -644,24 +644,28 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
if (pkKeyRef.current !== locatorKey || !editLocatorForQuery) {
|
||||
pkKeyRef.current = locatorKey;
|
||||
const locatorSeq = ++pkSeqRef.current;
|
||||
try {
|
||||
const [resCols, resIndexes] = await Promise.all([
|
||||
DBGetColumns(buildRpcConnectionConfig(config) as any, dbName, tableName),
|
||||
DBGetIndexes(buildRpcConnectionConfig(config) as any, dbName, tableName)
|
||||
.catch((error: any) => ({ success: false, message: String(error?.message || error || 'Failed to load indexes'), data: [] })),
|
||||
]);
|
||||
if (fetchSeqRef.current !== seq) return;
|
||||
if (pkSeqRef.current !== locatorSeq) return;
|
||||
if (pkKeyRef.current !== locatorKey) return;
|
||||
const loadEditLocator = async (): Promise<{
|
||||
primaryKeys: string[];
|
||||
locator: EditRowLocator;
|
||||
} | null> => {
|
||||
try {
|
||||
const [resCols, resIndexes] = await Promise.all([
|
||||
DBGetColumns(buildRpcConnectionConfig(config) as any, dbName, tableName),
|
||||
DBGetIndexes(buildRpcConnectionConfig(config) as any, dbName, tableName)
|
||||
.catch((error: any) => ({ success: false, message: String(error?.message || error || 'Failed to load indexes'), data: [] })),
|
||||
]);
|
||||
if (fetchSeqRef.current !== seq) return null;
|
||||
if (pkSeqRef.current !== locatorSeq) return null;
|
||||
if (pkKeyRef.current !== locatorKey) return null;
|
||||
|
||||
if (!resCols?.success || !Array.isArray(resCols.data)) {
|
||||
const nextLocator = buildAllColumnsLocator([], { translate: tr });
|
||||
setPkColumns([]);
|
||||
setEditLocator(nextLocator);
|
||||
if (nextLocator.reason) message.info(nextLocator.reason);
|
||||
return { primaryKeys: [], locator: nextLocator };
|
||||
}
|
||||
|
||||
if (!resCols?.success || !Array.isArray(resCols.data)) {
|
||||
const nextLocator = buildAllColumnsLocator([], { translate: tr });
|
||||
pkColumnsForQuery = [];
|
||||
editLocatorForQuery = nextLocator;
|
||||
setPkColumns([]);
|
||||
setEditLocator(nextLocator);
|
||||
if (nextLocator.reason) message.info(nextLocator.reason);
|
||||
} else {
|
||||
const columnDefs = resCols.data as ColumnDefinition[];
|
||||
const primaryKeys = columnDefs
|
||||
.filter((column: any) => getColumnDefinitionKey(column) === 'PRI')
|
||||
@@ -686,8 +690,6 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
translate: tr,
|
||||
}), tr);
|
||||
|
||||
pkColumnsForQuery = primaryKeys;
|
||||
editLocatorForQuery = nextLocator;
|
||||
setPkColumns(primaryKeys);
|
||||
setEditLocator(nextLocator);
|
||||
if (nextLocator.readOnly) {
|
||||
@@ -695,17 +697,28 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
} else if (nextLocator.strategy === 'all-columns' && nextLocator.reason) {
|
||||
message.info(nextLocator.reason);
|
||||
}
|
||||
return { primaryKeys, locator: nextLocator };
|
||||
} catch {
|
||||
if (fetchSeqRef.current !== seq) return null;
|
||||
if (pkSeqRef.current !== locatorSeq) return null;
|
||||
if (pkKeyRef.current !== locatorKey) return null;
|
||||
const nextLocator = buildAllColumnsLocator([], { translate: tr });
|
||||
setPkColumns([]);
|
||||
setEditLocator(nextLocator);
|
||||
if (nextLocator.reason) message.info(nextLocator.reason);
|
||||
return { primaryKeys: [], locator: nextLocator };
|
||||
}
|
||||
} catch {
|
||||
if (fetchSeqRef.current !== seq) return;
|
||||
if (pkSeqRef.current !== locatorSeq) return;
|
||||
if (pkKeyRef.current !== locatorKey) return;
|
||||
const nextLocator = buildAllColumnsLocator([], { translate: tr });
|
||||
pkColumnsForQuery = [];
|
||||
editLocatorForQuery = nextLocator;
|
||||
setPkColumns([]);
|
||||
setEditLocator(nextLocator);
|
||||
if (nextLocator.reason) message.info(nextLocator.reason);
|
||||
};
|
||||
|
||||
if (dbTypeLower === 'kingbase') {
|
||||
// Kingbase catalog metadata can be noticeably slower than the page query.
|
||||
// Keep the grid read-only briefly and enable editing when the locator arrives.
|
||||
void loadEditLocator();
|
||||
} else {
|
||||
const locatorMetadata = await loadEditLocator();
|
||||
if (!locatorMetadata) return;
|
||||
pkColumnsForQuery = locatorMetadata.primaryKeys;
|
||||
editLocatorForQuery = locatorMetadata.locator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { ForeignKeyDefinition } from '../types';
|
||||
import type { ErDiagramTableSnapshot } from './dataGridErDiagramModel';
|
||||
import { collectErDiagramNeighborhood, useDataGridErDiagram } from './useDataGridErDiagram';
|
||||
|
||||
const backendApp = vi.hoisted(() => ({
|
||||
DBGetColumns: vi.fn(),
|
||||
DBGetDatabaseForeignKeys: vi.fn(),
|
||||
DBGetForeignKeys: vi.fn(),
|
||||
DBGetIndexes: vi.fn(),
|
||||
DBGetTables: vi.fn(),
|
||||
@@ -128,12 +130,62 @@ describe('collectErDiagramNeighborhood', () => {
|
||||
);
|
||||
expect(twoHop.canExpandRelations).toBe(false);
|
||||
});
|
||||
|
||||
it('uses a prefetched foreign-key snapshot instead of scanning every table', async () => {
|
||||
const unrelatedTables = Array.from({ length: 500 }, (_, index) => `unrelated_${index}`);
|
||||
const schemaTableNames = ['orders', 'customers', 'order_items', ...unrelatedTables];
|
||||
const loadSnapshot = vi.fn(async (tableName: string) => ({
|
||||
tableName,
|
||||
columns: [],
|
||||
foreignKeys: [],
|
||||
uniqueKeyGroups: [],
|
||||
}));
|
||||
const loadForeignKeys = vi.fn(async () => []);
|
||||
const prefetchedForeignKeysByTable = new Map<string, ForeignKeyDefinition[]>(
|
||||
schemaTableNames.map((tableName) => [tableName, []]),
|
||||
);
|
||||
prefetchedForeignKeysByTable.set('orders', [{
|
||||
name: 'fk_orders_customer',
|
||||
columnName: 'customer_id',
|
||||
refTableName: 'customers',
|
||||
refColumnName: 'id',
|
||||
constraintName: 'fk_orders_customer',
|
||||
}]);
|
||||
prefetchedForeignKeysByTable.set('order_items', [{
|
||||
name: 'fk_items_order',
|
||||
columnName: 'order_id',
|
||||
refTableName: 'orders',
|
||||
refColumnName: 'id',
|
||||
constraintName: 'fk_items_order',
|
||||
}]);
|
||||
|
||||
const result = await collectErDiagramNeighborhood({
|
||||
currentSnapshot: {
|
||||
tableName: 'orders',
|
||||
columns: [],
|
||||
foreignKeys: [],
|
||||
uniqueKeyGroups: [],
|
||||
},
|
||||
schemaTableNames,
|
||||
relationDepth: 1,
|
||||
loadSnapshot,
|
||||
loadForeignKeys,
|
||||
resolveTableName: (tableName) => tableName,
|
||||
prefetchedForeignKeysByTable,
|
||||
});
|
||||
|
||||
expect(result.relations.map((relation) => `${relation.sourceTableName}->${relation.targetTableName}`)).toEqual(
|
||||
expect.arrayContaining(['orders->customers', 'order_items->orders']),
|
||||
);
|
||||
expect(loadForeignKeys).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDataGridErDiagram cache invalidation', () => {
|
||||
beforeEach(() => {
|
||||
Object.values(backendApp).forEach((mock) => mock.mockReset());
|
||||
backendApp.DBGetColumns.mockResolvedValue({ success: true, data: [] });
|
||||
backendApp.DBGetDatabaseForeignKeys.mockResolvedValue({ success: true, data: {} });
|
||||
backendApp.DBGetForeignKeys.mockResolvedValue({ success: true, data: [] });
|
||||
backendApp.DBGetIndexes.mockResolvedValue({ success: true, data: [] });
|
||||
backendApp.DBGetTables.mockResolvedValue({ success: true, data: [{ table: 'orders' }] });
|
||||
@@ -201,4 +253,50 @@ describe('useDataGridErDiagram cache invalidation', () => {
|
||||
renderer?.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
it('loads one Kingbase foreign-key snapshot instead of querying every table', async () => {
|
||||
const unrelatedTables = Array.from({ length: 500 }, (_, index) => ({
|
||||
table: `ldf_server.unrelated_${index}`,
|
||||
}));
|
||||
backendApp.DBGetTables.mockResolvedValue({
|
||||
success: true,
|
||||
data: [{ table: 'ldf_server.orders' }, ...unrelatedTables],
|
||||
});
|
||||
|
||||
let controller: ReturnType<typeof useDataGridErDiagram> | null = null;
|
||||
let renderer: ReactTestRenderer | null = null;
|
||||
const params = {
|
||||
connections: [{
|
||||
id: 'kingbase-er-snapshot-test',
|
||||
config: {
|
||||
type: 'kingbase',
|
||||
host: '127.0.0.1',
|
||||
port: 54321,
|
||||
database: 'ldf_server_dbs_dev',
|
||||
},
|
||||
}],
|
||||
connectionId: 'kingbase-er-snapshot-test',
|
||||
dbName: 'ldf_server_dbs_dev',
|
||||
tableName: 'ldf_server.orders',
|
||||
};
|
||||
const Harness = () => {
|
||||
controller = useDataGridErDiagram(params);
|
||||
return null;
|
||||
};
|
||||
|
||||
await act(async () => {
|
||||
renderer = create(React.createElement(Harness));
|
||||
await vi.waitFor(() => {
|
||||
expect(controller?.loading).toBe(false);
|
||||
expect(controller?.graph).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
expect(backendApp.DBGetDatabaseForeignKeys).toHaveBeenCalledTimes(1);
|
||||
expect(backendApp.DBGetForeignKeys).not.toHaveBeenCalled();
|
||||
|
||||
act(() => {
|
||||
renderer?.unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { DBGetColumns, DBGetForeignKeys, DBGetIndexes, DBGetTables } from '../../wailsjs/go/app/App';
|
||||
import {
|
||||
DBGetColumns,
|
||||
DBGetDatabaseForeignKeys,
|
||||
DBGetForeignKeys,
|
||||
DBGetIndexes,
|
||||
DBGetTables,
|
||||
} from '../../wailsjs/go/app/App';
|
||||
import type { ColumnDefinition, ForeignKeyDefinition } from '../types';
|
||||
import { createBoundedAsyncCache } from '../utils/boundedAsyncCache';
|
||||
import { buildRpcConnectionConfig } from '../utils/connectionRpcConfig';
|
||||
import { normalizeColumnDefinitions } from '../utils/columnDefinition';
|
||||
import { resolveDataSourceType } from '../utils/dataSourceCapabilities';
|
||||
import { resolveUniqueKeyGroupsFromIndexes } from './dataGridCopyInsert';
|
||||
import {
|
||||
buildErDiagramGraph,
|
||||
@@ -38,6 +45,7 @@ const ER_SCHEMA_CACHE_MAX_ENTRIES = 32;
|
||||
const ER_TABLE_METADATA_CACHE_MAX_ENTRIES = 256;
|
||||
|
||||
const schemaTableNamesCache = createBoundedAsyncCache<string[]>(ER_SCHEMA_CACHE_MAX_ENTRIES);
|
||||
const databaseForeignKeysCache = createBoundedAsyncCache<Map<string, ForeignKeyDefinition[]>>(ER_SCHEMA_CACHE_MAX_ENTRIES);
|
||||
const tableColumnsCache = createBoundedAsyncCache<ColumnDefinition[]>(ER_TABLE_METADATA_CACHE_MAX_ENTRIES);
|
||||
const tableForeignKeysCache = createBoundedAsyncCache<ForeignKeyDefinition[]>(ER_TABLE_METADATA_CACHE_MAX_ENTRIES);
|
||||
const tableUniqueKeyGroupsCache = createBoundedAsyncCache<string[][]>(ER_TABLE_METADATA_CACHE_MAX_ENTRIES);
|
||||
@@ -62,7 +70,13 @@ const normalizeConnectionConfig = (connection: any) => ({
|
||||
});
|
||||
|
||||
const invalidateCacheByPrefix = (prefix: string) => {
|
||||
[schemaTableNamesCache, tableColumnsCache, tableForeignKeysCache, tableUniqueKeyGroupsCache].forEach((cache) => {
|
||||
[
|
||||
schemaTableNamesCache,
|
||||
databaseForeignKeysCache,
|
||||
tableColumnsCache,
|
||||
tableForeignKeysCache,
|
||||
tableUniqueKeyGroupsCache,
|
||||
].forEach((cache) => {
|
||||
cache.invalidatePrefix(prefix);
|
||||
});
|
||||
};
|
||||
@@ -139,6 +153,27 @@ const loadTableForeignKeys = async (
|
||||
return normalizeForeignKeyDefinitions(response.data);
|
||||
});
|
||||
|
||||
const loadDatabaseForeignKeys = async (
|
||||
config: any,
|
||||
dbName: string,
|
||||
cacheKey: string,
|
||||
): Promise<Map<string, ForeignKeyDefinition[]>> => databaseForeignKeysCache.getOrLoad(cacheKey, async () => {
|
||||
const response = await DBGetDatabaseForeignKeys(buildRpcConnectionConfig(config) as any, dbName);
|
||||
if (!response?.success || !response.data || typeof response.data !== 'object' || Array.isArray(response.data)) {
|
||||
throw new Error(response?.message || 'Failed to load database foreign keys');
|
||||
}
|
||||
|
||||
const result = new Map<string, ForeignKeyDefinition[]>();
|
||||
Object.entries(response.data as Record<string, unknown>).forEach(([sourceTableName, rawForeignKeys]) => {
|
||||
const key = normalizeErQualifiedName(sourceTableName);
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
result.set(key, normalizeForeignKeyDefinitions(rawForeignKeys));
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
const loadTableUniqueKeyGroups = async (
|
||||
config: any,
|
||||
dbName: string,
|
||||
@@ -157,10 +192,12 @@ const loadTableSnapshot = async (
|
||||
dbName: string,
|
||||
tableName: string,
|
||||
tableCacheKey: string,
|
||||
prefetchedForeignKeys?: Promise<ForeignKeyDefinition[]>,
|
||||
): Promise<ErDiagramTableSnapshot> => {
|
||||
const [columnsResult, foreignKeysResult, uniqueKeyGroupsResult] = await Promise.allSettled([
|
||||
loadTableColumns(config, dbName, tableName, `${tableCacheKey}|columns`),
|
||||
loadTableForeignKeys(config, dbName, tableName, `${tableCacheKey}|foreignKeys`),
|
||||
prefetchedForeignKeys
|
||||
|| loadTableForeignKeys(config, dbName, tableName, `${tableCacheKey}|foreignKeys`),
|
||||
loadTableUniqueKeyGroups(config, dbName, tableName, `${tableCacheKey}|uniqueKeys`),
|
||||
]);
|
||||
|
||||
@@ -225,6 +262,7 @@ type CollectErDiagramNeighborhoodParams = {
|
||||
loadSnapshot: (tableName: string) => Promise<ErDiagramTableSnapshot>;
|
||||
loadForeignKeys: (tableName: string) => Promise<ForeignKeyDefinition[]>;
|
||||
resolveTableName: (tableName: string) => string;
|
||||
prefetchedForeignKeysByTable?: ReadonlyMap<string, ForeignKeyDefinition[]>;
|
||||
};
|
||||
|
||||
type CollectErDiagramNeighborhoodResult = {
|
||||
@@ -263,14 +301,25 @@ export const collectErDiagramNeighborhood = async (
|
||||
|
||||
registerTableName(currentTableName);
|
||||
params.schemaTableNames.forEach(registerTableName);
|
||||
params.prefetchedForeignKeysByTable?.forEach((foreignKeys, tableName) => {
|
||||
const actualTableName = registerTableName(tableName);
|
||||
const tableKey = normalizeErQualifiedName(actualTableName);
|
||||
if (tableKey) {
|
||||
foreignKeysByKey.set(tableKey, foreignKeys);
|
||||
}
|
||||
});
|
||||
params.currentSnapshot.foreignKeys.forEach((foreignKey) => {
|
||||
registerRelationTarget(foreignKey.refTableName);
|
||||
});
|
||||
const currentForeignKeys = foreignKeysByKey.has(currentKey)
|
||||
? foreignKeysByKey.get(currentKey) || []
|
||||
: params.currentSnapshot.foreignKeys || [];
|
||||
snapshotByKey.set(currentKey, {
|
||||
...params.currentSnapshot,
|
||||
tableName: registerTableName(params.currentSnapshot.tableName),
|
||||
foreignKeys: currentForeignKeys,
|
||||
});
|
||||
foreignKeysByKey.set(currentKey, params.currentSnapshot.foreignKeys || []);
|
||||
foreignKeysByKey.set(currentKey, currentForeignKeys);
|
||||
visitedKeys.add(currentKey);
|
||||
|
||||
const loadSnapshotByKey = async (tableKey: string): Promise<ErDiagramTableSnapshot> => {
|
||||
@@ -284,14 +333,18 @@ export const collectErDiagramNeighborhood = async (
|
||||
const snapshot = await params.loadSnapshot(tableName);
|
||||
const actualTableName = registerTableName(snapshot.tableName || tableName);
|
||||
const normalizedActualTableName = normalizeErQualifiedName(actualTableName);
|
||||
const snapshotForeignKeys = foreignKeysByKey.has(tableKey)
|
||||
? foreignKeysByKey.get(tableKey) || []
|
||||
: snapshot.foreignKeys || [];
|
||||
const nextSnapshot = {
|
||||
...snapshot,
|
||||
tableName: actualTableName,
|
||||
foreignKeys: snapshotForeignKeys,
|
||||
};
|
||||
snapshotByKey.set(tableKey, nextSnapshot);
|
||||
foreignKeysByKey.set(tableKey, nextSnapshot.foreignKeys || []);
|
||||
foreignKeysByKey.set(tableKey, snapshotForeignKeys);
|
||||
snapshotByKey.set(normalizedActualTableName, nextSnapshot);
|
||||
foreignKeysByKey.set(normalizedActualTableName, nextSnapshot.foreignKeys || []);
|
||||
foreignKeysByKey.set(normalizedActualTableName, snapshotForeignKeys);
|
||||
return nextSnapshot;
|
||||
} catch {
|
||||
warningCount += 1;
|
||||
@@ -506,7 +559,9 @@ export const useDataGridErDiagram = (params: DataGridErDiagramParams) => {
|
||||
const seq = ++requestSeqRef.current;
|
||||
const config = normalizeConnectionConfig(connection);
|
||||
const schemaCacheKey = `${cachePrefix}schemaTables`;
|
||||
const databaseForeignKeysCacheKey = `${cachePrefix}databaseForeignKeys`;
|
||||
const currentTableCacheKey = `${cachePrefix}${normalizedTableName}`;
|
||||
const isKingbase = resolveDataSourceType(config) === 'kingbase';
|
||||
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
@@ -520,7 +575,31 @@ export const useDataGridErDiagram = (params: DataGridErDiagramParams) => {
|
||||
|
||||
const loadGraph = async () => {
|
||||
let warningCount = 0;
|
||||
const currentSnapshot = await loadTableSnapshot(config, normalizedDbName, normalizedTableName, currentTableCacheKey);
|
||||
const databaseForeignKeysResultPromise = isKingbase
|
||||
? loadDatabaseForeignKeys(config, normalizedDbName, databaseForeignKeysCacheKey)
|
||||
.then((value) => ({ value, failed: false as const }))
|
||||
.catch(() => ({ value: null, failed: true as const }))
|
||||
: null;
|
||||
const prefetchedCurrentForeignKeys = databaseForeignKeysResultPromise
|
||||
? databaseForeignKeysResultPromise.then((result) => {
|
||||
if (result.value) {
|
||||
return result.value.get(normalizeErQualifiedName(normalizedTableName)) || [];
|
||||
}
|
||||
return loadTableForeignKeys(
|
||||
config,
|
||||
normalizedDbName,
|
||||
normalizedTableName,
|
||||
`${currentTableCacheKey}|foreignKeys`,
|
||||
);
|
||||
})
|
||||
: undefined;
|
||||
const currentSnapshot = await loadTableSnapshot(
|
||||
config,
|
||||
normalizedDbName,
|
||||
normalizedTableName,
|
||||
currentTableCacheKey,
|
||||
prefetchedCurrentForeignKeys,
|
||||
);
|
||||
|
||||
let schemaTableNames = [currentSnapshot.tableName];
|
||||
try {
|
||||
@@ -536,6 +615,23 @@ export const useDataGridErDiagram = (params: DataGridErDiagramParams) => {
|
||||
]);
|
||||
|
||||
const resolveTableName = (name: string) => resolveErActualTableName(name, resolvedSchemaTableNames);
|
||||
let prefetchedForeignKeysByTable: Map<string, ForeignKeyDefinition[]> | undefined;
|
||||
if (databaseForeignKeysResultPromise) {
|
||||
const databaseForeignKeysResult = await databaseForeignKeysResultPromise;
|
||||
prefetchedForeignKeysByTable = new Map(
|
||||
resolvedSchemaTableNames.map((name) => [normalizeErQualifiedName(name), []]),
|
||||
);
|
||||
databaseForeignKeysResult.value?.forEach((foreignKeys, tableKey) => {
|
||||
prefetchedForeignKeysByTable?.set(tableKey, foreignKeys);
|
||||
});
|
||||
prefetchedForeignKeysByTable.set(
|
||||
normalizeErQualifiedName(currentSnapshot.tableName),
|
||||
currentSnapshot.foreignKeys,
|
||||
);
|
||||
if (databaseForeignKeysResult.failed) {
|
||||
warningCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const neighborhood = await collectErDiagramNeighborhood({
|
||||
currentSnapshot,
|
||||
@@ -543,13 +639,25 @@ export const useDataGridErDiagram = (params: DataGridErDiagramParams) => {
|
||||
relationDepth,
|
||||
loadSnapshot: async (relatedTableName) => {
|
||||
const tableCacheKey = `${cachePrefix}${relatedTableName}`;
|
||||
return loadTableSnapshot(config, normalizedDbName, relatedTableName, tableCacheKey);
|
||||
const prefetchedForeignKeys = prefetchedForeignKeysByTable
|
||||
? Promise.resolve(
|
||||
prefetchedForeignKeysByTable.get(normalizeErQualifiedName(relatedTableName)) || [],
|
||||
)
|
||||
: undefined;
|
||||
return loadTableSnapshot(
|
||||
config,
|
||||
normalizedDbName,
|
||||
relatedTableName,
|
||||
tableCacheKey,
|
||||
prefetchedForeignKeys,
|
||||
);
|
||||
},
|
||||
loadForeignKeys: async (relatedTableName) => {
|
||||
const tableCacheKey = `${cachePrefix}${relatedTableName}`;
|
||||
return loadTableForeignKeys(config, normalizedDbName, relatedTableName, `${tableCacheKey}|foreignKeys`);
|
||||
},
|
||||
resolveTableName,
|
||||
prefetchedForeignKeysByTable,
|
||||
});
|
||||
warningCount += neighborhood.warningCount;
|
||||
|
||||
|
||||
@@ -420,6 +420,7 @@ if (
|
||||
DBGetDatabases: async () => ({ success: true, data: ['missav_bot'] }),
|
||||
DBGetTables: async () => ({ success: true, data: cloneBrowserMockValue(mockQueryTables) }),
|
||||
DBGetAllColumns: async () => ({ success: true, data: cloneBrowserMockValue(mockQueryColumns) }),
|
||||
DBGetDatabaseForeignKeys: async () => ({ success: true, data: {} }),
|
||||
DBGetColumns: async (_config: any, _dbName: string, tableName: string) => ({
|
||||
success: true,
|
||||
data: cloneBrowserMockValue(
|
||||
|
||||
2
frontend/wailsjs/go/app/App.d.ts
vendored
2
frontend/wailsjs/go/app/App.d.ts
vendored
@@ -62,6 +62,8 @@ export function DBGetColumns(arg1:connection.ConnectionConfig,arg2:string,arg3:s
|
||||
|
||||
export function DBGetDatabases(arg1:connection.ConnectionConfig):Promise<connection.QueryResult>;
|
||||
|
||||
export function DBGetDatabaseForeignKeys(arg1:connection.ConnectionConfig,arg2:string):Promise<connection.QueryResult>;
|
||||
|
||||
export function DBGetForeignKeys(arg1:connection.ConnectionConfig,arg2:string,arg3:string):Promise<connection.QueryResult>;
|
||||
|
||||
export function DBGetIndexes(arg1:connection.ConnectionConfig,arg2:string,arg3:string):Promise<connection.QueryResult>;
|
||||
|
||||
@@ -110,6 +110,10 @@ export function DBGetDatabases(arg1) {
|
||||
return window['go']['app']['App']['DBGetDatabases'](arg1);
|
||||
}
|
||||
|
||||
export function DBGetDatabaseForeignKeys(arg1, arg2) {
|
||||
return window['go']['app']['App']['DBGetDatabaseForeignKeys'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function DBGetForeignKeys(arg1, arg2, arg3) {
|
||||
return window['go']['app']['App']['DBGetForeignKeys'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user