fix(export): preserve local query result constraints
This commit is contained in:
5 files changed
+192
-7
No files matched your search
@@ -129,7 +129,7 @@ import { applyColumnFormatter, buildColumnFormatterKey, getSupportedTimeZoneOpti
|
||||
import { temporalCellEditorConfig, type TemporalCellEditorConfig } from "@/lib/dataGrid/dataGridTemporalEditor";
|
||||
import { isCancelSearchShortcut, isCopyCurrentRowShortcut, isDeleteCurrentRowShortcut, isFocusSearchShortcut, isModRShortcut, isSaveShortcut, isToggleTransposeShortcut } from "@/lib/editor/keyboardShortcuts";
|
||||
import { dataGridHeaderContentWidth, scrollbarGutterWidth } from "@/lib/dataGrid/dataGridScrollGutter";
|
||||
import { canGoNextDataGridPage } from "@/lib/dataGrid/dataGridPagination";
|
||||
import { canGoNextDataGridPage, hasCompleteLocalDataGridResult } from "@/lib/dataGrid/dataGridPagination";
|
||||
import { dataGridCountQueryOptions } from "@/lib/dataGrid/dataGridQueryOptions";
|
||||
import { dataGridBottomScrollTop, dataGridScrollPosition, isDataGridAtScrollBottom, isDataGridNearScrollBottom, shouldCheckInfiniteScrollAfterScroll, type DataGridScrollPosition } from "@/lib/dataGrid/dataGridInfiniteScroll";
|
||||
import { CANVAS_DATA_GRID_ROW_HEIGHT, dataGridSearchMatchKey, drawCanvasDataGrid } from "@/lib/dataGrid/canvasDataGridRenderer";
|
||||
@@ -2359,10 +2359,19 @@ const hasKnownTotalRowCount = computed(() => typeof serverKnownTotalRowCount.val
|
||||
// rowCount IS the total. Without this hint, the "page is full → assume more"
|
||||
// fallback in canGoNextDataGridPage lets the user keep clicking next forever.
|
||||
const allRowsLoaded = computed(() => isResultsContext.value && props.pageLimit === undefined);
|
||||
// True when the in-memory result already holds the complete result set (results
|
||||
// context, no server-side pagination, not truncated, no further pages). Used to
|
||||
// skip re-executing the query on export and instead write the local rows.
|
||||
const hasCompleteLocalResult = computed(() => !!props.result && allRowsLoaded.value && props.result.truncated !== true && props.result.has_more !== true);
|
||||
// Skip re-executing a query when the first page already contains every row.
|
||||
// This also covers paginated queries whose first page is shorter than the limit.
|
||||
const hasCompleteLocalResult = computed(() =>
|
||||
hasCompleteLocalDataGridResult({
|
||||
isResultsContext: isResultsContext.value,
|
||||
rowCount: props.result.rows.length,
|
||||
pageLimit: props.pageLimit,
|
||||
pageOffset: props.pageOffset,
|
||||
totalRowCount: serverKnownTotalRowCount.value,
|
||||
truncated: props.result.truncated,
|
||||
hasMore: props.result.has_more,
|
||||
}),
|
||||
);
|
||||
const canGoNextPage = computed(() => {
|
||||
return canGoNextDataGridPage({
|
||||
hasMore: props.result.has_more,
|
||||
|
||||
@@ -234,6 +234,22 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
return pattern ? { ...result, rows: formatTemporalRowsForExport(result.rows, result.columnTypes, pattern) } : result;
|
||||
}
|
||||
|
||||
function normalizeCompleteLocalResult(result: QueryResult): { columns: string[]; columnTypes: string[]; rows: CellValue[][] } {
|
||||
const hiddenColumnIndexes = new Set(result.hidden_column_indexes ?? []);
|
||||
const exportedColumnIndexes = result.columns.map((_, index) => index).filter((index) => !hiddenColumnIndexes.has(index));
|
||||
const hasHiddenColumns = exportedColumnIndexes.length !== result.columns.length;
|
||||
const editorSettings = useSettingsStore().editorSettings;
|
||||
const rows = editorSettings.exportRowLimitEnabled ? result.rows.slice(0, editorSettings.exportRowLimit) : result.rows;
|
||||
|
||||
// Internal key columns are query-only metadata. Keep every user column,
|
||||
// including columns hidden manually in the grid, while preserving alignment.
|
||||
return {
|
||||
columns: hasHiddenColumns ? exportedColumnIndexes.map((index) => result.columns[index]!) : result.columns,
|
||||
columnTypes: hasHiddenColumns ? exportedColumnIndexes.map((index) => result.column_types?.[index] ?? "") : (result.column_types ?? []),
|
||||
rows: hasHiddenColumns ? rows.map((row) => exportedColumnIndexes.map((index) => row[index])) : rows,
|
||||
};
|
||||
}
|
||||
|
||||
async function resultToExport(rowIds?: number[], onProgress?: (info: { rowsExported: number; totalRows: number | null }) => void, useFullExport = true, formatDateTime = true): Promise<{ columns: string[]; columnTypes: string[]; rows: CellValue[][] }> {
|
||||
if (useFullExport && rowIds === undefined && fullExportResult && !hasCompleteLocalResult?.value) {
|
||||
const result = await fullExportResult(onProgress);
|
||||
@@ -245,7 +261,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
// and reflects client-side filters/search and unsaved edits, which would
|
||||
// silently change what the export contains.
|
||||
if (useFullExport && rowIds === undefined && hasCompleteLocalResult?.value && completeLocalResult?.value) {
|
||||
return applyGlobalDateTimeExportFormat({ columns: completeLocalResult.value.columns, columnTypes: completeLocalResult.value.column_types ?? [], rows: completeLocalResult.value.rows }, formatDateTime);
|
||||
return applyGlobalDateTimeExportFormat(normalizeCompleteLocalResult(completeLocalResult.value), formatDateTime);
|
||||
}
|
||||
return applyGlobalDateTimeExportFormat(
|
||||
{
|
||||
|
||||
@@ -11,6 +11,28 @@ export interface CanGoNextDataGridPageOptions {
|
||||
allRowsLoaded?: boolean;
|
||||
}
|
||||
|
||||
export interface CompleteLocalDataGridResultOptions {
|
||||
isResultsContext: boolean;
|
||||
rowCount: number;
|
||||
pageLimit?: number;
|
||||
pageOffset?: number;
|
||||
totalRowCount?: number;
|
||||
truncated?: boolean;
|
||||
hasMore?: boolean;
|
||||
}
|
||||
|
||||
export function hasCompleteLocalDataGridResult(options: CompleteLocalDataGridResultOptions): boolean {
|
||||
if (!options.isResultsContext || options.truncated === true || options.hasMore === true) return false;
|
||||
if (options.pageLimit === undefined) return true;
|
||||
if ((options.pageOffset ?? 0) !== 0) return false;
|
||||
|
||||
const pageLimit = Math.max(1, options.pageLimit);
|
||||
if (options.rowCount < pageLimit) return true;
|
||||
|
||||
const totalRowCount = options.totalRowCount;
|
||||
return typeof totalRowCount === "number" && Number.isFinite(totalRowCount) && totalRowCount >= 0 && options.rowCount >= totalRowCount;
|
||||
}
|
||||
|
||||
export function canGoNextDataGridPage(options: CanGoNextDataGridPageOptions): boolean {
|
||||
if (options.hasMore === true) return true;
|
||||
|
||||
|
||||
Reference in new issue
Block a user