mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-15 02:49:49 +08:00
🐛 fix(sidebar): 修复视图裸名定位失败
This commit is contained in:
@@ -867,6 +867,30 @@ describe('QueryEditor external SQL save', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('prefers the unique schema-qualified view target when metadata also contains a bare view name', () => {
|
||||
const views = [
|
||||
{ dbName: 'SYSDBA', viewName: 'V_ACCOUNT', schemaName: undefined },
|
||||
{ dbName: 'SYSDBA', viewName: 'SYSDBA.V_ACCOUNT', schemaName: 'SYSDBA' },
|
||||
];
|
||||
|
||||
expect(resolveQueryEditorNavigationTarget(
|
||||
'select * from V_ACCOUNT',
|
||||
'select * from V_ACCOUNT'.length + 1,
|
||||
'SYSDBA',
|
||||
['SYSDBA'],
|
||||
[],
|
||||
views,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
)).toEqual({
|
||||
type: 'view',
|
||||
dbName: 'SYSDBA',
|
||||
viewName: 'SYSDBA.V_ACCOUNT',
|
||||
schemaName: 'SYSDBA',
|
||||
});
|
||||
});
|
||||
|
||||
it('opens a table tab on ctrl left click inside the editor', async () => {
|
||||
editorState.value = 'select * from analytics.events where id = 1';
|
||||
autoFetchState.visible = true;
|
||||
|
||||
@@ -1432,6 +1432,16 @@ export const resolveQueryEditorNavigationTarget = (
|
||||
&& meta.normalizedRawObjectName === exactQualifiedName
|
||||
);
|
||||
if (exact) {
|
||||
if (!normalizedSchemaName && !exact.normalizedSchemaName) {
|
||||
const schemaQualifiedMatches = metas.filter((meta) =>
|
||||
meta.normalizedDbName === normalizedDbName
|
||||
&& meta.normalizedObjectName === normalizedObjectName
|
||||
&& Boolean(meta.normalizedSchemaName)
|
||||
);
|
||||
if (schemaQualifiedMatches.length === 1) {
|
||||
return schemaQualifiedMatches[0];
|
||||
}
|
||||
}
|
||||
return exact;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,114 @@ describe('sidebarLocate', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('finds a unique schema-qualified view when the locate request only has the view name', () => {
|
||||
const target = resolveSidebarLocateTarget({
|
||||
tabId: 'conn-1-SYSDBA-view-V_ACCOUNT',
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'SYSDBA',
|
||||
tableName: 'V_ACCOUNT',
|
||||
objectGroup: 'views',
|
||||
}, { groupBySchema: true });
|
||||
|
||||
const tree = [
|
||||
{
|
||||
key: 'conn-1',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA',
|
||||
dataRef: { id: 'conn-1', dbName: 'SYSDBA' },
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-schema-SYSDBA',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-schema-SYSDBA-views',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-view-SYSDBA.V_ACCOUNT',
|
||||
type: 'view',
|
||||
dataRef: {
|
||||
id: 'conn-1',
|
||||
dbName: 'SYSDBA',
|
||||
viewName: 'SYSDBA.V_ACCOUNT',
|
||||
schemaName: 'SYSDBA',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
expect(findSidebarNodePathForLocate(tree, target)).toEqual([
|
||||
'conn-1',
|
||||
'conn-1-SYSDBA',
|
||||
'conn-1-SYSDBA-schema-SYSDBA',
|
||||
'conn-1-SYSDBA-schema-SYSDBA-views',
|
||||
'conn-1-SYSDBA-view-SYSDBA.V_ACCOUNT',
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not guess a schema-qualified view when an unqualified locate request is ambiguous', () => {
|
||||
const target = resolveSidebarLocateTarget({
|
||||
tabId: 'conn-1-SYSDBA-view-V_ACCOUNT',
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'SYSDBA',
|
||||
tableName: 'V_ACCOUNT',
|
||||
objectGroup: 'views',
|
||||
}, { groupBySchema: true });
|
||||
|
||||
const tree = [
|
||||
{
|
||||
key: 'conn-1',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA',
|
||||
dataRef: { id: 'conn-1', dbName: 'SYSDBA' },
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-schema-SYSDBA',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-schema-SYSDBA-views',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-view-SYSDBA.V_ACCOUNT',
|
||||
type: 'view',
|
||||
dataRef: { id: 'conn-1', dbName: 'SYSDBA', viewName: 'SYSDBA.V_ACCOUNT', schemaName: 'SYSDBA' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'conn-1-SYSDBA-schema-REPORT',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-schema-REPORT-views',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-SYSDBA-view-REPORT.V_ACCOUNT',
|
||||
type: 'view',
|
||||
dataRef: { id: 'conn-1', dbName: 'SYSDBA', viewName: 'REPORT.V_ACCOUNT', schemaName: 'REPORT' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
expect(findSidebarNodePathForLocate(tree, target)).toBeNull();
|
||||
});
|
||||
|
||||
it('finds external SQL file paths from loaded tree data', () => {
|
||||
const target = resolveSidebarLocateTarget({
|
||||
filePath: 'C:\\Users\\me\\sql\\report.sql',
|
||||
|
||||
@@ -249,7 +249,12 @@ export const findSidebarNodePathByKey = (
|
||||
return null;
|
||||
};
|
||||
|
||||
const matchesLocateObjectName = (target: SidebarLocateTarget, nodeObjectName: string, nodeSchemaName: string): boolean => {
|
||||
const matchesLocateObjectName = (
|
||||
target: SidebarLocateTarget,
|
||||
nodeObjectName: string,
|
||||
nodeSchemaName: string,
|
||||
options: { allowUnqualifiedSchemaMatch?: boolean } = {},
|
||||
): boolean => {
|
||||
const normalizedNodeName = toTrimmedString(nodeObjectName);
|
||||
if (!normalizedNodeName) return false;
|
||||
|
||||
@@ -264,6 +269,9 @@ const matchesLocateObjectName = (target: SidebarLocateTarget, nodeObjectName: st
|
||||
if (normalize(normalizedNodeName) === normalize(target.tableName)) return true;
|
||||
|
||||
if (!resolvedTargetSchema) {
|
||||
if (options.allowUnqualifiedSchemaMatch) {
|
||||
return normalize(nodeObject) === normalize(targetObject);
|
||||
}
|
||||
return !resolvedNodeSchema && normalize(nodeObject) === normalize(targetObject);
|
||||
}
|
||||
|
||||
@@ -271,7 +279,11 @@ const matchesLocateObjectName = (target: SidebarLocateTarget, nodeObjectName: st
|
||||
&& normalize(nodeObject) === normalize(targetObject);
|
||||
};
|
||||
|
||||
const matchesLocateObjectNode = (node: SidebarLocateTreeNodeLike, target: SidebarLocateTarget): boolean => {
|
||||
const matchesLocateObjectNode = (
|
||||
node: SidebarLocateTreeNodeLike,
|
||||
target: SidebarLocateTarget,
|
||||
options: { allowUnqualifiedSchemaMatch?: boolean } = {},
|
||||
): boolean => {
|
||||
const dataRef = node.dataRef || {};
|
||||
|
||||
if (target.objectGroup === 'externalSqlFiles') {
|
||||
@@ -288,26 +300,72 @@ const matchesLocateObjectNode = (node: SidebarLocateTreeNodeLike, target: Sideba
|
||||
|
||||
if (target.objectGroup === 'views') {
|
||||
if (node.type !== 'view') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.viewName || dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.viewName || dataRef.tableName), toTrimmedString(dataRef.schemaName), options);
|
||||
}
|
||||
|
||||
if (target.objectGroup === 'materializedViews') {
|
||||
if (node.type !== 'materialized-view') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.viewName || dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.viewName || dataRef.tableName), toTrimmedString(dataRef.schemaName), options);
|
||||
}
|
||||
|
||||
if (target.objectGroup === 'triggers') {
|
||||
if (node.type !== 'db-trigger') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.triggerName || dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.triggerName || dataRef.tableName), toTrimmedString(dataRef.schemaName), options);
|
||||
}
|
||||
|
||||
if (target.objectGroup === 'routines') {
|
||||
if (node.type !== 'routine') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.routineName || dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.routineName || dataRef.tableName), toTrimmedString(dataRef.schemaName), options);
|
||||
}
|
||||
|
||||
if (node.type !== 'table') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.tableName), toTrimmedString(dataRef.schemaName), options);
|
||||
};
|
||||
|
||||
const findSidebarNodePathForLocateByObject = (
|
||||
nodes: SidebarLocateTreeNodeLike[],
|
||||
target: SidebarLocateTarget,
|
||||
options: { allowUnqualifiedSchemaMatch?: boolean } = {},
|
||||
): string[] | null => {
|
||||
for (const node of nodes) {
|
||||
const nodeKey = String(node.key);
|
||||
if (matchesLocateObjectNode(node, target, options)) {
|
||||
return [nodeKey];
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
const childPath = findSidebarNodePathForLocateByObject(node.children, target, options);
|
||||
if (childPath) {
|
||||
return [nodeKey, ...childPath];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const collectSidebarNodePathsForLocateByObject = (
|
||||
nodes: SidebarLocateTreeNodeLike[],
|
||||
target: SidebarLocateTarget,
|
||||
options: { allowUnqualifiedSchemaMatch?: boolean } = {},
|
||||
ancestorPath: string[] = [],
|
||||
): string[][] => {
|
||||
const paths: string[][] = [];
|
||||
for (const node of nodes) {
|
||||
const nodeKey = String(node.key);
|
||||
const path = [...ancestorPath, nodeKey];
|
||||
if (matchesLocateObjectNode(node, target, options)) {
|
||||
paths.push(path);
|
||||
}
|
||||
if (node.children) {
|
||||
paths.push(...collectSidebarNodePathsForLocateByObject(node.children, target, options, path));
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
};
|
||||
|
||||
const hasLocateTargetSchema = (target: SidebarLocateTarget): boolean => {
|
||||
if (target.objectGroup === 'externalSqlFiles') return true;
|
||||
return Boolean(toTrimmedString(target.schemaName) || splitSidebarQualifiedName(target.tableName).schemaName);
|
||||
};
|
||||
|
||||
export const findSidebarNodePathForLocate = (
|
||||
@@ -317,18 +375,11 @@ export const findSidebarNodePathForLocate = (
|
||||
const exactPath = findSidebarNodePathByKey(nodes, target.targetKey);
|
||||
if (exactPath) return exactPath;
|
||||
|
||||
for (const node of nodes) {
|
||||
const nodeKey = String(node.key);
|
||||
if (matchesLocateObjectNode(node, target)) {
|
||||
return [nodeKey];
|
||||
}
|
||||
const strictPath = findSidebarNodePathForLocateByObject(nodes, target);
|
||||
if (strictPath) return strictPath;
|
||||
|
||||
if (node.children) {
|
||||
const childPath = findSidebarNodePathForLocate(node.children, target);
|
||||
if (childPath) {
|
||||
return [nodeKey, ...childPath];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
if (hasLocateTargetSchema(target)) return null;
|
||||
|
||||
const relaxedPaths = collectSidebarNodePathsForLocateByObject(nodes, target, { allowUnqualifiedSchemaMatch: true });
|
||||
return relaxedPaths.length === 1 ? relaxedPaths[0] : null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user