From aeab0f8b031a06e9a2f3e5f9bfe048647e4f985e Mon Sep 17 00:00:00 2001 From: Melody Ruch <396921921@qq.com> Date: Sat, 5 Sep 2026 09:04:57 +0800 Subject: [PATCH] fix(completion): resolve Oracle current schema for JDBC star expansion --- .../connectionStore.completion.spec.ts | 43 +++++++++++++++++++ apps/desktop/src/stores/connectionStore.ts | 15 +++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src/stores/__tests__/connectionStore.completion.spec.ts b/apps/desktop/src/stores/__tests__/connectionStore.completion.spec.ts index d8c3aebf42..ec63e24a5f 100644 --- a/apps/desktop/src/stores/__tests__/connectionStore.completion.spec.ts +++ b/apps/desktop/src/stores/__tests__/connectionStore.completion.spec.ts @@ -60,6 +60,19 @@ function oracleConnection(): ConnectionConfig { } as ConnectionConfig; } +function jdbcOracleConnection(): ConnectionConfig { + return { + ...postgresConnection(), + id: "jdbc-oracle-1", + name: "Oracle via JDBC", + db_type: "jdbc", + port: 1521, + username: "APP", + database: "ORCL", + connection_string: "jdbc:oracle:thin:@127.0.0.1:1521/ORCL", + } as ConnectionConfig; +} + function oceanBaseOracleConnection(): ConnectionConfig { return { ...oracleConnection(), @@ -613,6 +626,36 @@ describe("connectionStore completion assistant", () => { expect(store.lookupLocalCompletionColumns("oracle-1", "ORCL", "ORDERS")).toEqual([]); }); + it("lets a JDBC connection to Oracle resolve CURRENT_SCHEMA for unqualified column completion, same as the native Oracle driver", async () => { + const completionAssistantSearch = vi.fn().mockResolvedValue({ + candidates: [], + incomplete: false, + fallback_used: false, + }); + const getColumns = vi.fn().mockResolvedValue([{ name: "REPORT_ID", data_type: "NUMBER", is_nullable: false, column_default: null, is_primary_key: true, extra: null, comment: null }]); + + vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false })); + vi.doMock("@/lib/backend/api", () => ({ + checkConnectionHealth: vi.fn().mockResolvedValue(undefined), + completionAssistantSearch, + getColumns, + })); + + const { useConnectionStore } = await import("@/stores/connectionStore"); + const store = useConnectionStore(); + store.connections = [jdbcOracleConnection()]; + store.connectedIds.add("jdbc-oracle-1"); + + // No schema is selected (undefined) — before the fix this fell through + // both the Oracle current-schema completion path and the schema-required + // guard, silently returning [] without ever calling getColumns. + const columns = await store.listCompletionColumns("jdbc-oracle-1", "ORCL", "ORDERS", undefined, { clientSessionId: "tab-a", version: 0 }); + + expect(completionAssistantSearch).not.toHaveBeenCalled(); + expect(getColumns).toHaveBeenCalledWith("jdbc-oracle-1", "ORCL", "", "ORDERS", undefined, "tab-a"); + expect(columns).toEqual([expect.objectContaining({ name: "REPORT_ID", table: "ORDERS", schema: undefined, dataType: "NUMBER" })]); + }); + it("uses the Dameng login schema for unqualified column completion", async () => { const completionAssistantSearch = vi.fn().mockRejectedValue(new Error("assistant unavailable")); const getColumns = vi.fn().mockResolvedValue([{ name: "ID", data_type: "BIGINT", is_nullable: false, column_default: null, is_primary_key: true, extra: null, comment: null }]); diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts index 6deb046142..f5c0e30c78 100644 --- a/apps/desktop/src/stores/connectionStore.ts +++ b/apps/desktop/src/stores/connectionStore.ts @@ -8094,12 +8094,19 @@ export const useConnectionStore = defineStore("connection", () => { async function listCompletionColumns(connectionId: string, database: string, table: string, schema?: string, context?: { clientSessionId?: string; version?: number; tableQuoted?: boolean; schemaQuoted?: boolean }, catalog?: string): Promise { const config = getConfig(connectionId); - const oracleIdentifier = config?.db_type === "oracle" || config?.db_type === "oceanbase-oracle"; - const uppercaseUnquotedIdentifier = oracleIdentifier || config?.db_type === "saphana"; + // Use the effective database type (e.g. a JDBC connection whose URL is + // `jdbc:oracle:...` resolves to "oracle") rather than the raw db_type. + // Otherwise a JDBC-Oracle connection with no schema selected falls + // through neither the Oracle current-schema completion path nor the + // schema-required early return below finds a schema, and every star + // expansion silently returns no columns. + const effectiveDbType = effectiveDatabaseTypeForConnection(config); + const oracleIdentifier = effectiveDbType === "oracle" || effectiveDbType === "oceanbase-oracle"; + const uppercaseUnquotedIdentifier = oracleIdentifier || effectiveDbType === "saphana"; const completionTable = uppercaseUnquotedIdentifier && context?.tableQuoted === false ? table.toUpperCase() : table; - const rawCompletionSchema = schema?.trim() || (config?.db_type === "dameng" ? config.username?.trim() || undefined : undefined); + const rawCompletionSchema = schema?.trim() || (effectiveDbType === "dameng" ? config?.username?.trim() || undefined : undefined); const completionSchema = uppercaseUnquotedIdentifier && rawCompletionSchema && context?.schemaQuoted === false ? rawCompletionSchema.toUpperCase() : rawCompletionSchema; - const usesCurrentSchema = usesOracleCurrentSchemaCompletion(config?.db_type, completionSchema); + const usesCurrentSchema = usesOracleCurrentSchemaCompletion(effectiveDbType, completionSchema); if (isSchemaAwareDatabase(connectionId) && !connectionUsesDatabaseObjectTreeMode(config) && !completionSchema && !usesCurrentSchema) { return []; }