From 9cf2ec4194c54c1c65b259fc33366d5598724ffc Mon Sep 17 00:00:00 2001 From: zipg Date: Mon, 20 Jul 2026 13:50:12 +0800 Subject: [PATCH] fix(oracle): skip metadata lookups for missing tables --- .../src/lib/metadata/tableMetadataCache.ts | 2 +- .../queryStore.hiddenPrimaryKey.spec.ts | 34 +++++++++++++++++++ apps/desktop/src/stores/queryStore.ts | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/lib/metadata/tableMetadataCache.ts b/apps/desktop/src/lib/metadata/tableMetadataCache.ts index 5804c30a80..5f7410c223 100644 --- a/apps/desktop/src/lib/metadata/tableMetadataCache.ts +++ b/apps/desktop/src/lib/metadata/tableMetadataCache.ts @@ -139,7 +139,7 @@ export async function loadTableMetadata(request: TableMetadataRequest): Promise< scope, async () => { const columns = await api.getColumns(request.connectionId, request.database, request.schema ?? "", request.tableName, request.catalog); - const indexes = await api.listIndexes(request.connectionId, request.database, request.schema ?? "", request.tableName, request.catalog).catch((): IndexInfo[] => []); + const indexes = columns.length > 0 ? await api.listIndexes(request.connectionId, request.database, request.schema ?? "", request.tableName, request.catalog).catch((): IndexInfo[] => []) : []; const primaryKeys = editableRowIdentifierColumns(request.databaseType as DatabaseType, columns, indexes, request.tableType); return { schema: request.schema || undefined, diff --git a/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts b/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts index 26132e3810..3b6c3bc96f 100644 --- a/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts +++ b/apps/desktop/src/stores/__tests__/queryStore.hiddenPrimaryKey.spec.ts @@ -173,6 +173,40 @@ describe("queryStore hidden primary key editing", () => { expect(tab.queryEditabilityReason).toBeUndefined(); }); + it("does not check Oracle ROWID eligibility when query metadata returns no columns", async () => { + getConnectionConfig.mockReturnValue({ id: "oracle-1", name: "Oracle", db_type: "oracle", database: "ORCL", query_timeout_secs: 30 }); + getColumns.mockResolvedValue([]); + analyzeEditableQueryEditability.mockResolvedValue({ + editable: true, + analysis: { + schema: undefined, + tableName: "aa", + selectStar: true, + columns: [], + }, + }); + executeMulti.mockResolvedValue([ + { + columns: ["Error"], + rows: [["ORA-00942: table or view does not exist"]], + affected_rows: 0, + execution_time_ms: 1, + execution_error: true, + }, + ]); + + const { useQueryStore } = await import("@/stores/queryStore"); + const store = useQueryStore(); + const tabId = store.createTab("oracle-1", "ORCL", "Query"); + + await store.executeTabSql(tabId, "SELECT * FROM aa"); + + expect(getColumns).toHaveBeenCalledWith("oracle-1", "ORCL", "", "AA", undefined); + expect(listIndexes).not.toHaveBeenCalled(); + expect(listObjects).not.toHaveBeenCalled(); + expect(executeMulti).toHaveBeenCalledWith("oracle-1", "ORCL", "SELECT * FROM aa", undefined, expect.any(String), expect.objectContaining({ timeoutSecs: 30 })); + }); + it("keeps a keyless Oracle query editable when its WHERE clause reads another table", async () => { getConnectionConfig.mockReturnValue({ id: "oracle-1", name: "Oracle", db_type: "oracle", database: "ORCL", query_timeout_secs: 30 }); getColumns.mockResolvedValue([ diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index da8dc13562..b857b3b035 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -2449,6 +2449,7 @@ export const useQueryStore = defineStore("query", () => { if (sources.length !== 1 || analysis.distinct) return unchanged; const loaded = await loadEditableQuerySource(tab, analysis, sources[0]!, conn, databaseType, traceId, elapsed); + if (loaded.tableMeta.columns.length === 0) return unchanged; if (loaded.tableMeta.tableType?.toUpperCase().includes("VIEW")) return unchanged; const metadataAnalysis = expandStarProjectionColumnsForSource(bindColumnsForSource(databaseType, loaded.analysis, loaded.source, loaded.tableMeta.columns), loaded.source, loaded.tableMeta.columns); const declaredPrimaryKeys = loaded.tableMeta.columns.filter((column) => column.is_primary_key).map((column) => column.name);