feat(oracle): expand package members in schema tree

This commit is contained in:
t8y2 committed 2026-08-10 18:15:47 +08:00
1 parent ad9075e18f
commit 585e2f3c50
12 files changed
+508 -75

No files matched your search

@@ -178,7 +178,7 @@ describe("connectionStore metadata loading", () => {
store.connectedIds = new Set([connection.id]);
store.treeNodes = [packageNode];
await store.loadXuguPackageMembers(packageNode);
await store.loadPackageMembers(packageNode);
expect(completionAssistantSearch).toHaveBeenCalledWith({
connection_id: connection.id,
@@ -200,6 +200,84 @@ describe("connectionStore metadata loading", () => {
expect(packageNode.children?.every((child) => child.parentName === "business_api")).toBe(true);
}, 15000);
it("loads Oracle package overloads through the shared package member path", async () => {
const completionAssistantSearch = vi.fn().mockResolvedValue({
candidates: [
{ name: "CALCULATE", kind: "procedure", signature: "P_VALUE IN NUMBER" },
{ name: "CALCULATE", kind: "procedure", signature: "P_VALUE IN VARCHAR2" },
{ name: "ITEM_COUNT", kind: "function", data_type: "NUMBER" },
],
incomplete: false,
fallback_used: false,
});
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
vi.doMock("@/lib/backend/api", () => ({ checkConnectionHealth: vi.fn().mockResolvedValue(undefined), completionAssistantSearch }));
const { useConnectionStore } = await import("@/stores/connectionStore");
const store = useConnectionStore();
const connection = oracleConnection();
const packageNode: TreeNode = {
id: "oracle-1:XE:APP:package:BUSINESS_API",
label: "BUSINESS_API",
type: "package",
connectionId: connection.id,
database: "XE",
schema: "APP",
children: [],
isExpanded: false,
};
store.connections = [connection];
store.connectedIds = new Set([connection.id]);
store.treeNodes = [packageNode];
await store.loadPackageMembers(packageNode);
expect(completionAssistantSearch).toHaveBeenCalledWith({
connection_id: connection.id,
database: "XE",
schema: "APP",
object_kinds: ["routine"],
mask: "",
case_sensitive: true,
global_search: false,
max_results: 1000,
search_in_comments: false,
search_in_definitions: false,
parent_schema: "APP",
parent_name: "BUSINESS_API",
match_mode: "prefix",
});
expect(packageNode.isExpanded).toBe(true);
expect(packageNode.children?.map((child) => child.label)).toEqual(["CALCULATE(P_VALUE IN NUMBER)", "CALCULATE(P_VALUE IN VARCHAR2)", "ITEM_COUNT"]);
}, 15000);
it("does not query package members for unsupported databases", async () => {
const completionAssistantSearch = vi.fn();
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
vi.doMock("@/lib/backend/api", () => ({ checkConnectionHealth: vi.fn().mockResolvedValue(undefined), completionAssistantSearch }));
const { useConnectionStore } = await import("@/stores/connectionStore");
const store = useConnectionStore();
const connection = { ...xuguConnection(), id: "dameng-1", db_type: "dameng" } as ConnectionConfig;
const packageNode: TreeNode = {
id: "dameng-1:APP:package:BUSINESS_API",
label: "BUSINESS_API",
type: "package",
connectionId: connection.id,
database: "APP",
schema: "APP",
children: [],
isExpanded: false,
};
store.connections = [connection];
store.connectedIds = new Set([connection.id]);
await store.loadPackageMembers(packageNode);
expect(completionAssistantSearch).not.toHaveBeenCalled();
expect(packageNode.isExpanded).toBe(false);
}, 15000);
it("loads missing database roots only for connected sidebar search targets", async () => {
const checkConnectionHealth = vi.fn().mockResolvedValue(undefined);
const listDatabases = vi.fn().mockResolvedValue([{ name: "dajia", comment: null }]);
+12 -10
View File
@@ -62,7 +62,7 @@ import { mergeSqlObjectNavigationType, sqlObjectNavigationTypeFromTableType } fr
import * as api from "@/lib/backend/api";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
import { useTunnelProfileStore } from "@/stores/tunnelProfileStore";
import { connectionIsDorisFamilyCatalogCapable, isInternalDorisCatalog, isSchemaAware, normalizeSidebarObjectKind, sidebarObjectKindsForDatabase, usesTreeSchemaMode } from "@/lib/database/databaseCapabilities";
import { connectionIsDorisFamilyCatalogCapable, isInternalDorisCatalog, isSchemaAware, normalizeSidebarObjectKind, sidebarObjectKindsForDatabase, supportsPackageMemberExpansion, usesTreeSchemaMode } from "@/lib/database/databaseCapabilities";
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionShouldDiscoverJdbcSchemas, connectionShouldLoadIdentifierQuote, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, gaussdbIdentifierQuoteOverride } from "@/lib/database/jdbcDialect";
import { buildDatabaseTreeNodes, buildDuckDbConnectionTreeNodes, compareSidebarNames, sortSidebarDatabases, sortSidebarNames, shouldIncludeDefaultDatabaseNode } from "@/lib/database/databaseTree";
import { buildSqlServerDatabaseTreeNodes } from "@/lib/database/sqlServerTree";
@@ -111,7 +111,7 @@ import { appendAgentDriverUpdateHint, hasAgentDriverUpdate, hasInstalledAgentVer
import { appendConnectionErrorHints } from "@/lib/connection/connectionErrorHints";
import { appendVisibleDatabaseSelection } from "@/lib/connection/connectionVisibleDatabases";
import { filterNacosNamespacesForSidebar, normalizeNacosNamespacesForDisplay } from "@/lib/nacos/nacosNamespaceVisibility";
import { buildXuguPackageMemberNodes, markXuguPackageNodesExpandable } from "@/lib/sidebar/xuguPackageMembers";
import { buildPackageMemberNodes, markPackageNodesExpandable } from "@/lib/sidebar/packageMembers";
import { configuredDatabaseProductName, connectionConfigFingerprint, normalizeDatabaseConnectionInfo } from "@/lib/connection/connectionDatabaseInfo";
import { createMetadataLoadTrace, logMetadataLoadTrace, MetadataLoadCoordinator, type MetadataLoadTraceLogger } from "@/lib/metadata/metadataLoadCoordinator";
import type { MetadataScopeInput } from "@/lib/metadata/metadataLoadScope";
@@ -1606,7 +1606,8 @@ export const useConnectionStore = defineStore("connection", () => {
});
const refreshedGroup = grouped.find((group) => group.type === options.node.type);
const children = refreshedGroup?.children ?? [];
return options.node.connectionId && getConfig(options.node.connectionId)?.db_type === "xugu" ? markXuguPackageNodesExpandable(children) : children;
const databaseType = options.node.connectionId ? effectiveDatabaseTypeForConnection(getConfig(options.node.connectionId)) : undefined;
return supportsPackageMemberExpansion(databaseType) ? markPackageNodesExpandable(children) : children;
}
function tableInfosToCompletionTables(tables: readonly TableInfo[], schema?: string): SqlCompletionTable[] {
@@ -1870,8 +1871,8 @@ export const useConnectionStore = defineStore("connection", () => {
schema: options.effectiveSchema,
objects: supplementalObjects,
});
if (getConfig(options.connectionId)?.db_type === "xugu") {
supplementalChildren = markXuguPackageNodesExpandable(supplementalChildren);
if (supportsPackageMemberExpansion(effectiveDatabaseTypeForConnection(getConfig(options.connectionId)))) {
supplementalChildren = markPackageNodesExpandable(supplementalChildren);
}
if (supplementalChildren.length === 0) return;
if (isTreeLoadSearchChanged(searchFilter, options.loadOptions)) return;
@@ -5618,8 +5619,9 @@ export const useConnectionStore = defineStore("connection", () => {
});
}
async function loadXuguPackageMembers(node: TreeNode, options?: LoadTreeOptions): Promise<void> {
if (node.type !== "package" || !node.connectionId || !node.database || getConfig(node.connectionId)?.db_type !== "xugu") return;
async function loadPackageMembers(node: TreeNode, options?: LoadTreeOptions): Promise<void> {
if (node.type !== "package" || !node.connectionId || !node.database) return;
if (!supportsPackageMemberExpansion(effectiveDatabaseTypeForConnection(getConfig(node.connectionId)))) return;
const connectionId = node.connectionId;
const database = node.database;
const schema = node.schema;
@@ -5629,7 +5631,7 @@ export const useConnectionStore = defineStore("connection", () => {
try {
await runTreeMetadataLoad(
{
kind: "xugu-package-members",
kind: "package-members",
connectionId,
database,
schema,
@@ -5656,7 +5658,7 @@ export const useConnectionStore = defineStore("connection", () => {
});
const targetNode = treeNodeLoadTarget(load);
if (!targetNode) return;
setChildren(targetNode, buildXuguPackageMemberNodes(targetNode, response.candidates));
setChildren(targetNode, buildPackageMemberNodes(targetNode, response.candidates));
targetNode.isExpanded = true;
},
options,
@@ -7280,7 +7282,7 @@ export const useConnectionStore = defineStore("connection", () => {
loadTables,
loadTableForLocate,
loadObjectGroupChildren,
loadXuguPackageMembers,
loadPackageMembers,
loadMoreObjectGroupChildren,
loadAllObjectGroupChildren,
loadTableGroups,