From 291890f67e3d3968cc63932b27bebe62fb0ea7b9 Mon Sep 17 00:00:00 2001 From: zipg Date: Thu, 3 Sep 2026 17:52:15 +0800 Subject: [PATCH] feat(export): respect connection query timeout in table export --- .../export/ExportProgressDialog.vue | 10 ++- .../__tests__/ExportProgressDialog.spec.ts | 26 +++++++ apps/desktop/src/components/grid/DataGrid.vue | 19 ++++- .../src/components/layout/ContentArea.vue | 2 + .../src/lib/__tests__/sql/queryError.spec.ts | 1 + apps/desktop/src/lib/sql/queryError.ts | 2 +- crates/dbx-core/src/db/agent_driver.rs | 70 ++++++++++++++++++- crates/dbx-core/src/table_export.rs | 25 ++++++- 8 files changed, 148 insertions(+), 7 deletions(-) diff --git a/apps/desktop/src/components/export/ExportProgressDialog.vue b/apps/desktop/src/components/export/ExportProgressDialog.vue index cada6aa06c..9f7e6b7900 100644 --- a/apps/desktop/src/components/export/ExportProgressDialog.vue +++ b/apps/desktop/src/components/export/ExportProgressDialog.vue @@ -3,12 +3,13 @@ import { computed, onBeforeUnmount, onMounted, ref } from "vue"; import { useI18n } from "vue-i18n"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; -import { Loader2, CheckCircle2, XCircle, AlertCircle, FolderOpen, Minimize2, X } from "@lucide/vue"; +import { Loader2, CheckCircle2, XCircle, AlertCircle, FolderOpen, Minimize2, Wrench, X } from "@lucide/vue"; import { useToast } from "@/composables/useToast"; import { isTauriRuntime } from "@/lib/backend/tauriRuntime"; import * as api from "@/lib/backend/api"; import { translateBackendError } from "@/i18n/backend-errors"; import { formatDataTransferDuration } from "@/composables/useExportTracker"; +import { isQueryTimeoutErrorMessage } from "@/lib/sql/queryError"; const { t } = useI18n(); const { toast } = useToast(); @@ -38,11 +39,13 @@ const props = defineProps<{ finishedAt?: number; disableCancel?: boolean; canMinimize?: boolean; + connectionId?: string; }>(); const emit = defineEmits<{ cancel: []; minimize: []; + changeQueryTimeout: []; "update:open": [value: boolean]; }>(); @@ -59,6 +62,7 @@ const displayName = computed(() => { }); const isActive = computed(() => props.status === "Running" || props.status === "Writing"); const isFinished = computed(() => props.status === "Done" || props.status === "Error" || props.status === "Cancelled"); +const canChangeQueryTimeout = computed(() => props.status === "Error" && !!props.connectionId && !!props.errorMessage && isQueryTimeoutErrorMessage(props.errorMessage)); const canRevealFile = computed(() => props.status === "Done" && !!props.filePath && isTauriRuntime()); const progressPercent = computed(() => { if (!props.totalRows || props.totalRows <= 0) return 0; @@ -163,6 +167,10 @@ async function revealExportFile() { diff --git a/apps/desktop/src/components/layout/ContentArea.vue b/apps/desktop/src/components/layout/ContentArea.vue index f4ef53520b..8e8df06995 100644 --- a/apps/desktop/src/components/layout/ContentArea.vue +++ b/apps/desktop/src/components/layout/ContentArea.vue @@ -1831,6 +1831,7 @@ defineExpose({ @reload="(sql?: string, searchText?: string, whereInput?: string, orderBy?: string, limit?: number, offset?: number, intent?: DataGridReloadIntent) => emit('reload', sql, searchText, whereInput, orderBy, limit, offset, intent)" @paginate="(offset: number, limit: number, whereInput?: string, orderBy?: string) => emit('paginate', offset, limit, whereInput, orderBy)" @sort="(column: string, columnIndex: number, direction: 'asc' | 'desc' | null, whereInput?: string, mode?: DataGridSortMode) => emit('sort', column, columnIndex, direction, whereInput, mode)" + @change-query-timeout="(connectionId: string) => emit('openConnectionSettings', connectionId, 'advanced')" >