feat(export): add SQL INSERT export crash diagnostics

This commit is contained in:
zipg authored and GitHub committed 2026-09-08 00:43:39 +08:00
1 parent 9ae2e70030
commit d44798f70f
10 files changed
+298 -5

No files matched your search

@@ -16,6 +16,8 @@ import { clearDataGridClipboardCopy, rememberDataGridClipboardCopy } from "@/lib
import { buildDataGridCopyInsertStatement, type DataGridCopyInsertMode, type DataGridTableMeta } from "@/lib/dataGrid/dataGridSql";
import { formatSqlInsert, formatTsv } from "@/lib/export/exportFormats";
import { showSqlInsertModeDialog, type SqlInsertMode } from "@/lib/export/sqlInsertMode";
import { summarizeExportRows } from "@/lib/export/exportDiagnostics";
import { appendDebugLog, appendNativeProcessMemoryLog, getBrowserMemorySnapshot, isDebugLoggingEnabled } from "@/lib/backend/debugLog";
import { uuid } from "@/lib/common/utils";
import { useSettingsStore } from "@/stores/settingsStore";
import { expandNestedJsonStringsForCopy } from "@/lib/common/jsonCopyValue";
@@ -1334,18 +1336,76 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
async function exportSql(rowIds?: number[]) {
await runExclusiveExport(async () => {
const exportId = uuid();
const exportStartedAt = performance.now();
const logExportStage = (stage: string, details: Record<string, unknown> = {}, sampleNativeMemory = false) => {
if (!isDebugLoggingEnabled()) return;
appendDebugLog("info", `[DBX][export:sql:${stage}]`, {
exportId,
elapsedMs: Math.round(performance.now() - exportStartedAt),
...details,
browserMemory: getBrowserMemorySnapshot(),
});
if (sampleNativeMemory) void appendNativeProcessMemoryLog(`export-sql-${stage}`, { exportId });
};
logExportStage(
"start",
{
context: context.value,
databaseType: databaseType.value,
exportAllRows: rowIds === undefined,
requestedRowCount: rowIds?.length ?? null,
hasCompleteLocalResult: hasCompleteLocalResult?.value ?? null,
},
true,
);
const insertMode = await showSqlInsertModeDialog();
if (insertMode === null) return;
if (insertMode === null) {
logExportStage("cancelled", { stage: "insert-mode-dialog" });
return;
}
logExportStage("mode-selected", { insertMode });
try {
// Step 1: table-data context — existing backend table export
if (await exportFullTableDataViaBackend("sql", rowIds, "name", true, insertMode)) return;
logExportStage("backend-export-start");
const handledByBackend = await exportFullTableDataViaBackend("sql", rowIds, "name", true, insertMode);
logExportStage("backend-export-finished", { handledByBackend });
if (handledByBackend) {
logExportStage("done", { path: "backend" });
return;
}
// Step 2: query-result context — NEW backend streaming with background task
if (await exportQueryResultSqlViaBackend(rowIds, insertMode)) return;
logExportStage("query-backend-export-start");
const handledQueryByBackend = await exportQueryResultSqlViaBackend(rowIds, insertMode);
logExportStage("query-backend-export-finished", { handledByBackend: handledQueryByBackend });
if (handledQueryByBackend) {
logExportStage("done", { path: "query-backend" });
return;
}
// Step 3: fallback — local export (Web and edge-case scenarios)
const result = await resultToExport(rowIds, undefined, true, false);
logExportStage(
"result-ready",
{
columns: result.columns.length,
rows: result.rows.length,
values: isDebugLoggingEnabled() ? summarizeExportRows(result.rows) : undefined,
},
true,
);
logExportStage("row-remap-start");
const exportData = sqlInsertExportData(result);
logExportStage("row-remap-done", {
columns: exportData.columns.length,
rows: exportData.rows.length,
values: isDebugLoggingEnabled() ? summarizeExportRows(exportData.rows) : undefined,
});
logExportStage("sql-build-start");
const content = await formatSqlInsert({
databaseType: databaseType.value,
identifierQuote: options.identifierQuote?.value,
@@ -1358,10 +1418,23 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
rows: exportData.rows,
insertMode,
});
await saveTextFile(content, exportFileName(tableMeta.value?.tableName || "export", "sql", { preferFallback: true }), "SQL", "sql");
logExportStage(
"sql-build-done",
{
sqlChars: content.length,
},
true,
);
logExportStage("save-start", { contentChars: content.length }, true);
await saveTextFile(content, exportFileName(tableMeta.value?.tableName || "export", "sql", { preferFallback: true }), "SQL", "sql", { exportId, operation: "sql-insert-all" });
logExportStage("done", { contentChars: content.length });
toast(t("grid.exported"));
} catch (e: any) {
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
logExportStage("error", {
errorName: e?.name || typeof e,
errorMessage: e?.message || String(e),
});
}
});
}