fix(grid): hydrate large values across result changes

This commit is contained in:
zipg authored and GitHub committed 2026-08-14 11:28:10 +08:00
1 parent 56cacd0441
commit 348e3a0c70
4 files changed
+99 -22

No files matched your search

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { appendLargeValueCells, canUseTableDataLargeValuePreview, largeValueCellMap, remapLargeValueCells, TABLE_DATA_CELL_PREVIEW_SIZE, TABLE_DATA_PREVIEW_CONTENT_MAX_BYTES, tableDataLargeValuePreviewOptions } from "@/lib/dataGrid/dataGridLargeValues";
import { describe, expect, it, vi } from "vitest";
import { appendLargeValueCells, canUseTableDataLargeValuePreview, createResultScopedPendingRequests, largeValueCellMap, remapLargeValueCells, TABLE_DATA_CELL_PREVIEW_SIZE, TABLE_DATA_PREVIEW_CONTENT_MAX_BYTES, tableDataLargeValuePreviewOptions } from "@/lib/dataGrid/dataGridLargeValues";
import { buildDataGridCellDetail } from "@/lib/dataGrid/dataGridDetail";
import type { ColumnInfo } from "@/types/database";
@@ -14,6 +14,14 @@ function column(name: string, dataType: string, isPrimaryKey = false): ColumnInf
};
}
function deferred<T>() {
let resolve!: (value: T) => void;
const promise = new Promise<T>((resolvePromise) => {
resolve = resolvePromise;
});
return { promise, resolve };
}
describe("data grid large-value metadata", () => {
it("enables bounded table previews only for supported databases with stable keys", () => {
const columns = [column("id", "bigint", true), column("payload", "longtext")];
@@ -97,6 +105,46 @@ describe("data grid large-value metadata", () => {
expect(largeValueCellMap(result).get("4:2")).toEqual(result.large_value_cells[0]);
});
it("deduplicates pending requests only within the same result", async () => {
const requests = createResultScopedPendingRequests<boolean>();
const result = {};
const load = deferred<boolean>();
const request = vi.fn(() => load.promise);
const first = requests.run("0:1", result, request);
const second = requests.run("0:1", result, request);
await Promise.resolve();
expect(second).toBe(first);
expect(request).toHaveBeenCalledOnce();
load.resolve(true);
await expect(first).resolves.toBe(true);
});
it("keeps the newer result request after the previous result settles", async () => {
const requests = createResultScopedPendingRequests<boolean>();
const previousLoad = deferred<boolean>();
const currentLoad = deferred<boolean>();
const previousResult = {};
const currentResult = {};
const currentRequest = vi.fn(() => currentLoad.promise);
const previous = requests.run("0:1", previousResult, () => previousLoad.promise);
const current = requests.run("0:1", currentResult, currentRequest);
await Promise.resolve();
previousLoad.resolve(false);
await expect(previous).resolves.toBe(false);
const deduplicatedCurrent = requests.run("0:1", currentResult, currentRequest);
expect(deduplicatedCurrent).toBe(current);
expect(currentRequest).toHaveBeenCalledOnce();
currentLoad.resolve(true);
await expect(current).resolves.toBe(true);
});
it("marks a backend-bounded value as truncated even when the preview is shorter than the UI limit", () => {
const detail = buildDataGridCellDetail({
rowIndex: 0,
@@ -31,6 +31,27 @@ export function largeValueCellMap(result: Pick<QueryResult, "large_value_cells">
return new Map((result.large_value_cells ?? []).map((cell) => [largeValueCellKey(cell.row_index, cell.column_index), cell]));
}
export function createResultScopedPendingRequests<T>() {
const pending = new Map<string, { result: object; promise: Promise<T> }>();
return {
run(key: string, result: object, request: () => Promise<T>): Promise<T> {
const current = pending.get(key);
if (current?.result === result) return current.promise;
let entry: { result: object; promise: Promise<T> };
const promise = Promise.resolve()
.then(request)
.finally(() => {
if (pending.get(key) === entry) pending.delete(key);
});
entry = { result, promise };
pending.set(key, entry);
return promise;
},
};
}
export function appendLargeValueCells(previous: QueryResult["large_value_cells"], segment: QueryResult["large_value_cells"], rowOffset: number, appendedRowCount: number): QueryResult["large_value_cells"] {
const appended = (segment ?? []).filter((cell) => cell.row_index < appendedRowCount).map((cell) => ({ ...cell, row_index: cell.row_index + rowOffset }));
const combined = [...(previous ?? []), ...appended];