fix(grid): reuse local result rows for csv/xlsx export instead of re-running query
* fix(grid): reuse local result rows for csv/xlsx export instead of re-running query
Exporting the full result set ("导出当前结果集全部数据 CSV") previously
re-executed the source SQL on the database via the backend streaming path
(exportQueryResultViaBackend -> start_query_result_export), and the frontend
fallback (fullExportResult/fetchTabResultForExport) did the same. For a slow
query this means running it a second time just to export rows already on
screen, which can hang or crash (e.g. a 2-minute paimon query returning 30
rows).
When the in-memory result already holds the complete set (no server-side
pagination, not truncated, no further pages), skip both re-executing paths and
write the displayed rows directly. Large/truncated/paginated results keep the
original streaming behavior unchanged.
* fix(grid): export raw in-memory result for complete local result set
---------
Co-authored-by: t8y2 <1156263951@qq.com>
This commit is contained in:
parent
15114f58dd
commit
5db0fc4fb1
|
|
@ -3278,6 +3278,10 @@ 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);
|
||||
const canGoNextPage = computed(() => {
|
||||
return canGoNextDataGridPage({
|
||||
hasMore: props.result.has_more,
|
||||
|
|
@ -5827,6 +5831,8 @@ const {
|
|||
hasRowSelection,
|
||||
fullExportResult: props.fullExportResult,
|
||||
queryResultExportRequest: props.queryResultExportRequest,
|
||||
hasCompleteLocalResult,
|
||||
completeLocalResult: computed(() => (hasCompleteLocalResult.value ? props.result : undefined)),
|
||||
allExportResults: computed(() => props.allExportResults),
|
||||
currentResultLabel: computed(() => props.result.sourceLabel),
|
||||
exportFileBaseName: computed(() => props.exportFileBaseName),
|
||||
|
|
|
|||
|
|
@ -52,6 +52,23 @@ export interface UseDataGridExportOptions {
|
|||
hasRowSelection: ComputedRef<boolean>;
|
||||
fullExportResult?: (onProgress?: (info: { rowsExported: number; totalRows: number | null }) => void) => Promise<QueryResult | undefined>;
|
||||
queryResultExportRequest?: (options: { exportId: string; filePath: string; format: "csv" | "xlsx" }) => Promise<QueryResultExportRequest | undefined>;
|
||||
/**
|
||||
* True when the in-memory result already holds the complete result set —
|
||||
* i.e. the query ran without server-side pagination, was not truncated, and
|
||||
* has no further pages. When true, full-result exports skip the re-executing
|
||||
* backend/frontend streaming paths and write the local rows directly, so a
|
||||
* slow query is never re-run just to export rows that are already on screen.
|
||||
*/
|
||||
hasCompleteLocalResult?: ComputedRef<boolean>;
|
||||
/**
|
||||
* The raw in-memory QueryResult to use for "export all" when
|
||||
* hasCompleteLocalResult is true. Exports the original query result (all
|
||||
* rows, all columns, committed values) so the output matches the original
|
||||
* re-run-SQL semantics — displayItems only covers visible columns and
|
||||
* reflects client-side filters/search and unsaved edits, which would
|
||||
* silently change what "export all data" produces.
|
||||
*/
|
||||
completeLocalResult?: ComputedRef<QueryResult | undefined>;
|
||||
allExportResults?: ComputedRef<Array<{ sheetName: string; result: QueryResult }> | undefined>;
|
||||
currentResultLabel?: ComputedRef<string | undefined>;
|
||||
exportFileBaseName?: ComputedRef<string | undefined>;
|
||||
|
|
@ -122,6 +139,8 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
|||
hasRowSelection,
|
||||
fullExportResult,
|
||||
queryResultExportRequest,
|
||||
hasCompleteLocalResult,
|
||||
completeLocalResult,
|
||||
allExportResults,
|
||||
currentResultLabel,
|
||||
exportFileBaseName,
|
||||
|
|
@ -146,10 +165,18 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
|||
}
|
||||
|
||||
async function resultToExport(rowIds?: number[], onProgress?: (info: { rowsExported: number; totalRows: number | null }) => void, useFullExport = true): Promise<{ columns: string[]; rows: CellValue[][] }> {
|
||||
if (useFullExport && rowIds === undefined && fullExportResult) {
|
||||
if (useFullExport && rowIds === undefined && fullExportResult && !hasCompleteLocalResult?.value) {
|
||||
const result = await fullExportResult(onProgress);
|
||||
if (result) return { columns: result.columns, rows: result.rows };
|
||||
}
|
||||
// The full result is already in memory — export the raw QueryResult (all
|
||||
// rows, all columns, committed values) so "export all data" matches the
|
||||
// original re-run-SQL semantics. displayItems only covers visible columns
|
||||
// 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 { columns: completeLocalResult.value.columns, rows: completeLocalResult.value.rows };
|
||||
}
|
||||
return {
|
||||
columns: columns.value,
|
||||
rows: rowsToExport(rowIds).map((item) => item.data),
|
||||
|
|
@ -512,7 +539,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
|||
if (await exportQueryResultViaBackend("csv", rowIds)) return;
|
||||
if (await exportFullTableDataViaBackend("csv", rowIds)) return;
|
||||
|
||||
const needsFullExport = rowIds === undefined && !!fullExportResult;
|
||||
const needsFullExport = rowIds === undefined && !!fullExportResult && !hasCompleteLocalResult?.value;
|
||||
if (needsFullExport && exportProgressDialog && exportProgressState) {
|
||||
exportProgressState.value = {
|
||||
title: t("exportProgress.title"),
|
||||
|
|
@ -716,7 +743,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
|||
if (!path) return;
|
||||
outputPath = path as string;
|
||||
}
|
||||
const needsFullExport = rowIds === undefined && !!fullExportResult;
|
||||
const needsFullExport = rowIds === undefined && !!fullExportResult && !hasCompleteLocalResult?.value;
|
||||
if (needsFullExport && exportProgressDialog && exportProgressState) {
|
||||
exportProgressState.value = {
|
||||
title: t("exportProgress.title"),
|
||||
|
|
@ -908,6 +935,9 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
|||
if (rowIds !== undefined || context.value !== "results" || !queryResultExportRequest) {
|
||||
return false;
|
||||
}
|
||||
// The full result is already in memory — don't re-execute the query on the
|
||||
// backend just to stream the same rows back to a file.
|
||||
if (hasCompleteLocalResult?.value) return false;
|
||||
|
||||
const extension = format;
|
||||
const filterName = format === "csv" ? "CSV" : "Excel";
|
||||
|
|
|
|||
Loading…
Reference in New Issue