diff --git a/apps/desktop/src/composables/__tests__/useNavigationTargets.store.spec.ts b/apps/desktop/src/composables/__tests__/useNavigationTargets.store.spec.ts index d06ede977..004dc78a0 100644 --- a/apps/desktop/src/composables/__tests__/useNavigationTargets.store.spec.ts +++ b/apps/desktop/src/composables/__tests__/useNavigationTargets.store.spec.ts @@ -99,6 +99,7 @@ describe("useNavigationTargets with the real query store", () => { vi.unstubAllGlobals(); installLocalStorage(); mocks.connectionStore.activeConnectionId = ""; + mocks.connectionStore.getConfig.mockImplementation((connectionId: string) => ({ id: connectionId, db_type: "postgres" })); mocks.settingsStore.editorSettings.reuseDataTab = true; mocks.ensureConnected?.mockResolvedValue?.(undefined); mocks.connectionStore.ensureConnected.mockResolvedValue(undefined); @@ -172,6 +173,63 @@ describe("useNavigationTargets with the real query store", () => { expect(new Set(queryStore.tabs.map((tab) => tab.id))).toHaveLength(2); }); + it("keeps different sidebar tables independent when data-tab reuse is enabled", async () => { + const { queryStore } = await setupNavigation(); + const { useSidebarDataOpenRuntime } = await import("@/composables/useSidebarDataOpenRuntime"); + const runtime = useSidebarDataOpenRuntime(); + const users = { id: "users", label: "users", type: "table" as const, connectionId: "connection-1", database: "app", schema: "public", tableType: "TABLE" }; + + await runtime.openData(users); + await runtime.openData({ ...users, id: "orders", label: "orders" }); + + expect(queryStore.tabs).toHaveLength(2); + expect(queryStore.tabs.map((tab) => tab.tableMeta?.tableName)).toEqual(["users", "orders"]); + }); + + it("reuses a sidebar table when the same table is opened from the object browser", async () => { + mocks.connectionStore.getConfig.mockImplementation((connectionId: string) => ({ id: connectionId, db_type: "mysql" })); + const { navigation, queryStore } = await setupNavigation(); + const { useSidebarDataOpenRuntime } = await import("@/composables/useSidebarDataOpenRuntime"); + const runtime = useSidebarDataOpenRuntime(); + const users = { id: "users", label: "users", type: "table" as const, connectionId: "connection-1", database: "app", tableType: "TABLE" }; + + await runtime.openData(users); + const sidebarTabId = queryStore.activeTabId; + await navigation.openObjectBrowserTableTarget({ connectionId: "connection-1", database: "app", schema: "app", tableName: "users", tableType: "TABLE" }); + + expect(queryStore.tabs).toHaveLength(1); + expect(queryStore.activeTabId).toBe(sidebarTabId); + }); + + it("reuses a restored legacy MySQL tab when the same table is opened from the sidebar", async () => { + mocks.connectionStore.getConfig.mockImplementation((connectionId: string) => ({ id: connectionId, db_type: "mysql" })); + mocks.loadOpenTabsState.mockResolvedValue({ + tabs: [ + { + id: "restored-users", + title: "app.users", + connectionId: "connection-1", + database: "app", + schema: "app", + mode: "data", + sql: "SELECT * FROM users", + tableMeta: { schema: "app", tableName: "users", tableType: "TABLE", columns: [], primaryKeys: [] }, + }, + ], + activeTabId: "restored-users", + }); + const { queryStore } = await setupNavigation(); + await queryStore.initOpenTabs({ validConnectionIds: ["connection-1"] }); + const { useSidebarDataOpenRuntime } = await import("@/composables/useSidebarDataOpenRuntime"); + const runtime = useSidebarDataOpenRuntime(); + + await runtime.openData({ id: "users", label: "users", type: "table", connectionId: "connection-1", database: "app", tableType: "TABLE" }); + + expect(queryStore.tabs).toHaveLength(1); + expect(queryStore.activeTabId).toBe("restored-users"); + expect(queryStore.tabs[0]?.schema).toBeUndefined(); + }); + it("creates a new target tab even when the same table was restored", async () => { mocks.loadOpenTabsState.mockResolvedValue({ tabs: [ diff --git a/apps/desktop/src/composables/__tests__/useSidebarDataOpenRuntime.spec.ts b/apps/desktop/src/composables/__tests__/useSidebarDataOpenRuntime.spec.ts index 4195781b8..c9aa46f70 100644 --- a/apps/desktop/src/composables/__tests__/useSidebarDataOpenRuntime.spec.ts +++ b/apps/desktop/src/composables/__tests__/useSidebarDataOpenRuntime.spec.ts @@ -169,6 +169,17 @@ describe("useSidebarDataOpenRuntime", () => { expect(mocks.tabs).toHaveLength(1); }); + it("keeps different sidebar tables independent when reuse is enabled", async () => { + mocks.reuseDataTab = true; + const ordersNode = { ...tableNode, id: "table-orders", label: "orders" }; + + await useSidebarDataOpenRuntime().openData(tableNode); + await useSidebarDataOpenRuntime().openData(ordersNode); + + expect(mocks.tabs).toHaveLength(2); + expect(mocks.tabs.map((tab) => tab.title)).toEqual(["users", "orders"]); + }); + it("creates a new HBase tab for the same table when reuse is disabled", async () => { mocks.databaseType = "hbase"; @@ -178,6 +189,16 @@ describe("useSidebarDataOpenRuntime", () => { expect(mocks.tabs).toHaveLength(2); }); + it("keeps different HBase tables independent when reuse is enabled", async () => { + mocks.databaseType = "hbase"; + mocks.reuseDataTab = true; + + await useSidebarDataOpenRuntime().openData(tableNode); + await useSidebarDataOpenRuntime().openData({ ...tableNode, id: "table-orders", label: "orders" }); + + expect(mocks.tabs).toHaveLength(2); + }); + it("starts cold-cache OceanBase metadata before the table query", async () => { await useSidebarDataOpenRuntime().openData(tableNode); diff --git a/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts b/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts index 8c2aaad15..00b0ea0f0 100644 --- a/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts +++ b/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts @@ -30,7 +30,7 @@ export function useSidebarDataOpenRuntime() { async function openData(node: TreeNode, request?: SidebarDataOpenRequest, openMode: DataTabOpenMode = "default", options: { reuseScope?: DataTabReuseScope } = {}) { if (!(node.type === "table" || node.type === "view" || node.type === "materialized_view") || !hasNodeDatabaseContext(node)) return; const config = connectionStore.getConfig(node.connectionId); - const reuseScope = options.reuseScope ?? (settingsStore.editorSettings.reuseDataTab ? "database" : "none"); + const reuseScope = options.reuseScope ?? (settingsStore.editorSettings.reuseDataTab ? "same-table" : "none"); if (config?.db_type === "hbase") { await connectionStore.ensureConnected(node.connectionId); const tabId = queryStore.createTab(node.connectionId, node.database, node.label, "hbase", undefined, node.label, undefined, { forceNew: openMode === "new-tab" || reuseScope === "none" }); diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 47b058376..a553f4d2c 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -4965,7 +4965,7 @@ export default { disconnectTabHandlingModeKeepTabsKeepResults: "Do not close related tabs", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "Keep related tabs, SQL text, and current results without extra cleanup.", reuseDataTab: "Reuse data tab", - reuseDataTabDescription: "Reuse data tabs when opening tables from the sidebar; when reopening the same table from the object browser, switch to its existing tab.", + reuseDataTabDescription: "Switch to the existing data tab when reopening the same table; different tables always use separate tabs.", sidebarHiddenTablePrefixes: "Hidden table name prefixes", sidebarHiddenTablePrefixesDescription: "One prefix per line. Only sidebar table, view, and collection labels are shortened; tooltips and actions still use the full name.", sidebarHiddenTablePrefixesPlaceholder: "Example:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index c8c47b8cf..5a387a909 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -4740,7 +4740,7 @@ export default withEnglishFallback({ disconnectTabHandlingModeKeepTabsKeepResults: "No cerrar pestañas relacionadas", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "Conserva las pestañas relacionadas, el texto SQL y los resultados actuales sin limpieza adicional.", reuseDataTab: "Reutilizar pestaña de datos", - reuseDataTabDescription: "Reutiliza las pestañas de datos al abrir tablas desde la barra lateral; al volver a abrir la misma tabla desde el explorador de objetos, cambia a su pestaña existente.", + reuseDataTabDescription: "Al volver a abrir la misma tabla, cambia a su pestaña de datos existente; las tablas diferentes siempre usan pestañas separadas.", sidebarHiddenTablePrefixes: "Prefijos ocultos de tablas", sidebarHiddenTablePrefixesDescription: "Un prefijo por linea. Solo acorta etiquetas de tablas, vistas y colecciones en la barra lateral; las acciones y ayudas usan el nombre completo.", sidebarHiddenTablePrefixesPlaceholder: "Ejemplo:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 4773deaf9..4bc33741b 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -4740,7 +4740,7 @@ export default withEnglishFallback({ disconnectTabHandlingModeKeepTabsKeepResults: "Non chiudere le schede correlate", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "Mantieni le schede correlate, il testo SQL e i risultati correnti senza ulteriore pulizia.", reuseDataTab: "Riusa scheda dati", - reuseDataTabDescription: "Riutilizza le schede dati quando apri tabelle dalla barra laterale; quando riapri la stessa tabella dal browser degli oggetti, passa alla scheda esistente.", + reuseDataTabDescription: "Quando riapri la stessa tabella, passa alla scheda dati esistente; tabelle diverse usano sempre schede separate.", sidebarHiddenTablePrefixes: "Prefissi dei nomi delle tabelle nascosti", sidebarHiddenTablePrefixesDescription: "Un prefisso per riga. Solo le etichette di tabelle, viste e collezioni della barra laterale vengono abbreviate; i suggerimenti e le azioni utilizzano ancora il nome completo.", sidebarHiddenTablePrefixesPlaceholder: "Esempio:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index 3691157df..1295c42b2 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -4769,7 +4769,7 @@ export default withEnglishFallback({ disconnectTabHandlingModeKeepTabsKeepResults: "関連タブを閉じない", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "関連タブ、SQLテキスト、現在の結果を追加のクリーンアップなしで保持します。", reuseDataTab: "データタブを再利用", - reuseDataTabDescription: "サイドバーからテーブルを開く際はデータタブを再利用し、オブジェクトブラウザーから同じテーブルを再度開く際は既存のタブに切り替えます。", + reuseDataTabDescription: "同じテーブルを再度開くと既存のデータタブに切り替え、異なるテーブルは常に別のタブで開きます。", sidebarHiddenTablePrefixes: "非表示テーブル名プレフィックス", sidebarHiddenTablePrefixesDescription: "1行に1つのプレフィックス。サイドバーのテーブル、ビュー、コレクションラベルのみ短縮されます。ツールチップと操作は完全な名前を使用します。", sidebarHiddenTablePrefixesPlaceholder: "Example:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts index f035d3c56..e22c78597 100644 --- a/apps/desktop/src/i18n/locales/ko.ts +++ b/apps/desktop/src/i18n/locales/ko.ts @@ -4568,7 +4568,7 @@ export default withEnglishFallback({ disconnectTabHandlingModeKeepTabsKeepResults: "관련 탭 닫지 않기", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "관련 탭, SQL 텍스트, 현재 결과를 추가 정리 없이 유지합니다.", reuseDataTab: "데이터 탭 재사용", - reuseDataTabDescription: "사이드바에서 테이블을 열 때 데이터 탭을 재사용하고, 개체 브라우저에서 같은 테이블을 다시 열면 기존 탭으로 전환합니다.", + reuseDataTabDescription: "같은 테이블을 다시 열면 기존 데이터 탭으로 전환하고, 다른 테이블은 항상 별도 탭에서 엽니다.", sidebarHiddenTablePrefixes: "숨겨진 테이블 이름 접두사", sidebarHiddenTablePrefixesDescription: "한 줄에 하나의 접두사. 사이드바의 테이블, 뷰, 컬렉션 라벨만 줄이며 툴팁과 작업은 전체 이름을 계속 사용합니다.", sidebarHiddenTablePrefixesPlaceholder: "예:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index edd258e93..8aedfab2d 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -4742,7 +4742,7 @@ export default withEnglishFallback({ disconnectTabHandlingModeKeepTabsKeepResults: "Não fechar abas relacionadas", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "Manter abas relacionadas, texto SQL e resultados atuais sem limpeza adicional.", reuseDataTab: "Reutilizar aba de dados", - reuseDataTabDescription: "Reutiliza abas de dados ao abrir tabelas pela barra lateral; ao reabrir a mesma tabela pelo navegador de objetos, alterna para a aba existente.", + reuseDataTabDescription: "Ao reabrir a mesma tabela, alterna para a aba de dados existente; tabelas diferentes sempre usam abas separadas.", sidebarHiddenTablePrefixes: "Prefixos de nome de tabela ocultos", sidebarHiddenTablePrefixesDescription: "Um prefixo por linha. Apenas os rótulos de tabela, view e coleção da barra lateral são encurtados; tooltips e ações ainda usam o nome completo.", sidebarHiddenTablePrefixesPlaceholder: "Exemplo:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index fc5a2e5d0..72ceb15f0 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -4965,7 +4965,7 @@ export default withEnglishFallback({ disconnectTabHandlingModeKeepTabsKeepResults: "不关闭相关页签", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "保留相关页签、SQL 文本和当前结果,不做额外处理。", reuseDataTab: "复用数据标签页", - reuseDataTabDescription: "从侧边栏打开表时复用数据标签页;从浏览对象重复打开同一张表时切换到已有标签页。", + reuseDataTabDescription: "重复打开同一张表时切换到已有数据标签页,不同表始终使用独立标签页。", sidebarHiddenTablePrefixes: "隐藏表名前缀", sidebarHiddenTablePrefixesDescription: "每行一个前缀,仅影响侧边栏表、视图和集合的显示名称,悬浮提示和实际操作仍使用完整名称。", sidebarHiddenTablePrefixesPlaceholder: "例如:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index 73d9e7605..d9102b4b6 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -4203,7 +4203,7 @@ export default withEnglishFallback({ disconnectTabHandlingModeKeepTabsKeepResults: "不關閉相關分頁", disconnectTabHandlingModeKeepTabsKeepResultsDescription: "保留相關分頁、SQL 文字與目前結果,不另外做清理。", reuseDataTab: "重複使用資料分頁", - reuseDataTabDescription: "從側邊欄開啟資料表時重複使用資料分頁;從瀏覽物件重複開啟同一資料表時切換到現有分頁。", + reuseDataTabDescription: "重複開啟同一資料表時切換到現有資料分頁,不同資料表一律使用獨立分頁。", sidebarHiddenTablePrefixes: "隱藏資料表名稱字首", sidebarHiddenTablePrefixesDescription: "每行一個字首。只縮短側邊欄中的資料表、檢視和集合標籤;工具提示和實際操作仍使用完整名稱。", sidebarHiddenTablePrefixesPlaceholder: "範例:\nODS_\nT8Y2_LONG_", diff --git a/apps/desktop/src/lib/__tests__/database/jdbcDialect.spec.ts b/apps/desktop/src/lib/__tests__/database/jdbcDialect.spec.ts index 150f4bd65..34c580898 100644 --- a/apps/desktop/src/lib/__tests__/database/jdbcDialect.spec.ts +++ b/apps/desktop/src/lib/__tests__/database/jdbcDialect.spec.ts @@ -221,6 +221,10 @@ describe("query execution schema", () => { }); describe("object tree node schema", () => { + it("ignores database-shaped schema metadata for MySQL tables", () => { + expect(connectionObjectTreeNodeSchema({ db_type: "mysql" }, "app", "app")).toBeUndefined(); + }); + it("uses the SQLite database alias to qualify attached tables", () => { expect(connectionObjectTreeNodeSchema({ db_type: "sqlite" }, "analytics")).toBe("analytics"); }); diff --git a/apps/desktop/src/lib/__tests__/sidebar/dataTabOpenPolicy.spec.ts b/apps/desktop/src/lib/__tests__/sidebar/dataTabOpenPolicy.spec.ts index 399597332..1b53b84ba 100644 --- a/apps/desktop/src/lib/__tests__/sidebar/dataTabOpenPolicy.spec.ts +++ b/apps/desktop/src/lib/__tests__/sidebar/dataTabOpenPolicy.spec.ts @@ -52,11 +52,11 @@ describe("dataTabOpenPolicy", () => { const existing = dataTab("users", "users"); existing.tableMeta = { schema: "public", tableName: "users", columns: [], primaryKeys: [] }; - expect(findExistingDataTabCandidate([existing], usersTarget, { openMode: "new-tab", reuseScope: "database" })).toBeUndefined(); + expect(findExistingDataTabCandidate([existing], usersTarget, { openMode: "new-tab", reuseScope: "same-table" })).toBeUndefined(); expect(findExistingDataTabCandidate([existing], usersTarget, { openMode: "new-tab", reuseScope: "none" })).toBeUndefined(); }); - it("applies none, same-table, and database reuse scopes independently", () => { + it("only reuses the same table when reuse is enabled", () => { const sameTable = dataTab("users", "users"); sameTable.tableMeta = { schema: "public", tableName: "users", columns: [], primaryKeys: [] }; const otherTable = dataTab("orders", "orders"); @@ -64,7 +64,13 @@ describe("dataTabOpenPolicy", () => { expect(findExistingDataTabCandidate([sameTable], usersTarget, { openMode: "default", reuseScope: "none" })).toBeUndefined(); expect(findExistingDataTabCandidate([sameTable], usersTarget, { openMode: "default", reuseScope: "same-table" })).toEqual({ tab: sameTable, match: "same-table" }); expect(findExistingDataTabCandidate([otherTable], usersTarget, { openMode: "default", reuseScope: "same-table" })).toBeUndefined(); - expect(findExistingDataTabCandidate([otherTable], usersTarget, { openMode: "default", reuseScope: "database" })).toEqual({ tab: otherTable, match: "database" }); + }); + + it("does not reuse a same-name table from another schema", () => { + const archiveUsers = dataTab("archive-users", "users", "archive"); + archiveUsers.tableMeta = { schema: "archive", tableName: "users", columns: [], primaryKeys: [] }; + + expect(findExistingDataTabCandidate([archiveUsers], usersTarget, { openMode: "default", reuseScope: "same-table" })).toBeUndefined(); }); it("allows metadata to update a tab that still points to the requested table", () => { diff --git a/apps/desktop/src/lib/database/jdbcDialect.ts b/apps/desktop/src/lib/database/jdbcDialect.ts index 7dfeb9554..a48031ff8 100644 --- a/apps/desktop/src/lib/database/jdbcDialect.ts +++ b/apps/desktop/src/lib/database/jdbcDialect.ts @@ -175,11 +175,11 @@ export function metadataSchemaForConnection(connection: JdbcDialectConnection | export function connectionObjectTreeNodeSchema(connection: JdbcDialectConnection | undefined, database: string, schema?: string): string | undefined { if (connection?.db_type === "jdbc" && inferJdbcDialect(connection) === "databend") return schema || database; if (connectionUsesDatabaseObjectTreeMode(connection)) return undefined; - if (schema) return schema; const type = effectiveDatabaseTypeForConnection(connection); - if (type === "informix") return undefined; - if (type === "sqlite") return database; - return isSchemaAware(type) ? database : undefined; + if (type === "informix") return schema || undefined; + if (type === "sqlite") return schema || database; + if (!type) return schema; + return isSchemaAware(type) ? schema || database : undefined; } /** Maps a database type to the corresponding CodeMirror SQL dialect name used by QueryEditor and DdlViewDialog. */ diff --git a/apps/desktop/src/lib/sidebar/dataTabOpenPolicy.ts b/apps/desktop/src/lib/sidebar/dataTabOpenPolicy.ts index 5ba9752d1..08ddf4eee 100644 --- a/apps/desktop/src/lib/sidebar/dataTabOpenPolicy.ts +++ b/apps/desktop/src/lib/sidebar/dataTabOpenPolicy.ts @@ -2,7 +2,7 @@ import { matchesModifierOnlyShortcut, type ShortcutLikeEvent } from "@/lib/edito import type { QueryTab, TreeNodeType } from "@/types/database"; export type DataTabOpenMode = "default" | "new-tab"; -export type DataTabReuseScope = "none" | "same-table" | "database"; +export type DataTabReuseScope = "none" | "same-table"; type DataTabLike = Pick; @@ -16,7 +16,7 @@ export interface DataTabTarget { export type ExistingDataTabCandidate = { tab: T; - match: "same-table" | "database"; + match: "same-table"; }; const dataNodeTypes = new Set(["table", "view", "materialized_view"]); @@ -53,8 +53,5 @@ export function findExistingDataTabCandidate(tabs: T[], t const sameTable = tabs.find((tab) => isSameTable(tab, target)); if (sameTable) return { tab: sameTable, match: "same-table" }; - if (options.reuseScope === "same-table") return undefined; - - const sameDatabase = tabs.find((tab) => isSameDatabase(tab, target)); - return sameDatabase ? { tab: sameDatabase, match: "database" } : undefined; + return undefined; } diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index 2e08f53c9..9ffbed95b 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -42,7 +42,7 @@ import { dataTabExecutionDatabase } from "@/lib/table/dataTabExecutionDatabase"; import { tableOpenPageLimit } from "@/lib/table/tableOpenPageLimit"; import { getCachedTableMetadata, loadTableIndexes, loadTableMetadata, type TableMetadataRequest } from "@/lib/metadata/tableMetadataCache"; import { buildTableSelectSql, quoteTableDataIdentifier } from "@/lib/table/tableSelectSql"; -import { connectionQueryExecutionSchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, metadataSchemaForConnection } from "@/lib/database/jdbcDialect"; +import { connectionObjectTreeNodeSchema, connectionQueryExecutionSchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, metadataSchemaForConnection } from "@/lib/database/jdbcDialect"; import { frontendQueryTimeoutSecsForSql, queryTimeoutSecsForConnection } from "@/lib/sql/queryTimeout"; import { queryResultNameFromPreamble, queryResultSourceLabel } from "@/lib/sql/queryResultSource"; import { beginDataGridNativeSelectionBlock, finishDataGridNativeSelectionBlock } from "@/lib/dataGrid/dataGridNativeSelection"; @@ -1200,6 +1200,12 @@ export const useQueryStore = defineStore("query", () => { } function applyRestoredOpenTabs(restored: { tabs: QueryTab[]; activeTabId: string | null }) { + const connectionStore = useConnectionStore(); + for (const tab of restored.tabs) { + if (tab.mode !== "data") continue; + const connection = connectionStore.getConfig(tab.connectionId); + if (connection) tab.schema = connectionObjectTreeNodeSchema(connection, tab.database, tab.schema); + } tabs.value = restored.tabs; activeTabId.value = restored.activeTabId; activeTabHistory.value = restored.activeTabId ? [restored.activeTabId] : [];