mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 06:00:39 +00:00
fix(grid): preserve exact totals beyond the result row limit
Closes #6786
This commit is contained in:
@@ -205,7 +205,7 @@ import {
|
||||
replaceColumnValueFilterCondition,
|
||||
} from "@/lib/dataGrid/dataGridColumnFilter";
|
||||
import { normalizeResultPageSize, resultPageSizeMenuOptions } from "@/lib/dataGrid/paginationPageSize";
|
||||
import { continuousQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||
import { continuousQueryResultMaxRows, effectiveQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||
import { allNullColumnIndexes } from "@/lib/dataGrid/dataGridColumnVisibility";
|
||||
import { buildDataGridColumnLookupItems, dataGridColumnCommentFor, filterDataGridColumnLookupItems } from "@/lib/dataGrid/dataGridColumnLookup";
|
||||
import { uniqueDataGridColumnOrderKeys } from "@/lib/dataGrid/dataGridColumnOrder";
|
||||
@@ -633,6 +633,8 @@ const dataGridRenderMode = computed(() => settingsStore.editorSettings.dataGridR
|
||||
const dataGridSearchMode = computed(() => settingsStore.editorSettings.dataGridSearchMode);
|
||||
const compactDataGridToolbar = computed(() => isDataGridToolbarCompact(dataGridTopbarWidth.value, dataGridViewportWidth.value, DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH));
|
||||
const infiniteScrollEnabled = computed(() => props.paginationEnabled && settingsStore.editorSettings.infiniteScroll);
|
||||
const queryResultMaxRows = computed(() => effectiveQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows));
|
||||
const paginationMaxRows = computed(() => (isResultsContext.value ? queryResultMaxRows.value : undefined));
|
||||
const infiniteScrollMaxRows = computed(() => continuousQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows));
|
||||
const flatteningMultiLineEnabled = computed(() => settingsStore.editorSettings.flatteningMultiLineText);
|
||||
const expandedCellEditor = ref<{ rowId: number; col: number } | null>(null);
|
||||
@@ -3058,6 +3060,7 @@ const paginationTotalRowCount = computed(() =>
|
||||
paginationTotalRowCount: props.paginationTotalRowCount,
|
||||
serverKnownTotalRowCount: serverKnownTotalRowCount.value,
|
||||
totalRowCountIsExact: totalRowCountIsExact.value,
|
||||
maxRows: paginationMaxRows.value,
|
||||
}),
|
||||
);
|
||||
// Only a server-confirmed total drives pagination — an inferred total means
|
||||
@@ -3335,8 +3338,9 @@ function applyCustomPageSize() {
|
||||
}
|
||||
|
||||
function jumpToCountedLastPage(total: number) {
|
||||
if (total <= 0) return;
|
||||
const lastPageNum = Math.max(1, Math.ceil(total / pageSize.value));
|
||||
const paginationTotal = resolveDataGridPaginationTotal({ paginationTotalRowCount: total, totalRowCountIsExact: true, maxRows: paginationMaxRows.value });
|
||||
if (paginationTotal === undefined || paginationTotal <= 0) return;
|
||||
const lastPageNum = Math.max(1, Math.ceil(paginationTotal / pageSize.value));
|
||||
if (lastPageNum <= currentPage.value) return;
|
||||
// Do not bump currentPage before the new page loads — otherwise stale rows
|
||||
// briefly render with last-page indexes (e.g. 12001-13000) and flash a fake full page.
|
||||
|
||||
@@ -42,9 +42,10 @@ export function dataGridTotalRowCountLabelKey(totalRowCountIsExact: boolean, ine
|
||||
return inexactMode === "estimated" ? "grid.totalRowCountEstimated" : "grid.totalRowCountAtLeast";
|
||||
}
|
||||
|
||||
export function resolveDataGridPaginationTotal(options: { paginationTotalRowCount?: number; serverKnownTotalRowCount?: number; totalRowCountIsExact: boolean }): number | undefined {
|
||||
if (options.paginationTotalRowCount !== undefined) return options.paginationTotalRowCount;
|
||||
return options.totalRowCountIsExact ? options.serverKnownTotalRowCount : undefined;
|
||||
export function resolveDataGridPaginationTotal(options: { paginationTotalRowCount?: number; serverKnownTotalRowCount?: number; totalRowCountIsExact: boolean; maxRows?: number }): number | undefined {
|
||||
const total = options.paginationTotalRowCount ?? (options.totalRowCountIsExact ? options.serverKnownTotalRowCount : undefined);
|
||||
if (total === undefined || options.maxRows === undefined) return total;
|
||||
return Math.min(total, options.maxRows);
|
||||
}
|
||||
|
||||
export function hasCompleteLocalDataGridResult(options: CompleteLocalDataGridResultOptions): boolean {
|
||||
|
||||
@@ -4115,7 +4115,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
traceId: string;
|
||||
elapsed: () => string;
|
||||
timeoutSecs: number;
|
||||
maxRows?: number;
|
||||
}) {
|
||||
const resultRowCount = options.result.rows.length;
|
||||
if (resultRowCount <= 0) {
|
||||
@@ -4158,11 +4157,12 @@ export const useQueryStore = defineStore("query", () => {
|
||||
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, undefined);
|
||||
return;
|
||||
}
|
||||
const cappedTotal = capQueryResultTotal(total, options.maxRows);
|
||||
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, cappedTotal);
|
||||
// COUNT describes all matching rows; the configured result limit only
|
||||
// constrains how many of them pagination may load and retain.
|
||||
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, total);
|
||||
queryExecutionLog("info", "count:done", {
|
||||
traceId: options.traceId,
|
||||
total: cappedTotal,
|
||||
total,
|
||||
elapsed: options.elapsed(),
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -5151,7 +5151,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
traceId,
|
||||
elapsed,
|
||||
timeoutSecs: queryTimeoutSecs,
|
||||
maxRows: queryResultMaxRows,
|
||||
});
|
||||
}
|
||||
queryExecutionLog("info", "result:assigned", {
|
||||
|
||||
+42
@@ -28,6 +28,40 @@ test("estimated display totals do not become pagination bounds", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("exact display totals keep pagination inside the configured result cap", () => {
|
||||
assert.equal(
|
||||
resolveDataGridPaginationTotal({
|
||||
serverKnownTotalRowCount: 175_390,
|
||||
totalRowCountIsExact: true,
|
||||
maxRows: 100_000,
|
||||
}),
|
||||
100_000,
|
||||
);
|
||||
assert.equal(
|
||||
resolveDataGridPaginationTotal({
|
||||
serverKnownTotalRowCount: 99_999,
|
||||
totalRowCountIsExact: true,
|
||||
maxRows: 100_000,
|
||||
}),
|
||||
99_999,
|
||||
);
|
||||
assert.equal(
|
||||
resolveDataGridPaginationTotal({
|
||||
serverKnownTotalRowCount: 100_000,
|
||||
totalRowCountIsExact: true,
|
||||
maxRows: 100_000,
|
||||
}),
|
||||
100_000,
|
||||
);
|
||||
assert.equal(
|
||||
resolveDataGridPaginationTotal({
|
||||
serverKnownTotalRowCount: 175_390,
|
||||
totalRowCountIsExact: true,
|
||||
}),
|
||||
175_390,
|
||||
);
|
||||
});
|
||||
|
||||
test("first query page is complete when its known total is already loaded", () => {
|
||||
assert.equal(
|
||||
hasCompleteLocalDataGridResult({
|
||||
@@ -208,11 +242,19 @@ test("last page always re-counts when a count path is available", () => {
|
||||
test("jumping to last page does not rewrite indexes before the new page loads", () => {
|
||||
const source = readFileSync("apps/desktop/src/components/grid/DataGrid.vue", "utf8");
|
||||
const jumpFn = source.match(/function jumpToCountedLastPage\(total: number\) \{[\s\S]*?\n\}/)?.[0] ?? "";
|
||||
assert.match(jumpFn, /resolveDataGridPaginationTotal/);
|
||||
assert.match(jumpFn, /maxRows: paginationMaxRows\.value/);
|
||||
assert.match(jumpFn, /emit\("paginate"/);
|
||||
assert.doesNotMatch(jumpFn, /currentPage\.value\s*=/);
|
||||
assert.match(source, /function rowNumberPageOffset/);
|
||||
});
|
||||
|
||||
test("query result caps do not limit table-data pagination", () => {
|
||||
const source = readFileSync("apps/desktop/src/components/grid/DataGrid.vue", "utf8");
|
||||
assert.match(source, /const paginationMaxRows = computed\(\(\) => \(isResultsContext\.value \? queryResultMaxRows\.value : undefined\)\)/);
|
||||
assert.equal(source.match(/maxRows: paginationMaxRows\.value/g)?.length, 2);
|
||||
});
|
||||
|
||||
test("row number gutter width tracks the largest visible row index", () => {
|
||||
const source = readFileSync("apps/desktop/src/components/grid/DataGrid.vue", "utf8");
|
||||
assert.match(source, /dataGridRowNumberColumnWidth/);
|
||||
|
||||
+4
-4
@@ -7169,7 +7169,7 @@ for (const resultState of [
|
||||
{ label: "truncated", result: { truncated: true, has_more: false } },
|
||||
{ label: "ambiguous exhaustion", result: {} },
|
||||
]) {
|
||||
test(`oracle agent ${resultState.label} short page uses COUNT and caps the configured result total`, async () => {
|
||||
test(`oracle agent ${resultState.label} short page preserves an exact COUNT beyond the result cap`, async () => {
|
||||
const restoreStorage = installMemoryStorage();
|
||||
setActivePinia(createPinia());
|
||||
const connectionStore = useConnectionStore();
|
||||
@@ -7214,7 +7214,7 @@ for (const resultState of [
|
||||
}
|
||||
if (url === "/api/query/execute") {
|
||||
countRequests += 1;
|
||||
return new Response(JSON.stringify({ columns: ["COUNT(*)"], rows: [[3_357_833]], affected_rows: 0, execution_time_ms: 1 }), { status: 200, headers: { "Content-Type": "application/json" } });
|
||||
return new Response(JSON.stringify({ columns: ["COUNT(*)"], rows: [[175_390]], affected_rows: 0, execution_time_ms: 1 }), { status: 200, headers: { "Content-Type": "application/json" } });
|
||||
}
|
||||
if (url === "/api/query/analyze-editability") {
|
||||
return new Response(JSON.stringify({ editable: false, reason: "complex-source" }), {
|
||||
@@ -7227,11 +7227,11 @@ for (const resultState of [
|
||||
|
||||
try {
|
||||
await store.executeTabSql(tabId, "SELECT ID FROM LARGE_TABLE");
|
||||
await waitFor(() => tab.resultTotalRowCount === 100_000);
|
||||
await waitFor(() => tab.resultTotalRowCount === 175_390);
|
||||
|
||||
assert.equal(countRequests, 1);
|
||||
assert.equal(tab.result?.rows.length, 10_000);
|
||||
assert.equal(tab.resultTotalRowCount, 100_000);
|
||||
assert.equal(tab.resultTotalRowCount, 175_390);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
restoreStorage();
|
||||
|
||||
Reference in New Issue
Block a user