diff --git a/apps/desktop/src/components/sidebar/TreeItem.vue b/apps/desktop/src/components/sidebar/TreeItem.vue index aba2756514..ed0d90d7d6 100644 --- a/apps/desktop/src/components/sidebar/TreeItem.vue +++ b/apps/desktop/src/components/sidebar/TreeItem.vue @@ -1306,7 +1306,7 @@ async function openData() { logPhase("ensure-connected", { tabId }); if (!config) throw new Error("Connection config not found"); - const limit = tableOpenPageLimit(settingsStore.editorSettings.pageSize); + const limit = tableOpenPageLimit(); const refreshTableMetaInBackground = async () => { const metadataStartedAt = performance.now(); console.info("[DBX][openData:metadata:start]", { diff --git a/apps/desktop/src/composables/useNavigationTargets.ts b/apps/desktop/src/composables/useNavigationTargets.ts index 57222479db..cf37dc1b4d 100644 --- a/apps/desktop/src/composables/useNavigationTargets.ts +++ b/apps/desktop/src/composables/useNavigationTargets.ts @@ -23,7 +23,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab const connectionStore = useConnectionStore(); const queryStore = useQueryStore(); const settingsStore = useSettingsStore(); - const pageLimit = tableOpenPageLimit(settingsStore.editorSettings.pageSize); + const pageLimit = tableOpenPageLimit(); connectionStore.activeConnectionId = target.connectionId; const config = connectionStore.getConfig(target.connectionId); diff --git a/apps/desktop/src/lib/table/tableOpenPageLimit.ts b/apps/desktop/src/lib/table/tableOpenPageLimit.ts index 1edf5b0657..43404274aa 100644 --- a/apps/desktop/src/lib/table/tableOpenPageLimit.ts +++ b/apps/desktop/src/lib/table/tableOpenPageLimit.ts @@ -2,7 +2,7 @@ import { DEFAULT_RESULT_PAGE_SIZE, normalizeResultPageSize } from "@/lib/dataGri export const DEFAULT_TABLE_OPEN_PAGE_LIMIT = DEFAULT_RESULT_PAGE_SIZE; -export function tableOpenPageLimit(preferredLimit?: unknown): number { - // Data tabs should reopen with the rows-per-page preference the grid persists. - return normalizeResultPageSize(preferredLimit, DEFAULT_TABLE_OPEN_PAGE_LIMIT); +export function tableOpenPageLimit(): number { + // Opening a table should not inherit the mutable SQL result-grid rows-per-page setting. + return normalizeResultPageSize(DEFAULT_TABLE_OPEN_PAGE_LIMIT); } diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index 1cbd114800..04a903e8e6 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -2694,7 +2694,7 @@ export const useQueryStore = defineStore("query", () => { countSql = plan.countSql; useAgentResultSession = plan.useAgentResultSession; } else if (tab.mode === "data") { - pageLimit = options?.pagination?.limit ?? tableOpenPageLimit(settingsStore.editorSettings.pageSize); + pageLimit = options?.pagination?.limit ?? tableOpenPageLimit(); pageOffset = options?.pagination?.offset ?? 0; } @@ -3268,7 +3268,7 @@ export const useQueryStore = defineStore("query", () => { pagination: tab.mode === "data" ? { - limit: tab.resultPageLimit ?? tableOpenPageLimit(useSettingsStore().editorSettings.pageSize), + limit: tab.resultPageLimit ?? tableOpenPageLimit(), offset: tab.resultPageOffset ?? 0, } : undefined, diff --git a/packages/app-tests/queryStore.test.ts b/packages/app-tests/queryStore.test.ts index 81befc7e0f..daf537113d 100644 --- a/packages/app-tests/queryStore.test.ts +++ b/packages/app-tests/queryStore.test.ts @@ -2412,7 +2412,7 @@ test("data tab execution preserves pagination offset metadata", async () => { } }); -test("data tab default pagination follows persisted rows-per-page", async () => { +test("data tab default pagination is independent from query result page size", async () => { const restoreStorage = installMemoryStorage(); setActivePinia(createPinia()); const connectionStore = useConnectionStore(); @@ -2445,12 +2445,12 @@ test("data tab default pagination follows persisted rows-per-page", async () => }); try { - await store.executeTabSql(tabId, 'SELECT * FROM "users" LIMIT 1000;'); + await store.executeTabSql(tabId, 'SELECT * FROM "users" LIMIT 100;'); assert.equal(preparedPagination, false); - assert.equal(executeBody.maxRows, 1000); - assert.equal(executeBody.fetchSize, 1000); - assert.equal(tab.resultPageLimit, 1000); + assert.equal(executeBody.maxRows, 100); + assert.equal(executeBody.fetchSize, 100); + assert.equal(tab.resultPageLimit, 100); assert.equal(tab.resultPageOffset, 0); } finally { globalThis.fetch = originalFetch; @@ -2458,6 +2458,52 @@ test("data tab default pagination follows persisted rows-per-page", async () => } }); +test("reloading an evicted data tab preserves its saved pagination", async () => { + const restoreStorage = installMemoryStorage(); + setActivePinia(createPinia()); + const connectionStore = useConnectionStore(); + const settingsStore = useSettingsStore(); + const store = useQueryStore(); + const originalFetch = globalThis.fetch; + let executeBody: any; + + settingsStore.updateEditorSettings({ pageSize: 1000 }); + connectionStore.addEphemeralConnection(conn("conn-1")); + const tabId = store.createTab("conn-1", "db", "users", "data", "public"); + const tab = store.tabs.find((item) => item.id === tabId); + assert.ok(tab); + tab.sql = 'SELECT * FROM "public"."users" LIMIT 50 OFFSET 50;'; + tab.lastExecutedSql = tab.sql; + tab.resultPageLimit = 50; + tab.resultPageOffset = 50; + tab.resultEvicted = true; + + globalThis.fetch = withConnectionHealthMock(async (input, init) => { + const url = String(input); + if (url === "/api/query/execute-multi") { + executeBody = JSON.parse(String(init?.body ?? "{}")); + return new Response(JSON.stringify([{ columns: ["id"], rows: [[51]], affected_rows: 0, execution_time_ms: 1 }]), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + return new Response("unexpected request", { status: 500 }); + }); + + try { + await store.reloadEvictedTab(tabId); + + assert.equal(executeBody.maxRows, 50); + assert.equal(executeBody.fetchSize, 50); + assert.equal(tab.resultPageLimit, 50); + assert.equal(tab.resultPageOffset, 50); + assert.equal(tab.resultEvicted, undefined); + } finally { + globalThis.fetch = originalFetch; + restoreStorage(); + } +}); + test("activating an empty data tab waits for explicit execution", async () => { const restoreStorage = installMemoryStorage(); setActivePinia(createPinia()); diff --git a/packages/app-tests/settingsStore.test.ts b/packages/app-tests/settingsStore.test.ts index ac137ba52b..82078f7c47 100644 --- a/packages/app-tests/settingsStore.test.ts +++ b/packages/app-tests/settingsStore.test.ts @@ -48,10 +48,8 @@ test("normalizes saved query result page size", () => { assert.equal(normalizeEditorSettings({ pageSize: 0 }).pageSize, 100); }); -test("uses saved rows-per-page for table opens", () => { +test("uses dedicated default row limit for table opens", () => { assert.equal(tableOpenPageLimit(), 100); - assert.equal(tableOpenPageLimit(500), 500); - assert.equal(tableOpenPageLimit(0), 100); }); test("defaults export batch size to 2000 rows", () => {