fix(oracle): refresh table hover metadata from local index

Closes #8056
This commit is contained in:
t8y2 committed 2026-09-04 00:44:21 +08:00
1 parent fe955da729
commit 37dc74d9de
4 files changed
+109 -10

No files matched your search

@@ -1,7 +1,7 @@
import { createPinia, setActivePinia } from "pinia";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { getSqlCompletionContext } from "@/lib/sql/sqlCompletion";
import type { ConnectionConfig } from "@/types/database";
import type { ConnectionConfig, TreeNode } from "@/types/database";
function installLocalStorage() {
const data = new Map<string, string>();
@@ -471,6 +471,85 @@ describe("connectionStore completion assistant", () => {
expect(store.lookupLocalCompletionTables("oracle-1", "ORCL", "", 20, "scott")).toEqual(tables);
});
it("reconciles a non-empty filtered cache with a table added to the sidebar tree", async () => {
const completionAssistantSearch = vi.fn().mockResolvedValue({
candidates: [{ name: "REPORT_0001", kind: "table", schema: "APP" }],
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();
store.connections = [oracleConnection()];
store.connectedIds.add("oracle-1");
const first = await store.listCompletionTables("oracle-1", "ORCL", "REPORT", 200, "APP");
const assistantCallsBeforeSidebarLoad = completionAssistantSearch.mock.calls.length;
store.treeNodes = [
{
id: "oracle-1:ORCL:APP:tables",
label: "Tables",
type: "group-tables",
connectionId: "oracle-1",
database: "ORCL",
schema: "APP",
children: [
{ id: "report-0001", label: "REPORT_0001", type: "table", connectionId: "oracle-1", database: "ORCL", schema: "APP" },
{ id: "report-1001", label: "REPORT_1001", type: "table", connectionId: "oracle-1", database: "ORCL", schema: "APP" },
{ id: "other-schema", label: "REPORT_1001", type: "table", connectionId: "oracle-1", database: "ORCL", schema: "OTHER" },
],
},
] as TreeNode[];
const merged = await store.listCompletionTables("oracle-1", "ORCL", "REPORT", 200, "APP");
expect(first).toEqual([expect.objectContaining({ name: "REPORT_0001", schema: "APP" })]);
expect(merged.map((table) => table.name)).toEqual(["REPORT_0001", "REPORT_1001"]);
expect(merged.every((table) => table.schema === "APP")).toBe(true);
expect(completionAssistantSearch).toHaveBeenCalledTimes(assistantCallsBeforeSidebarLoad);
});
it("reconciles an empty filtered cache after the sidebar loads a late table", async () => {
const completionAssistantSearch = vi.fn().mockResolvedValue({ candidates: [], 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();
store.connections = [oracleConnection()];
store.connectedIds.add("oracle-1");
const first = await store.listCompletionTables("oracle-1", "ORCL", "REPORT_1001", 200, "APP");
const assistantCallsBeforeSidebarLoad = completionAssistantSearch.mock.calls.length;
store.treeNodes = [
{
id: "oracle-1:ORCL:APP:tables",
label: "Tables",
type: "group-tables",
connectionId: "oracle-1",
database: "ORCL",
schema: "APP",
children: [{ id: "report-1001", label: "REPORT_1001", type: "table", connectionId: "oracle-1", database: "ORCL", schema: "APP" }],
},
] as TreeNode[];
const merged = await store.listCompletionTables("oracle-1", "ORCL", "REPORT_1001", 200, "APP");
expect(first).toEqual([]);
expect(merged).toEqual([expect.objectContaining({ name: "REPORT_1001", schema: "APP", type: "table" })]);
expect(completionAssistantSearch).toHaveBeenCalledTimes(assistantCallsBeforeSidebarLoad);
});
it("maps global Oracle tables with safe qualification and schema priority", async () => {
const completionAssistantSearch = vi.fn().mockResolvedValue({
candidates: [
+7 -1
View File
@@ -7711,7 +7711,13 @@ export const useConnectionStore = defineStore("connection", () => {
// case-insensitive because tableMatchScore normalizes internally.
const relaxedFilter = relaxedCompletionTableFilter(trimmedFilter);
const cacheKey = `${connectionId}:${database}:${catalog ?? ""}:${trimmedFilter}:${limit ?? ""}:${schema ?? ""}:${globalSearch ? "global" : "scoped"}:${currentSchema ?? ""}`;
if (completionTablesCache.value[cacheKey]) {
const cachedTables = completionTablesCache.value[cacheKey];
if (cachedTables) {
const localTables = lookupLocalCompletionTables(connectionId, database, trimmedFilter, limit, schema, catalog);
if (localTables.length === 0) return cachedTables;
const mergedTables = dedupeCompletionTables([...localTables, ...cachedTables]);
completionTablesCache.value[cacheKey] = limit ? mergedTables.slice(0, limit) : mergedTables;
indexCompletionTables(connectionId, database, schema, completionTablesCache.value[cacheKey], catalog);
return completionTablesCache.value[cacheKey];
}
const requestRevision = completionCacheRevision(connectionId, database);