From 5c400a449864a87cb9544707a1f91dd763da6822 Mon Sep 17 00:00:00 2001 From: Eve <92513238+1320209572@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:48:14 +0800 Subject: [PATCH] fix(oracle): avoid blocking query object discovery --- .../queryStore.hiddenPrimaryKey.spec.ts | 79 +++++-------------- apps/desktop/src/stores/queryStore.ts | 3 + 2 files changed, 24 insertions(+), 58 deletions(-) diff --git a/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts b/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts index 111de824d8..bbb7e3c250 100644 --- a/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts +++ b/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts @@ -735,34 +735,26 @@ describe("queryStore hidden primary key editing", () => { expect(store.tabs.find((item) => item.id === tabId)?.result?.hidden_column_indexes).toEqual([2]); }); - it("resolves the Oracle current schema before adding ROWID to an unqualified keyless table query", async () => { + it("executes an unqualified Oracle query without enumerating objects when its type is unknown", async () => { getConnectionConfig.mockReturnValue({ id: "oracle-1", name: "Oracle", db_type: "oracle", database: "ORCL", query_timeout_secs: 30 }); getColumns.mockResolvedValue([ { name: "ID", data_type: "NUMBER", is_nullable: false, column_default: null, is_primary_key: false, extra: null }, { name: "NAME", data_type: "VARCHAR2(100)", is_nullable: true, column_default: null, is_primary_key: false, extra: null }, ]); - listTables.mockResolvedValue([{ name: "CUSTOMERS", table_type: "TABLE" }]); - analyzeEditableQueryEditability.mockImplementation(async (sql: string) => { - const hidden = sql.includes("__DBX_PK_0"); - return { - editable: true, - analysis: { - schema: undefined, - tableName: "CUSTOMERS", - selectStar: !hidden, - columns: hidden - ? [ - { star: true, sourceKey: "CUSTOMERS:0", resultName: "*", expression: "*" }, - { resultName: "__DBX_PK_0", expression: "ROWIDTOCHAR(ROWID)" }, - ] - : [], - }, - }; + listTables.mockReturnValue(new Promise(() => undefined)); + analyzeEditableQueryEditability.mockResolvedValue({ + editable: true, + analysis: { + schema: undefined, + tableName: "CUSTOMER_VIEW", + selectStar: true, + columns: [], + }, }); executeMulti.mockResolvedValue([ { - columns: ["ID", "NAME", "__DBX_PK_0"], - rows: [[1, "Alice", "AAAPr9AAEAAAACXAAA"]], + columns: ["ID", "NAME"], + rows: [[1, "Alice"]], affected_rows: 0, execution_time_ms: 1, }, @@ -773,45 +765,16 @@ describe("queryStore hidden primary key editing", () => { const tabId = store.createTab("oracle-1", "ORCL", "Query"); store.setAutoCommit(tabId, true); - await store.executeTabSql(tabId, "SELECT * FROM CUSTOMERS"); + const execution = store.executeTabSql(tabId, "SELECT * FROM CUSTOMER_VIEW"); + await vi.waitFor(() => expect(executeMulti).toHaveBeenCalled(), { timeout: 250 }); + await execution; - expect(listTables).toHaveBeenCalledWith("oracle-1", "ORCL", "", "CUSTOMERS"); - expect(executeMulti).toHaveBeenCalledWith("oracle-1", "ORCL", 'SELECT CUSTOMERS.*, ROWIDTOCHAR(ROWID) AS "__DBX_PK_0" FROM CUSTOMERS', undefined, expect.any(String), expect.objectContaining({ timeoutSecs: 30 })); - expect(store.tabs.find((item) => item.id === tabId)?.result?.hidden_column_indexes).toEqual([2]); - await vi.waitFor(() => expect(store.tabs.find((item) => item.id === tabId)?.tableMeta?.primaryKeys).toEqual(["__DBX_ROWID"])); - - await store.executeTabSql(tabId, "SELECT * FROM CUSTOMERS"); - expect(listTables).toHaveBeenCalledOnce(); - }); - - it("keeps an unqualified Oracle view read-only after resolving it in the current schema", async () => { - getConnectionConfig.mockReturnValue({ id: "oracle-1", name: "Oracle", db_type: "oracle", database: "ORCL", query_timeout_secs: 30 }); - getColumns.mockResolvedValue([ - { name: "ID", data_type: "NUMBER", is_nullable: false, column_default: null, is_primary_key: false, extra: null }, - { name: "NAME", data_type: "VARCHAR2(100)", is_nullable: true, column_default: null, is_primary_key: false, extra: null }, - ]); - listTables.mockResolvedValue([{ name: "CUSTOMERS", table_type: "VIEW" }]); - analyzeEditableQueryEditability.mockResolvedValue({ - editable: true, - analysis: { - schema: undefined, - tableName: "CUSTOMERS", - selectStar: true, - columns: [], - }, - }); - executeMulti.mockResolvedValue([{ columns: ["ID", "NAME"], rows: [[1, "Alice"]], affected_rows: 0, execution_time_ms: 1 }]); - - const { useQueryStore } = await import("@/stores/queryStore"); - const store = useQueryStore(); - const tabId = store.createTab("oracle-1", "ORCL", "Query"); - store.setAutoCommit(tabId, true); - - await store.executeTabSql(tabId, "SELECT * FROM CUSTOMERS"); - - expect(listTables).toHaveBeenCalledWith("oracle-1", "ORCL", "", "CUSTOMERS"); - expect(executeMulti).toHaveBeenCalledWith("oracle-1", "ORCL", "SELECT * FROM CUSTOMERS", undefined, expect.any(String), expect.objectContaining({ timeoutSecs: 30 })); - expect(store.tabs.find((item) => item.id === tabId)?.result?.hidden_column_indexes).toBeUndefined(); + expect(listTables).not.toHaveBeenCalled(); + expect(executeMulti).toHaveBeenCalledWith("oracle-1", "ORCL", "SELECT * FROM CUSTOMER_VIEW", undefined, expect.any(String), expect.objectContaining({ timeoutSecs: 30 })); + const tab = store.tabs.find((item) => item.id === tabId)!; + expect(tab.result?.hidden_column_indexes).toBeUndefined(); + await vi.waitFor(() => expect(tab.queryEditabilityReason).toBe("primary-key-not-returned")); + expect(tab.queryAnalysis).toBeUndefined(); }); it("does not check Oracle ROWID eligibility when query metadata returns no columns", async () => { diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index 6bff1dfa34..2fcc424dda 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -4838,6 +4838,9 @@ export const useQueryStore = defineStore("query", () => { async function resolveOracleRowIdSafety(tab: QueryTab, loaded: LoadedEditableSource, databaseType: DatabaseType): Promise { if (oracleRowIdIsSafeForQuery(tab, loaded)) return true; if (loaded.tableMeta.tableType?.trim()) return false; + // Never enumerate an Oracle schema on the query execution path: large + // schemas can make this optional editability check take minutes (#8462). + if (databaseType === "oracle") return false; const connection = useConnectionStore().getConfig(tab.connectionId!); const schema = loaded.tableMeta.schema?.trim() || tab.schema?.trim() || connection?.default_schema?.trim() || "";