mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-28 16:09:17 +00:00
feat(transfer): add upsert mode for data transfer (#105)
Add a third transfer mode that inserts new rows and updates existing rows based on primary key, supporting PostgreSQL, MySQL, SQLite, SQL Server, Oracle, and DuckDB with database-specific upsert syntax. Also fixes DuckDB primary key detection in transfer operations.
This commit is contained in:
@@ -10,7 +10,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
|
||||
import * as api from "@/lib/api";
|
||||
import type { TransferProgress } from "@/lib/api";
|
||||
import type { TransferProgress, TransferMode } from "@/lib/api";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
import { nextTransferTerminalState } from "@/lib/transferProgressState";
|
||||
import { ArrowRightLeft, Check, X, Loader2, Square, CheckSquare } from "lucide-vue-next";
|
||||
@@ -47,7 +47,7 @@ const targetSchema = ref("");
|
||||
|
||||
// Options
|
||||
const createTable = ref(true);
|
||||
const truncateBefore = ref(false);
|
||||
const transferMode = ref<TransferMode>("append");
|
||||
const batchSize = ref(1000);
|
||||
|
||||
// Transfer state
|
||||
@@ -188,7 +188,7 @@ function resetState() {
|
||||
targetDatabases.value = [];
|
||||
targetSchema.value = "";
|
||||
createTable.value = true;
|
||||
truncateBefore.value = false;
|
||||
transferMode.value = "append";
|
||||
batchSize.value = 1000;
|
||||
isTransferring.value = false;
|
||||
transferProgress.value.clear();
|
||||
@@ -230,7 +230,7 @@ async function startTransfer() {
|
||||
targetSchema: targetSchema.value,
|
||||
tables: [...selectedTables.value],
|
||||
createTable: createTable.value,
|
||||
truncateBefore: truncateBefore.value,
|
||||
mode: transferMode.value,
|
||||
batchSize: batchSize.value,
|
||||
};
|
||||
|
||||
@@ -437,10 +437,18 @@ const totalTransferred = computed(() =>
|
||||
<Square v-else class="w-3.5 h-3.5 text-muted-foreground/40 shrink-0" />
|
||||
{{ t("transfer.createTable") }}
|
||||
</div>
|
||||
<div class="flex items-center gap-2 cursor-pointer text-xs" @click="truncateBefore = !truncateBefore">
|
||||
<CheckSquare v-if="truncateBefore" class="w-3.5 h-3.5 text-primary shrink-0" />
|
||||
<Square v-else class="w-3.5 h-3.5 text-muted-foreground/40 shrink-0" />
|
||||
{{ t("transfer.truncateBefore") }}
|
||||
<div class="flex items-center gap-3">
|
||||
<Label class="text-xs shrink-0">{{ t("transfer.transferMode") }}</Label>
|
||||
<Select v-model="transferMode">
|
||||
<SelectTrigger class="h-7 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="append">{{ t("transfer.modeAppend") }}</SelectItem>
|
||||
<SelectItem value="overwrite">{{ t("transfer.modeOverwrite") }}</SelectItem>
|
||||
<SelectItem value="upsert">{{ t("transfer.modeUpsert") }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<Label class="text-xs shrink-0">{{ t("transfer.batchSize") }}</Label>
|
||||
|
||||
@@ -555,7 +555,10 @@ export default {
|
||||
selectAll: "Select All",
|
||||
deselectAll: "Deselect All",
|
||||
createTable: "Create table if not exists",
|
||||
truncateBefore: "Truncate target table before insert",
|
||||
transferMode: "Transfer mode",
|
||||
modeAppend: "Append (INSERT)",
|
||||
modeOverwrite: "Overwrite (TRUNCATE + INSERT)",
|
||||
modeUpsert: "Upsert (INSERT or UPDATE by PK)",
|
||||
batchSize: "Batch size",
|
||||
start: "Start Transfer",
|
||||
cancel: "Cancel",
|
||||
|
||||
@@ -547,7 +547,10 @@ export default {
|
||||
selectAll: "全选",
|
||||
deselectAll: "取消全选",
|
||||
createTable: "目标表不存在时自动建表",
|
||||
truncateBefore: "插入前清空目标表",
|
||||
transferMode: "传输模式",
|
||||
modeAppend: "追加 (INSERT)",
|
||||
modeOverwrite: "覆盖 (TRUNCATE + INSERT)",
|
||||
modeUpsert: "更新插入 (按主键 INSERT 或 UPDATE)",
|
||||
batchSize: "每批行数",
|
||||
start: "开始传输",
|
||||
cancel: "取消",
|
||||
|
||||
@@ -137,6 +137,7 @@ export type {
|
||||
SqlFileProgress,
|
||||
TransferRequest,
|
||||
TransferProgress,
|
||||
TransferMode,
|
||||
TableImportMode,
|
||||
TableImportStatus,
|
||||
TableImportColumnMapping,
|
||||
|
||||
+3
-1
@@ -450,6 +450,8 @@ export async function listenSqlFileProgress(handler: (progress: SqlFileProgress)
|
||||
}
|
||||
|
||||
// --- Data Transfer ---
|
||||
export type TransferMode = "append" | "overwrite" | "upsert";
|
||||
|
||||
export interface TransferRequest {
|
||||
transferId: string;
|
||||
sourceConnectionId: string;
|
||||
@@ -460,7 +462,7 @@ export interface TransferRequest {
|
||||
targetSchema: string;
|
||||
tables: string[];
|
||||
createTable: boolean;
|
||||
truncateBefore: boolean;
|
||||
mode: TransferMode;
|
||||
batchSize: number;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user