feat(postgres): support batch table cascade actions
This commit is contained in:
parent
a893f0e478
commit
1db11ea145
|
|
@ -52,7 +52,7 @@ import { isSchemaAware } from "@/lib/database/databaseCapabilities";
|
|||
import { supportsSchemaDiagram, supportsTableImport, supportsTableStructureEditing, supportsTableTruncate } from "@/lib/database/databaseFeatureSupport";
|
||||
import { codeMirrorSqlDialect, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { buildTableSelectSql } from "@/lib/table/tableSelectSql";
|
||||
import { buildDropObjectSql, buildDropTableSql, buildDuplicateTableStructureSql, buildCopyTableDataSql, buildEmptyTableSql, buildTruncateTableSql, supportsDropTableCascade, type TableAdminSqlOptions } from "@/lib/database/dbAdminSql";
|
||||
import { buildDropObjectSql, buildDropTableSql, buildDuplicateTableStructureSql, buildCopyTableDataSql, buildEmptyTableSql, buildTruncateTableSql, supportsDropTableCascade, supportsTruncateTableCascade, type TableAdminSqlOptions } from "@/lib/database/dbAdminSql";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { buildExecutableObjectSourceStatements, buildRoutineRenameObjectSourceStatements, executeObjectSourceSave, supportsSourceBackedRoutineRename } from "@/lib/table/objectSourceEditor";
|
||||
import { buildRenameObjectSql, supportsObjectRename } from "@/lib/table/objectRenameSql";
|
||||
|
|
@ -134,6 +134,7 @@ const showDropConfirm = ref(false);
|
|||
const dropTarget = ref<ObjectBrowserRow | null>(null);
|
||||
const dropPreviewSql = ref("");
|
||||
const dropTableCascade = ref(false);
|
||||
const batchDropCascade = ref(false);
|
||||
const showRenameDialog = ref(false);
|
||||
const renameTarget = ref<ObjectBrowserRow | null>(null);
|
||||
const renameInput = ref("");
|
||||
|
|
@ -142,6 +143,7 @@ const renamePreviewSqlText = ref("");
|
|||
const showTruncateConfirm = ref(false);
|
||||
const truncateTarget = ref<ObjectBrowserRow | null>(null);
|
||||
const truncatePreviewSql = ref("");
|
||||
const truncateTableCascade = ref(false);
|
||||
const showEmptyConfirm = ref(false);
|
||||
const emptyTarget = ref<ObjectBrowserRow | null>(null);
|
||||
const emptyPreviewSql = ref("");
|
||||
|
|
@ -156,6 +158,9 @@ const selectedTableIds = ref<Set<string>>(new Set());
|
|||
const expandedPartitionParentIds = ref<Set<string>>(new Set());
|
||||
const showBatchDropConfirm = ref(false);
|
||||
const batchDropPreviewSql = ref("");
|
||||
const showBatchTruncateConfirm = ref(false);
|
||||
const batchTruncatePreviewSql = ref("");
|
||||
const batchTruncateCascade = ref(false);
|
||||
// Paste table dialog state
|
||||
const showPasteDialog = ref(false);
|
||||
const pasteTableMode = ref<PasteTableMode>("structure-and-data");
|
||||
|
|
@ -179,6 +184,7 @@ const { addTask: addExportTask } = useExportTracker();
|
|||
|
||||
const needsSchema = computed(() => isSchemaAware(props.connection.db_type) && !connectionUsesDatabaseObjectTreeMode(props.connection));
|
||||
const canDropTargetCascade = computed(() => dropTarget.value?.type === "TABLE" && supportsDropTableCascade(effectiveDatabaseType.value));
|
||||
const canTruncateTargetCascade = computed(() => !!truncateTarget.value && supportsTruncateTableCascade(effectiveDatabaseType.value));
|
||||
const tableCount = computed(() => rows.value.filter((row) => row.type === "TABLE").length);
|
||||
const viewCount = computed(() => rows.value.filter((row) => row.type === "VIEW").length);
|
||||
const materializedViewCount = computed(() => rows.value.filter((row) => row.type === "MATERIALIZED_VIEW").length);
|
||||
|
|
@ -249,6 +255,8 @@ const selectedTableRows = computed(() => {
|
|||
return selectableRows.value.filter((row) => ids.has(row.id));
|
||||
});
|
||||
const selectedTableCount = computed(() => selectedTableRows.value.length);
|
||||
const canBatchDropCascade = computed(() => selectedTableCount.value > 0 && supportsDropTableCascade(effectiveDatabaseType.value));
|
||||
const canBatchTruncateCascade = computed(() => selectedTableCount.value > 0 && supportsTruncateTableCascade(effectiveDatabaseType.value));
|
||||
const allVisibleTablesSelected = computed(() => visibleSelectableRows.value.length > 0 && visibleSelectableRows.value.every((row) => selectedTableIds.value.has(row.id)));
|
||||
|
||||
function iconFor(row: ObjectBrowserRow) {
|
||||
|
|
@ -798,8 +806,9 @@ async function fetchSortedTableRowsForDrop(): Promise<ObjectBrowserRow[]> {
|
|||
async function refreshBatchDropPreviewSql() {
|
||||
const statements: string[] = [];
|
||||
const sortedRows = await fetchSortedTableRowsForDrop();
|
||||
const useCascade = canBatchDropCascade.value && batchDropCascade.value;
|
||||
for (const row of sortedRows) {
|
||||
const sql = await buildDropTableSql(tableAdminSqlOptions(row)).catch(() => "");
|
||||
const sql = await buildDropTableSql(tableAdminSqlOptions(row, { cascade: useCascade })).catch(() => "");
|
||||
if (sql) statements.push(sql);
|
||||
}
|
||||
batchDropPreviewSql.value = statements.join("\n");
|
||||
|
|
@ -807,6 +816,7 @@ async function refreshBatchDropPreviewSql() {
|
|||
|
||||
function requestBatchDropTables() {
|
||||
if (selectedTableCount.value === 0) return;
|
||||
batchDropCascade.value = false;
|
||||
batchDropPreviewSql.value = "";
|
||||
void refreshBatchDropPreviewSql();
|
||||
showBatchDropConfirm.value = true;
|
||||
|
|
@ -816,8 +826,9 @@ async function confirmBatchDropTables() {
|
|||
const targets = await fetchSortedTableRowsForDrop();
|
||||
if (targets.length === 0) return;
|
||||
try {
|
||||
const useCascade = canBatchDropCascade.value && batchDropCascade.value;
|
||||
for (const row of targets) {
|
||||
const sql = await buildDropTableSql(tableAdminSqlOptions(row));
|
||||
const sql = await buildDropTableSql(tableAdminSqlOptions(row, { cascade: useCascade }));
|
||||
await api.executeQuery(props.connection.id, props.database, sql);
|
||||
closeDroppedTableObjectTabsForRow(row);
|
||||
}
|
||||
|
|
@ -830,6 +841,43 @@ async function confirmBatchDropTables() {
|
|||
}
|
||||
}
|
||||
|
||||
async function refreshBatchTruncatePreviewSql() {
|
||||
const statements: string[] = [];
|
||||
const useCascade = canBatchTruncateCascade.value && batchTruncateCascade.value;
|
||||
for (const row of selectedTableRows.value) {
|
||||
const sql = await buildTruncateTableSql(tableAdminSqlOptions(row, { cascade: useCascade })).catch(() => "");
|
||||
if (sql) statements.push(sql);
|
||||
}
|
||||
batchTruncatePreviewSql.value = statements.join("\n");
|
||||
}
|
||||
|
||||
function requestBatchTruncateTables() {
|
||||
if (selectedTableCount.value === 0 || !supportsTruncateTable.value) return;
|
||||
batchTruncateCascade.value = false;
|
||||
batchTruncatePreviewSql.value = "";
|
||||
void refreshBatchTruncatePreviewSql();
|
||||
showBatchTruncateConfirm.value = true;
|
||||
}
|
||||
|
||||
async function confirmBatchTruncateTables() {
|
||||
const targets = [...selectedTableRows.value];
|
||||
if (targets.length === 0) return;
|
||||
try {
|
||||
const useCascade = canBatchTruncateCascade.value && batchTruncateCascade.value;
|
||||
for (const row of targets) {
|
||||
const sql = await buildTruncateTableSql(tableAdminSqlOptions(row, { cascade: useCascade }));
|
||||
await api.executeQuery(props.connection.id, props.database, sql);
|
||||
}
|
||||
toast(t("objects.batchTruncateSuccess", { count: targets.length }));
|
||||
clearTableSelection();
|
||||
showBatchTruncateConfirm.value = false;
|
||||
await reload();
|
||||
await connectionStore.refreshObjectListTreeNode(props.connection.id, props.database, selectedSchema.value);
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
async function exportStructure(row: ObjectBrowserRow) {
|
||||
try {
|
||||
const schema = row.schema || selectedSchema.value || props.database;
|
||||
|
|
@ -1136,11 +1184,12 @@ function tableAdminSqlOptions(row: ObjectBrowserRow, options?: { cascade?: boole
|
|||
|
||||
async function refreshTruncatePreviewSql(row: ObjectBrowserRow) {
|
||||
truncatePreviewSql.value = "";
|
||||
truncatePreviewSql.value = await buildTruncateTableSql(tableAdminSqlOptions(row)).catch(() => "");
|
||||
truncatePreviewSql.value = await buildTruncateTableSql(tableAdminSqlOptions(row, { cascade: canTruncateTargetCascade.value && truncateTableCascade.value })).catch(() => "");
|
||||
}
|
||||
|
||||
function requestTruncateTable(row: ObjectBrowserRow) {
|
||||
truncateTarget.value = row;
|
||||
truncateTableCascade.value = false;
|
||||
void refreshTruncatePreviewSql(row);
|
||||
showTruncateConfirm.value = true;
|
||||
}
|
||||
|
|
@ -1149,7 +1198,7 @@ async function confirmTruncateTable() {
|
|||
const row = truncateTarget.value;
|
||||
if (!row) return;
|
||||
try {
|
||||
const sql = truncatePreviewSql.value || (await buildTruncateTableSql(tableAdminSqlOptions(row)));
|
||||
const sql = truncatePreviewSql.value || (await buildTruncateTableSql(tableAdminSqlOptions(row, { cascade: canTruncateTargetCascade.value && truncateTableCascade.value })));
|
||||
await api.executeQuery(props.connection.id, props.database, sql);
|
||||
toast(t("contextMenu.truncateTableSuccess", { name: row.name }));
|
||||
} catch (e: any) {
|
||||
|
|
@ -1604,7 +1653,7 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
|||
{{ t("objects.pasteTableSelected") }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="selectedTableCount > 0" class="flex h-9 shrink-0 items-center gap-2 border-b bg-muted/30 px-3 text-xs">
|
||||
<div v-if="selectedTableCount > 0" class="flex h-9 shrink-0 items-center gap-2 overflow-x-auto border-b bg-muted/30 px-3 text-xs">
|
||||
<div class="min-w-0 flex-1 truncate text-muted-foreground">
|
||||
{{ t("objects.selectedTables", { count: selectedTableCount }) }}
|
||||
</div>
|
||||
|
|
@ -1616,6 +1665,10 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
|||
<Clipboard class="mr-1.5 h-3.5 w-3.5" />
|
||||
{{ t("objects.copyTableSelected") }}
|
||||
</Button>
|
||||
<Button v-if="supportsTruncateTable" variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchTruncateTables">
|
||||
<Scissors class="mr-1.5 h-3.5 w-3.5" />
|
||||
{{ t("objects.truncateSelected") }}
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchDropTables">
|
||||
<Trash2 class="mr-1.5 h-3.5 w-3.5" />
|
||||
{{ t("objects.dropSelected") }}
|
||||
|
|
@ -1848,7 +1901,36 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
|||
</template>
|
||||
</DangerConfirmDialog>
|
||||
|
||||
<DangerConfirmDialog v-model:open="showBatchDropConfirm" :title="t('objects.confirmBatchDropTitle')" :message="t('objects.confirmBatchDropMessage', { count: selectedTableCount })" :sql="batchDropPreviewSql" :confirm-label="t('objects.dropSelected')" @confirm="confirmBatchDropTables" />
|
||||
<DangerConfirmDialog v-model:open="showBatchDropConfirm" :title="t('objects.confirmBatchDropTitle')" :message="t('objects.confirmBatchDropMessage', { count: selectedTableCount })" :sql="batchDropPreviewSql" :confirm-label="t('objects.dropSelected')" @confirm="confirmBatchDropTables">
|
||||
<template v-if="canBatchDropCascade" #options>
|
||||
<label class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<input v-model="batchDropCascade" type="checkbox" class="mt-0.5 h-3.5 w-3.5 shrink-0 accent-primary" @change="refreshBatchDropPreviewSql()" />
|
||||
<span class="grid gap-0.5">
|
||||
<span class="font-medium text-foreground">{{ t("contextMenu.dropTableCascade") }}</span>
|
||||
<span class="text-xs leading-5 text-muted-foreground">{{ t("contextMenu.dropTableCascadeHint") }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
</DangerConfirmDialog>
|
||||
|
||||
<DangerConfirmDialog
|
||||
v-model:open="showBatchTruncateConfirm"
|
||||
:title="t('objects.confirmBatchTruncateTitle')"
|
||||
:message="t('objects.confirmBatchTruncateMessage', { count: selectedTableCount })"
|
||||
:sql="batchTruncatePreviewSql"
|
||||
:confirm-label="t('objects.truncateSelected')"
|
||||
@confirm="confirmBatchTruncateTables"
|
||||
>
|
||||
<template v-if="canBatchTruncateCascade" #options>
|
||||
<label class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<input v-model="batchTruncateCascade" type="checkbox" class="mt-0.5 h-3.5 w-3.5 shrink-0 accent-primary" @change="refreshBatchTruncatePreviewSql()" />
|
||||
<span class="grid gap-0.5">
|
||||
<span class="font-medium text-foreground">{{ t("contextMenu.truncateTableCascade") }}</span>
|
||||
<span class="text-xs leading-5 text-muted-foreground">{{ t("contextMenu.truncateTableCascadeHint") }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
</DangerConfirmDialog>
|
||||
|
||||
<Dialog v-model:open="showRenameDialog">
|
||||
<DialogContent class="sm:max-w-[420px]">
|
||||
|
|
@ -1876,7 +1958,17 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
|||
:sql="truncatePreviewSql"
|
||||
:confirm-label="t('contextMenu.truncateTable')"
|
||||
@confirm="confirmTruncateTable"
|
||||
/>
|
||||
>
|
||||
<template v-if="canTruncateTargetCascade" #options>
|
||||
<label class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<input v-model="truncateTableCascade" type="checkbox" class="mt-0.5 h-3.5 w-3.5 shrink-0 accent-primary" @change="truncateTarget && refreshTruncatePreviewSql(truncateTarget)" />
|
||||
<span class="grid gap-0.5">
|
||||
<span class="font-medium text-foreground">{{ t("contextMenu.truncateTableCascade") }}</span>
|
||||
<span class="text-xs leading-5 text-muted-foreground">{{ t("contextMenu.truncateTableCascadeHint") }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
</DangerConfirmDialog>
|
||||
|
||||
<DangerConfirmDialog v-model:open="showEmptyConfirm" :title="t('contextMenu.confirmEmptyTableTitle')" :message="t('contextMenu.confirmEmptyTableMessage', { name: emptyTarget?.name ?? '' })" :sql="emptyPreviewSql" :confirm-label="t('contextMenu.emptyTable')" @confirm="confirmEmptyTable" />
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ import {
|
|||
buildEmptyTableSql,
|
||||
buildTruncateTableSql,
|
||||
supportsDropTableCascade,
|
||||
supportsTruncateTableCascade,
|
||||
supportsSchemaComment,
|
||||
type DropTableChildObjectSqlOptions,
|
||||
type DropObjectSqlOptions,
|
||||
|
|
@ -1704,6 +1705,7 @@ async function duplicateConnection() {
|
|||
const showDropTableConfirm = ref(false);
|
||||
const showDropTableChildObjectConfirm = ref(false);
|
||||
const showBatchDropConfirm = ref(false);
|
||||
const showBatchTruncateConfirm = ref(false);
|
||||
const showStructurePreviewDialog = ref(false);
|
||||
const showStructureDocCopyDialog = ref(false);
|
||||
const structurePreviewSql = ref("");
|
||||
|
|
@ -1721,11 +1723,15 @@ const renameObjectError = ref("");
|
|||
const renameObjectPreviewSql = ref("");
|
||||
const dropTablePreviewSql = ref("");
|
||||
const dropTableCascade = ref(false);
|
||||
const batchDropCascade = ref(false);
|
||||
const emptyTablePreviewSql = ref("");
|
||||
const truncateTablePreviewSql = ref("");
|
||||
const truncateTableCascade = ref(false);
|
||||
const dropObjectPreviewSql = ref("");
|
||||
const dropTableChildObjectPreviewSql = ref("");
|
||||
const batchDropPreviewSql = ref("");
|
||||
const batchTruncatePreviewSql = ref("");
|
||||
const batchTruncateCascade = ref(false);
|
||||
const dropDatabasePreviewSql = ref("");
|
||||
const dropSchemaPreviewSql = ref("");
|
||||
const showDuplicateDialog = ref(false);
|
||||
|
|
@ -2019,6 +2025,16 @@ function selectedBatchDropTargets(): TreeNode[] {
|
|||
return selected;
|
||||
}
|
||||
|
||||
function selectedBatchTableTargets(): TreeNode[] {
|
||||
const targets = selectedBatchDropTargets();
|
||||
return targets.length > 1 && targets.every((node) => node.type === "table") ? targets : [];
|
||||
}
|
||||
|
||||
function selectedBatchTruncateTargets(): TreeNode[] {
|
||||
const targets = selectedBatchTableTargets();
|
||||
return targets.every((node) => supportsTableTruncate(databaseTypeForNode(node))) ? targets : [];
|
||||
}
|
||||
|
||||
function selectedBatchMongoIndexTargets(): TreeNode[] {
|
||||
const targets = selectedBatchDropTargets();
|
||||
return targets.length > 1 && targets.every((node) => canDropMongoIndexNode(node)) ? targets : [];
|
||||
|
|
@ -2056,12 +2072,25 @@ function batchDropConfirmMessage(): string {
|
|||
return t("contextMenu.confirmBatchDropMessage", { count: targets.length });
|
||||
}
|
||||
|
||||
async function dropSqlForTreeNode(node: TreeNode): Promise<string | null> {
|
||||
function batchTruncateMenuLabel(): string {
|
||||
return t("contextMenu.batchTruncate", { count: selectedBatchTruncateTargets().length });
|
||||
}
|
||||
|
||||
function batchTruncateConfirmTitle(): string {
|
||||
return t("contextMenu.confirmBatchTruncateTitle", { count: selectedBatchTruncateTargets().length });
|
||||
}
|
||||
|
||||
function batchTruncateConfirmMessage(): string {
|
||||
return t("contextMenu.confirmBatchTruncateMessage", { count: selectedBatchTruncateTargets().length });
|
||||
}
|
||||
|
||||
async function dropSqlForTreeNode(node: TreeNode, options?: { cascade?: boolean }): Promise<string | null> {
|
||||
if (node.type === "table" && node.connectionId && node.database) {
|
||||
return buildDropTableSql({
|
||||
databaseType: databaseTypeForNode(node),
|
||||
schema: node.schema,
|
||||
tableName: node.label,
|
||||
cascade: options?.cascade && supportsDropTableCascade(databaseTypeForNode(node)),
|
||||
});
|
||||
}
|
||||
const objectOptions = dropObjectSqlOptionsForNode(node);
|
||||
|
|
@ -2074,6 +2103,16 @@ async function dropSqlForTreeNode(node: TreeNode): Promise<string | null> {
|
|||
return null;
|
||||
}
|
||||
|
||||
async function truncateSqlForTreeNode(node: TreeNode, options?: { cascade?: boolean }): Promise<string | null> {
|
||||
if (node.type !== "table" || !node.connectionId || !node.database || !supportsTableTruncate(databaseTypeForNode(node))) return null;
|
||||
return buildTruncateTableSql({
|
||||
databaseType: databaseTypeForNode(node),
|
||||
schema: node.schema,
|
||||
tableName: node.label,
|
||||
cascade: options?.cascade && supportsTruncateTableCascade(databaseTypeForNode(node)),
|
||||
});
|
||||
}
|
||||
|
||||
async function refreshBatchDropPreviewSql() {
|
||||
const targets = selectedBatchDropTargets();
|
||||
const mongoIndexTargets = selectedBatchMongoIndexTargets();
|
||||
|
|
@ -2082,19 +2121,39 @@ async function refreshBatchDropPreviewSql() {
|
|||
return;
|
||||
}
|
||||
const statements: string[] = [];
|
||||
const useCascade = canBatchDropCascade.value && batchDropCascade.value;
|
||||
for (const target of targets) {
|
||||
const sql = await dropSqlForTreeNode(target);
|
||||
const sql = await dropSqlForTreeNode(target, { cascade: useCascade });
|
||||
if (sql) statements.push(sql);
|
||||
}
|
||||
batchDropPreviewSql.value = statements.join("\n");
|
||||
}
|
||||
|
||||
async function refreshBatchTruncatePreviewSql() {
|
||||
const targets = selectedBatchTruncateTargets();
|
||||
const statements: string[] = [];
|
||||
const useCascade = canBatchTruncateCascade.value && batchTruncateCascade.value;
|
||||
for (const target of targets) {
|
||||
const sql = await truncateSqlForTreeNode(target, { cascade: useCascade });
|
||||
if (sql) statements.push(sql);
|
||||
}
|
||||
batchTruncatePreviewSql.value = statements.join("\n");
|
||||
}
|
||||
|
||||
function requestBatchDrop() {
|
||||
if (!selectedBatchDropTargets().length) return;
|
||||
batchDropCascade.value = false;
|
||||
void refreshBatchDropPreviewSql();
|
||||
showBatchDropConfirm.value = true;
|
||||
}
|
||||
|
||||
function requestBatchTruncate() {
|
||||
if (!selectedBatchTruncateTargets().length) return;
|
||||
batchTruncateCascade.value = false;
|
||||
void refreshBatchTruncatePreviewSql();
|
||||
showBatchTruncateConfirm.value = true;
|
||||
}
|
||||
|
||||
function requestDropSelectedNodes(): boolean {
|
||||
const selected = selectedTreeNodesInVisibleOrder();
|
||||
if (selected.length > 1 && selected.some((node) => node.id === props.node.id)) {
|
||||
|
|
@ -2287,10 +2346,11 @@ async function confirmBatchDrop() {
|
|||
showBatchDropConfirm.value = false;
|
||||
return;
|
||||
}
|
||||
const useCascade = canBatchDropCascade.value && batchDropCascade.value;
|
||||
for (const target of targets) {
|
||||
if (!target.connectionId || !target.database) continue;
|
||||
await connectionStore.ensureConnected(target.connectionId);
|
||||
const sql = await dropSqlForTreeNode(target);
|
||||
const sql = await dropSqlForTreeNode(target, { cascade: useCascade });
|
||||
if (!sql) continue;
|
||||
await api.executeQuery(target.connectionId, target.database, sql, target.schema);
|
||||
closeDroppedTableObjectTabsForNode(target);
|
||||
|
|
@ -2303,6 +2363,25 @@ async function confirmBatchDrop() {
|
|||
}
|
||||
}
|
||||
|
||||
async function confirmBatchTruncate() {
|
||||
const targets = selectedBatchTruncateTargets();
|
||||
if (!targets.length) return;
|
||||
try {
|
||||
const useCascade = canBatchTruncateCascade.value && batchTruncateCascade.value;
|
||||
for (const target of targets) {
|
||||
if (!target.connectionId || !target.database) continue;
|
||||
await connectionStore.ensureConnected(target.connectionId);
|
||||
const sql = await truncateSqlForTreeNode(target, { cascade: useCascade });
|
||||
if (!sql) continue;
|
||||
await api.executeQuery(target.connectionId, target.database, sql, target.schema);
|
||||
}
|
||||
toast(t("contextMenu.batchTruncateSuccess", { count: targets.length }), 3000);
|
||||
showBatchTruncateConfirm.value = false;
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
const isTableNotView = computed(() => props.node.type === "table" && !isSqlServerLinkedNode(props.node));
|
||||
|
||||
const supportsTruncate = computed(() => {
|
||||
|
|
@ -2410,6 +2489,15 @@ const canEditSchemaComment = computed(() => {
|
|||
});
|
||||
|
||||
const canDropTableCascade = computed(() => props.node.type === "table" && supportsDropTableCascade(currentDatabaseType()));
|
||||
const canTruncateTableCascade = computed(() => props.node.type === "table" && supportsTruncateTableCascade(currentDatabaseType()));
|
||||
const canBatchDropCascade = computed(() => {
|
||||
const targets = selectedBatchTableTargets();
|
||||
return targets.length > 1 && targets.every((node) => supportsDropTableCascade(databaseTypeForNode(node)));
|
||||
});
|
||||
const canBatchTruncateCascade = computed(() => {
|
||||
const targets = selectedBatchTruncateTargets();
|
||||
return targets.length > 1 && targets.every((node) => supportsTruncateTableCascade(databaseTypeForNode(node)));
|
||||
});
|
||||
|
||||
function tableAdminSqlOptions(options?: { cascade?: boolean }): TableAdminSqlOptions {
|
||||
const result: TableAdminSqlOptions = {
|
||||
|
|
@ -2425,6 +2513,10 @@ function dropTableSqlOptions(): TableAdminSqlOptions {
|
|||
return tableAdminSqlOptions({ cascade: canDropTableCascade.value && dropTableCascade.value });
|
||||
}
|
||||
|
||||
function truncateTableSqlOptions(): TableAdminSqlOptions {
|
||||
return tableAdminSqlOptions({ cascade: canTruncateTableCascade.value && truncateTableCascade.value });
|
||||
}
|
||||
|
||||
async function refreshDropTablePreviewSql() {
|
||||
dropTablePreviewSql.value = "";
|
||||
dropTablePreviewSql.value = await buildDropTableSql(dropTableSqlOptions()).catch(() => "");
|
||||
|
|
@ -2437,7 +2529,7 @@ async function refreshEmptyTablePreviewSql() {
|
|||
|
||||
async function refreshTruncateTablePreviewSql() {
|
||||
truncateTablePreviewSql.value = "";
|
||||
truncateTablePreviewSql.value = await buildTruncateTableSql(tableAdminSqlOptions()).catch(() => "");
|
||||
truncateTablePreviewSql.value = await buildTruncateTableSql(truncateTableSqlOptions()).catch(() => "");
|
||||
}
|
||||
|
||||
function dropTable() {
|
||||
|
|
@ -2485,6 +2577,7 @@ async function confirmEmptyTable() {
|
|||
}
|
||||
|
||||
function truncateTable() {
|
||||
truncateTableCascade.value = false;
|
||||
void refreshTruncateTablePreviewSql();
|
||||
showTruncateTableConfirm.value = true;
|
||||
}
|
||||
|
|
@ -2494,7 +2587,7 @@ async function confirmTruncateTable() {
|
|||
if (!node.connectionId || !node.database) return;
|
||||
try {
|
||||
await connectionStore.ensureConnected(node.connectionId);
|
||||
const sql = truncateTablePreviewSql.value || (await buildTruncateTableSql(tableAdminSqlOptions()));
|
||||
const sql = truncateTablePreviewSql.value || (await buildTruncateTableSql(truncateTableSqlOptions()));
|
||||
await api.executeQuery(node.connectionId, node.database, sql, node.schema);
|
||||
toast(t("contextMenu.truncateTableSuccess", { name: node.label }), 3000);
|
||||
} catch (e: any) {
|
||||
|
|
@ -4328,8 +4421,11 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
|||
const node = props.node;
|
||||
const items: ContextMenuItem[] = [];
|
||||
const batchDropCount = selectedBatchDropTargets().length;
|
||||
const batchTruncateCount = selectedBatchTruncateTargets().length;
|
||||
const deleteMenuLabel = (singleLabel: string) => (batchDropCount > 1 ? batchDropMenuLabel() : singleLabel);
|
||||
const deleteMenuAction = (singleAction: () => void) => (batchDropCount > 1 ? requestBatchDrop : singleAction);
|
||||
const truncateMenuLabel = (singleLabel: string) => (batchTruncateCount > 1 ? batchTruncateMenuLabel() : singleLabel);
|
||||
const truncateMenuAction = (singleAction: () => void) => (batchTruncateCount > 1 ? requestBatchTruncate : singleAction);
|
||||
|
||||
// 1. Pin toggle
|
||||
if (canPin.value) {
|
||||
|
|
@ -4739,8 +4835,8 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
|||
items.push({ label: t("contextMenu.copyTable"), action: copyTableToClipboard, icon: Copy });
|
||||
if (supportsTruncate.value) {
|
||||
destructiveActions.push({
|
||||
label: t("contextMenu.truncateTable"),
|
||||
action: truncateTable,
|
||||
label: truncateMenuLabel(t("contextMenu.truncateTable")),
|
||||
action: truncateMenuAction(truncateTable),
|
||||
icon: Scissors,
|
||||
variant: "destructive" as const,
|
||||
});
|
||||
|
|
@ -5207,13 +5303,45 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
|||
:sql="truncateTablePreviewSql"
|
||||
:confirm-label="t('contextMenu.truncateTable')"
|
||||
@confirm="confirmTruncateTable"
|
||||
/>
|
||||
>
|
||||
<template v-if="canTruncateTableCascade" #options>
|
||||
<label class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<input v-model="truncateTableCascade" type="checkbox" class="mt-0.5 h-3.5 w-3.5 shrink-0 accent-primary" @change="refreshTruncateTablePreviewSql()" />
|
||||
<span class="grid gap-0.5">
|
||||
<span class="font-medium text-foreground">{{ t("contextMenu.truncateTableCascade") }}</span>
|
||||
<span class="text-xs leading-5 text-muted-foreground">{{ t("contextMenu.truncateTableCascadeHint") }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
</DangerConfirmDialog>
|
||||
|
||||
<DangerConfirmDialog v-model:open="showDropObjectConfirm" :title="dropObjectConfirmTitle()" :message="dropObjectConfirmMessage()" :sql="dropObjectPreviewSql" :confirm-label="dropObjectMenuLabel()" @confirm="confirmDropObject" />
|
||||
|
||||
<DangerConfirmDialog v-model:open="showDropTableChildObjectConfirm" :title="dropTableChildObjectConfirmTitle()" :message="dropTableChildObjectConfirmMessage()" :sql="dropTableChildObjectPreviewSql" :confirm-label="dropTableChildObjectMenuLabel()" @confirm="confirmDropTableChildObject" />
|
||||
|
||||
<DangerConfirmDialog v-model:open="showBatchDropConfirm" :title="batchDropConfirmTitle()" :message="batchDropConfirmMessage()" :sql="batchDropPreviewSql" :confirm-label="batchDropMenuLabel()" @confirm="confirmBatchDrop" />
|
||||
<DangerConfirmDialog v-model:open="showBatchDropConfirm" :title="batchDropConfirmTitle()" :message="batchDropConfirmMessage()" :sql="batchDropPreviewSql" :confirm-label="batchDropMenuLabel()" @confirm="confirmBatchDrop">
|
||||
<template v-if="canBatchDropCascade" #options>
|
||||
<label class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<input v-model="batchDropCascade" type="checkbox" class="mt-0.5 h-3.5 w-3.5 shrink-0 accent-primary" @change="refreshBatchDropPreviewSql()" />
|
||||
<span class="grid gap-0.5">
|
||||
<span class="font-medium text-foreground">{{ t("contextMenu.dropTableCascade") }}</span>
|
||||
<span class="text-xs leading-5 text-muted-foreground">{{ t("contextMenu.dropTableCascadeHint") }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
</DangerConfirmDialog>
|
||||
|
||||
<DangerConfirmDialog v-model:open="showBatchTruncateConfirm" :title="batchTruncateConfirmTitle()" :message="batchTruncateConfirmMessage()" :sql="batchTruncatePreviewSql" :confirm-label="batchTruncateMenuLabel()" @confirm="confirmBatchTruncate">
|
||||
<template v-if="canBatchTruncateCascade" #options>
|
||||
<label class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<input v-model="batchTruncateCascade" type="checkbox" class="mt-0.5 h-3.5 w-3.5 shrink-0 accent-primary" @change="refreshBatchTruncatePreviewSql()" />
|
||||
<span class="grid gap-0.5">
|
||||
<span class="font-medium text-foreground">{{ t("contextMenu.truncateTableCascade") }}</span>
|
||||
<span class="text-xs leading-5 text-muted-foreground">{{ t("contextMenu.truncateTableCascadeHint") }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
</DangerConfirmDialog>
|
||||
|
||||
<ProcedureExecutionDialog
|
||||
v-if="node.type === 'procedure' && node.connectionId && node.database"
|
||||
|
|
|
|||
|
|
@ -1401,6 +1401,8 @@ export default {
|
|||
confirmDropTableMessage: 'Are you sure you want to drop "{name}"? This will permanently delete the table and all its data.',
|
||||
dropTableCascade: "Use CASCADE",
|
||||
dropTableCascadeHint: "Also drop dependent objects such as views. Leave off to use PostgreSQL's default RESTRICT behavior.",
|
||||
truncateTableCascade: "Use CASCADE",
|
||||
truncateTableCascadeHint: "Also truncate tables that reference this table through foreign keys. Leave off to use PostgreSQL's default RESTRICT behavior.",
|
||||
confirmEmptyTableTitle: "Empty Table",
|
||||
confirmEmptyTableMessage: 'Are you sure you want to delete all data from "{name}"?',
|
||||
confirmTruncateTableTitle: "Truncate Table",
|
||||
|
|
@ -1419,6 +1421,7 @@ export default {
|
|||
dropForeignKey: "Drop Foreign Key",
|
||||
dropTrigger: "Drop Trigger",
|
||||
batchDrop: "Drop selected ({count})",
|
||||
batchTruncate: "Truncate selected ({count})",
|
||||
batchDropIndexes: "Drop Indexes ({count})",
|
||||
executeProcedure: "Execute Procedure",
|
||||
confirmExecuteProcedureTitle: "Execute Procedure",
|
||||
|
|
@ -1459,6 +1462,8 @@ export default {
|
|||
confirmDropBatchIndexesMessage: 'Are you sure you want to drop {count} selected indexes from "{table}"? This cannot be undone.',
|
||||
confirmBatchDropTitle: "Drop Selected Objects",
|
||||
confirmBatchDropMessage: "Are you sure you want to drop {count} selected objects? This cannot be undone.",
|
||||
confirmBatchTruncateTitle: "Truncate Selected Tables",
|
||||
confirmBatchTruncateMessage: "Are you sure you want to truncate {count} selected tables? This will remove all rows.",
|
||||
confirmDropProcedureTitle: "Drop Procedure",
|
||||
confirmDropProcedureMessage: 'Are you sure you want to drop procedure "{name}"?',
|
||||
confirmDropFunctionTitle: "Drop Function",
|
||||
|
|
@ -1469,6 +1474,7 @@ export default {
|
|||
dropTableChildObjectSuccess: '"{name}" dropped',
|
||||
dropAllIndexesSuccess: 'Dropped {count} indexes from "{name}"',
|
||||
batchDropSuccess: "Dropped {count} objects",
|
||||
batchTruncateSuccess: "Truncated {count} tables",
|
||||
emptyTableSuccess: 'All data deleted from "{name}"',
|
||||
truncateTableSuccess: 'Table "{name}" truncated',
|
||||
duplicateStructureSuccess: 'Table cloned as "{name}"',
|
||||
|
|
@ -1679,10 +1685,14 @@ export default {
|
|||
selectedTables: "{count} tables selected",
|
||||
exportSelected: "Export selected",
|
||||
dropSelected: "Drop selected",
|
||||
truncateSelected: "Truncate selected",
|
||||
clearSelection: "Clear selection",
|
||||
confirmBatchDropTitle: "Drop Selected Tables",
|
||||
confirmBatchDropMessage: "Are you sure you want to drop {count} selected tables? This cannot be undone.",
|
||||
confirmBatchTruncateTitle: "Truncate Selected Tables",
|
||||
confirmBatchTruncateMessage: "Are you sure you want to truncate {count} selected tables? This will remove all rows.",
|
||||
batchDropSuccess: "Dropped {count} tables",
|
||||
batchTruncateSuccess: "Truncated {count} tables",
|
||||
copyTableSelected: "Copy",
|
||||
pasteTableSelected: "Paste",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1359,6 +1359,8 @@ export default withEnglishFallback({
|
|||
confirmDropTableMessage: '¿Estás seguro de que deseas eliminar "{name}"? Esto borrará permanentemente la tabla y todos sus datos.',
|
||||
dropTableCascade: "Usar CASCADE",
|
||||
dropTableCascadeHint: "También elimina objetos dependientes, como vistas. Desactívalo para usar el comportamiento RESTRICT predeterminado de PostgreSQL.",
|
||||
truncateTableCascade: "Usar CASCADE",
|
||||
truncateTableCascadeHint: "También trunca las tablas que hacen referencia a esta tabla mediante claves foráneas. Desactívalo para usar el comportamiento RESTRICT predeterminado de PostgreSQL.",
|
||||
confirmEmptyTableTitle: "Vaciar tabla",
|
||||
confirmEmptyTableMessage: '¿Estás seguro de que deseas eliminar todos los datos de "{name}"?',
|
||||
confirmTruncateTableTitle: "Truncar tabla",
|
||||
|
|
@ -1376,6 +1378,7 @@ export default withEnglishFallback({
|
|||
dropForeignKey: "Eliminar clave foránea",
|
||||
dropTrigger: "Eliminar disparador",
|
||||
batchDrop: "Eliminar seleccionados ({count})",
|
||||
batchTruncate: "Truncar seleccionadas ({count})",
|
||||
executeProcedure: "Ejecutar procedimiento",
|
||||
confirmExecuteProcedureTitle: "Ejecutar procedimiento",
|
||||
confirmExecuteProcedureMessage: '¿Ejecutar el procedimiento "{name}"? Puedes completar o ajustar los valores de los parámetros primero.',
|
||||
|
|
@ -1415,6 +1418,8 @@ export default withEnglishFallback({
|
|||
confirmDropBatchIndexesMessage: '¿Seguro que deseas eliminar {count} índices seleccionados de "{table}"? Esta acción no se puede deshacer.',
|
||||
confirmBatchDropTitle: "Eliminar objetos seleccionados",
|
||||
confirmBatchDropMessage: "¿Seguro que deseas eliminar {count} objetos seleccionados? Esta acción no se puede deshacer.",
|
||||
confirmBatchTruncateTitle: "Truncar tablas seleccionadas",
|
||||
confirmBatchTruncateMessage: "¿Seguro que deseas truncar {count} tablas seleccionadas? Esto eliminará todas las filas.",
|
||||
confirmDropProcedureTitle: "Eliminar procedimiento",
|
||||
confirmDropProcedureMessage: '¿Seguro que deseas eliminar el procedimiento "{name}"?',
|
||||
confirmDropFunctionTitle: "Eliminar función",
|
||||
|
|
@ -1425,6 +1430,7 @@ export default withEnglishFallback({
|
|||
dropTableChildObjectSuccess: '"{name}" eliminado',
|
||||
dropAllIndexesSuccess: 'Se eliminaron {count} índices de "{name}"',
|
||||
batchDropSuccess: "{count} objetos eliminados",
|
||||
batchTruncateSuccess: "{count} tablas truncadas",
|
||||
emptyTableSuccess: 'Todos los datos eliminados de "{name}"',
|
||||
truncateTableSuccess: 'Tabla "{name}" truncada',
|
||||
duplicateStructureSuccess: 'Tabla clonada como "{name}"',
|
||||
|
|
@ -1622,10 +1628,14 @@ export default withEnglishFallback({
|
|||
selectedTables: "{count} tablas seleccionadas",
|
||||
exportSelected: "Exportar seleccionadas",
|
||||
dropSelected: "Eliminar seleccionadas",
|
||||
truncateSelected: "Truncar seleccionadas",
|
||||
clearSelection: "Limpiar selección",
|
||||
confirmBatchDropTitle: "Eliminar tablas seleccionadas",
|
||||
confirmBatchDropMessage: "¿Seguro que quieres eliminar {count} tablas seleccionadas? Esta acción no se puede deshacer.",
|
||||
confirmBatchTruncateTitle: "Truncar tablas seleccionadas",
|
||||
confirmBatchTruncateMessage: "¿Seguro que quieres truncar {count} tablas seleccionadas? Esto eliminará todas las filas.",
|
||||
batchDropSuccess: "{count} tablas eliminadas",
|
||||
batchTruncateSuccess: "{count} tablas truncadas",
|
||||
copyTableSelected: "Copiar",
|
||||
pasteTableSelected: "Pegar",
|
||||
sourceReadOnly: "Este código fuente es de solo lectura y no se puede editar.",
|
||||
|
|
|
|||
|
|
@ -1357,6 +1357,8 @@ export default withEnglishFallback({
|
|||
confirmDropTableMessage: 'Sei sicuro di voler eliminare (DROP) la tabella "{name}"? Questa operazione eliminerà permanentemente la tabella e tutti i suoi dati.',
|
||||
dropTableCascade: "Usa CASCADE",
|
||||
dropTableCascadeHint: "Elimina anche gli oggetti dipendenti, come le viste. Lascia disattivato per usare il comportamento RESTRICT predefinito di PostgreSQL.",
|
||||
truncateTableCascade: "Usa CASCADE",
|
||||
truncateTableCascadeHint: "Tronca anche le tabelle che fanno riferimento a questa tabella tramite chiavi esterne. Lascia disattivato per usare il comportamento RESTRICT predefinito di PostgreSQL.",
|
||||
confirmEmptyTableTitle: "Svuota Tabella",
|
||||
confirmEmptyTableMessage: 'Sei sicuro di voler eliminare tutti i dati da "{name}"?',
|
||||
confirmTruncateTableTitle: "Tronca Tabella",
|
||||
|
|
@ -1375,6 +1377,7 @@ export default withEnglishFallback({
|
|||
dropForeignKey: "Elimina Chiave Esterna",
|
||||
dropTrigger: "Elimina Trigger",
|
||||
batchDrop: "Elimina selezionati ({count})",
|
||||
batchTruncate: "Tronca selezionate ({count})",
|
||||
batchDropIndexes: "Elimina indici ({count})",
|
||||
executeProcedure: "Esegui Procedura",
|
||||
confirmExecuteProcedureTitle: "Esegui Procedura",
|
||||
|
|
@ -1413,6 +1416,8 @@ export default withEnglishFallback({
|
|||
confirmDropBatchIndexesMessage: 'Sei sicuro di voler eliminare {count} indici selezionati da "{table}"? Questa azione non può essere annullata.',
|
||||
confirmBatchDropTitle: "Elimina Oggetti Selezionati",
|
||||
confirmBatchDropMessage: "Sei sicuro di voler eliminare {count} oggetti selezionati? Questa azione non può essere annullata.",
|
||||
confirmBatchTruncateTitle: "Tronca Tabelle Selezionate",
|
||||
confirmBatchTruncateMessage: "Sei sicuro di voler troncare {count} tabelle selezionate? Questa operazione rimuoverà tutte le righe.",
|
||||
confirmDropProcedureTitle: "Elimina Procedura",
|
||||
confirmDropProcedureMessage: 'Sei sicuro di voler eliminare la procedura "{name}"?',
|
||||
confirmDropFunctionTitle: "Elimina Funzione",
|
||||
|
|
@ -1423,6 +1428,7 @@ export default withEnglishFallback({
|
|||
dropTableChildObjectSuccess: '"{name}" eliminato',
|
||||
dropAllIndexesSuccess: 'Eliminati {count} indici da "{name}"',
|
||||
batchDropSuccess: "Eliminati {count} oggetti",
|
||||
batchTruncateSuccess: "Troncate {count} tabelle",
|
||||
emptyTableSuccess: 'Tutti i dati eliminati da "{name}"',
|
||||
truncateTableSuccess: 'Tabella "{name}" troncata',
|
||||
duplicateStructureSuccess: 'Tabella clonata come "{name}"',
|
||||
|
|
@ -1620,10 +1626,14 @@ export default withEnglishFallback({
|
|||
selectedTables: "{count} tabelle selezionate",
|
||||
exportSelected: "Esporta selezionate",
|
||||
dropSelected: "Elimina selezionate",
|
||||
truncateSelected: "Tronca selezionate",
|
||||
clearSelection: "Cancella selezione",
|
||||
confirmBatchDropTitle: "Elimina Tabelle Selezionate",
|
||||
confirmBatchDropMessage: "Sei sicuro di voler eliminare {count} tabelle selezionate? Questa azione non può essere annullata.",
|
||||
confirmBatchTruncateTitle: "Tronca Tabelle Selezionate",
|
||||
confirmBatchTruncateMessage: "Sei sicuro di voler troncare {count} tabelle selezionate? Questa operazione rimuoverà tutte le righe.",
|
||||
batchDropSuccess: "Eliminate {count} tabelle",
|
||||
batchTruncateSuccess: "Troncate {count} tabelle",
|
||||
copyTableSelected: "Copia",
|
||||
pasteTableSelected: "Incolla",
|
||||
sourceReadOnly: "Il codice sorgente è di sola lettura, non modificabile.",
|
||||
|
|
|
|||
|
|
@ -1355,6 +1355,8 @@ export default withEnglishFallback({
|
|||
confirmDropTableMessage: "本当にテーブル「{name}」をドロップ(削除)しますか?テーブルとすべてのデータが永久に削除されます。",
|
||||
dropTableCascade: "CASCADE を使用",
|
||||
dropTableCascadeHint: "ビューなど、このテーブルに依存するオブジェクトも削除します。オフの場合は PostgreSQL の既定の RESTRICT を使用します。",
|
||||
truncateTableCascade: "CASCADE を使用",
|
||||
truncateTableCascadeHint: "外部キーでこのテーブルを参照するテーブルもトランケートします。オフの場合は PostgreSQL の既定の RESTRICT を使用します。",
|
||||
confirmEmptyTableTitle: "テーブルを空にする",
|
||||
confirmEmptyTableMessage: "本当に「{name}」のすべてのデータを削除しますか?",
|
||||
confirmTruncateTableTitle: "テーブルをトランケート",
|
||||
|
|
@ -1370,6 +1372,7 @@ export default withEnglishFallback({
|
|||
dropForeignKey: "外部キーを削除",
|
||||
dropTrigger: "トリガーを削除",
|
||||
batchDrop: "選択を削除({count}件)",
|
||||
batchTruncate: "選択をトランケート({count}件)",
|
||||
executeProcedure: "プロシージャを実行",
|
||||
confirmExecuteProcedureTitle: "プロシージャを実行",
|
||||
confirmExecuteProcedureMessage: "プロシージャ「{name}」を実行しますか?実行前に入力パラメータの値を設定・調整できます。",
|
||||
|
|
@ -1409,6 +1412,8 @@ export default withEnglishFallback({
|
|||
confirmDropBatchIndexesMessage: "本当に「{table}」から選択した {count} 個のインデックスを削除しますか?この操作は取り消せません。",
|
||||
confirmBatchDropTitle: "選択したオブジェクトを削除",
|
||||
confirmBatchDropMessage: "選択した{count}個のオブジェクトを削除しますか?この操作は取り消せません。",
|
||||
confirmBatchTruncateTitle: "選択したテーブルをトランケート",
|
||||
confirmBatchTruncateMessage: "選択した{count}テーブルをトランケートしますか?すべての行が削除されます。",
|
||||
confirmDropProcedureTitle: "プロシージャを削除",
|
||||
confirmDropProcedureMessage: "本当にプロシージャ「{name}」を削除しますか?",
|
||||
confirmDropFunctionTitle: "関数を削除",
|
||||
|
|
@ -1419,6 +1424,7 @@ export default withEnglishFallback({
|
|||
dropTableChildObjectSuccess: "「{name}」を削除しました",
|
||||
dropAllIndexesSuccess: "「{name}」から {count} 個のインデックスを削除しました",
|
||||
batchDropSuccess: "{count}個のオブジェクトを削除しました",
|
||||
batchTruncateSuccess: "{count}テーブルをトランケートしました",
|
||||
emptyTableSuccess: "「{name}」のすべてのデータを削除しました",
|
||||
truncateTableSuccess: "テーブル「{name}」をトランケートしました",
|
||||
duplicateStructureSuccess: "テーブルを「{name}」として複製しました",
|
||||
|
|
@ -1653,10 +1659,14 @@ export default withEnglishFallback({
|
|||
selectedTables: "{count}テーブル選択中",
|
||||
exportSelected: "選択をエクスポート",
|
||||
dropSelected: "選択を削除",
|
||||
truncateSelected: "選択をトランケート",
|
||||
clearSelection: "選択をクリア",
|
||||
confirmBatchDropTitle: "選択したテーブルを削除",
|
||||
confirmBatchDropMessage: "選択した{count}テーブルを削除しますか?この操作は取り消せません。",
|
||||
confirmBatchTruncateTitle: "選択したテーブルをトランケート",
|
||||
confirmBatchTruncateMessage: "選択した{count}テーブルをトランケートしますか?すべての行が削除されます。",
|
||||
batchDropSuccess: "{count}テーブルを削除しました",
|
||||
batchTruncateSuccess: "{count}テーブルをトランケートしました",
|
||||
copyTableSelected: "コピー",
|
||||
pasteTableSelected: "貼り付け",
|
||||
sourceReadOnly: "このソースは読み取り専用で、編集できません。",
|
||||
|
|
|
|||
|
|
@ -1358,6 +1358,8 @@ export default withEnglishFallback({
|
|||
confirmDropTableMessage: 'Tem certeza de que deseja remover "{name}"? Isso excluirá permanentemente a tabela e todos os seus dados.',
|
||||
dropTableCascade: "Usar CASCADE",
|
||||
dropTableCascadeHint: "Também remove objetos dependentes, como views. Deixe desmarcado para usar o comportamento RESTRICT padrão do PostgreSQL.",
|
||||
truncateTableCascade: "Usar CASCADE",
|
||||
truncateTableCascadeHint: "Também trunca tabelas que referenciam esta tabela por chaves estrangeiras. Deixe desmarcado para usar o comportamento RESTRICT padrão do PostgreSQL.",
|
||||
confirmEmptyTableTitle: "Esvaziar Tabela",
|
||||
confirmEmptyTableMessage: 'Tem certeza de que deseja excluir todos os dados de "{name}"?',
|
||||
confirmTruncateTableTitle: "Truncar Tabela",
|
||||
|
|
@ -1375,6 +1377,7 @@ export default withEnglishFallback({
|
|||
dropForeignKey: "Remover Chave Estrangeira",
|
||||
dropTrigger: "Remover Gatilho",
|
||||
batchDrop: "Remover selecionados ({count})",
|
||||
batchTruncate: "Truncar selecionadas ({count})",
|
||||
executeProcedure: "Executar Procedimento",
|
||||
confirmExecuteProcedureTitle: "Executar Procedimento",
|
||||
confirmExecuteProcedureMessage: 'Executar o procedimento "{name}"? Você pode preencher ou ajustar os valores dos parâmetros primeiro.',
|
||||
|
|
@ -1414,6 +1417,8 @@ export default withEnglishFallback({
|
|||
confirmDropBatchIndexesMessage: 'Tem certeza de que deseja remover {count} índices selecionados de "{table}"? Esta ação não pode ser desfeita.',
|
||||
confirmBatchDropTitle: "Remover Objetos Selecionados",
|
||||
confirmBatchDropMessage: "Tem certeza de que deseja remover {count} objetos selecionados? Esta ação não pode ser desfeita.",
|
||||
confirmBatchTruncateTitle: "Truncar tabelas selecionadas",
|
||||
confirmBatchTruncateMessage: "Tem certeza de que deseja truncar {count} tabelas selecionadas? Isso removerá todas as linhas.",
|
||||
confirmDropProcedureTitle: "Remover Procedimento",
|
||||
confirmDropProcedureMessage: 'Tem certeza de que deseja remover o procedimento "{name}"?',
|
||||
confirmDropFunctionTitle: "Remover Função",
|
||||
|
|
@ -1424,6 +1429,7 @@ export default withEnglishFallback({
|
|||
dropTableChildObjectSuccess: '"{name}" removido',
|
||||
dropAllIndexesSuccess: 'Removidos {count} índices de "{name}"',
|
||||
batchDropSuccess: "{count} objetos removidos",
|
||||
batchTruncateSuccess: "{count} tabelas truncadas",
|
||||
emptyTableSuccess: 'Todos os dados excluídos de "{name}"',
|
||||
truncateTableSuccess: 'Tabela "{name}" truncada',
|
||||
duplicateStructureSuccess: 'Tabela clonada como "{name}"',
|
||||
|
|
@ -1621,10 +1627,14 @@ export default withEnglishFallback({
|
|||
selectedTables: "{count} tabelas selecionadas",
|
||||
exportSelected: "Exportar selecionados",
|
||||
dropSelected: "Remover selecionados",
|
||||
truncateSelected: "Truncar selecionadas",
|
||||
clearSelection: "Limpar seleção",
|
||||
confirmBatchDropTitle: "Remover tabelas selecionadas",
|
||||
confirmBatchDropMessage: "Tem certeza de que deseja remover {count} tabelas selecionadas? Esta ação não pode ser desfeita.",
|
||||
confirmBatchTruncateTitle: "Truncar tabelas selecionadas",
|
||||
confirmBatchTruncateMessage: "Tem certeza de que deseja truncar {count} tabelas selecionadas? Isso removerá todas as linhas.",
|
||||
batchDropSuccess: "{count} tabelas removidas",
|
||||
batchTruncateSuccess: "{count} tabelas truncadas",
|
||||
copyTableSelected: "Copiar",
|
||||
pasteTableSelected: "Colar",
|
||||
sourceReadOnly: "O código fonte é somente leitura e não pode ser editado.",
|
||||
|
|
|
|||
|
|
@ -1403,6 +1403,8 @@ export default withEnglishFallback({
|
|||
confirmDropTableMessage: "确定要删除「{name}」吗?这将永久删除表及其所有数据。",
|
||||
dropTableCascade: "使用 CASCADE 强制删除",
|
||||
dropTableCascadeHint: "同时删除依赖该表的对象(如视图)。未勾选时使用 PostgreSQL 默认的 RESTRICT。",
|
||||
truncateTableCascade: "使用 CASCADE 强制截断",
|
||||
truncateTableCascadeHint: "同时截断通过外键引用该表的表。未勾选时使用 PostgreSQL 默认的 RESTRICT。",
|
||||
confirmEmptyTableTitle: "清空数据",
|
||||
confirmEmptyTableMessage: "确定要删除「{name}」中的所有数据吗?",
|
||||
confirmTruncateTableTitle: "截断表",
|
||||
|
|
@ -1421,6 +1423,7 @@ export default withEnglishFallback({
|
|||
dropForeignKey: "删除外键",
|
||||
dropTrigger: "删除触发器",
|
||||
batchDrop: "删除所选({count})",
|
||||
batchTruncate: "截断所选({count})",
|
||||
batchDropIndexes: "删除索引({count})",
|
||||
executeProcedure: "执行过程",
|
||||
confirmExecuteProcedureTitle: "执行存储过程",
|
||||
|
|
@ -1461,6 +1464,8 @@ export default withEnglishFallback({
|
|||
confirmDropBatchIndexesMessage: "确定要从「{table}」删除已选择的 {count} 个索引吗?此操作不可撤销。",
|
||||
confirmBatchDropTitle: "删除所选对象",
|
||||
confirmBatchDropMessage: "确定要删除已选择的 {count} 个对象吗?此操作不可撤销。",
|
||||
confirmBatchTruncateTitle: "截断所选表",
|
||||
confirmBatchTruncateMessage: "确定要截断已选择的 {count} 张表吗?这将删除所有行。",
|
||||
confirmDropProcedureTitle: "删除存储过程",
|
||||
confirmDropProcedureMessage: "确定要删除存储过程「{name}」吗?",
|
||||
confirmDropFunctionTitle: "删除函数",
|
||||
|
|
@ -1471,6 +1476,7 @@ export default withEnglishFallback({
|
|||
dropTableChildObjectSuccess: "「{name}」已删除",
|
||||
dropAllIndexesSuccess: "已从「{name}」删除 {count} 个索引",
|
||||
batchDropSuccess: "已删除 {count} 个对象",
|
||||
batchTruncateSuccess: "已截断 {count} 张表",
|
||||
emptyTableSuccess: "已清空「{name}」的所有数据",
|
||||
truncateTableSuccess: "表「{name}」已截断",
|
||||
duplicateStructureSuccess: "已克隆为新表「{name}」",
|
||||
|
|
@ -1679,10 +1685,14 @@ export default withEnglishFallback({
|
|||
selectedTables: "已选择 {count} 张表",
|
||||
exportSelected: "导出所选",
|
||||
dropSelected: "删除所选",
|
||||
truncateSelected: "截断所选",
|
||||
clearSelection: "清空选择",
|
||||
confirmBatchDropTitle: "删除所选表",
|
||||
confirmBatchDropMessage: "确定要删除已选择的 {count} 张表吗?此操作不可撤销。",
|
||||
confirmBatchTruncateTitle: "截断所选表",
|
||||
confirmBatchTruncateMessage: "确定要截断已选择的 {count} 张表吗?这将删除所有行。",
|
||||
batchDropSuccess: "已删除 {count} 张表",
|
||||
batchTruncateSuccess: "已截断 {count} 张表",
|
||||
copyTableSelected: "复制",
|
||||
pasteTableSelected: "粘贴",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1358,6 +1358,8 @@ export default withEnglishFallback({
|
|||
confirmDropTableMessage: "確定要刪除「{name}」嗎?這將永久刪除資料表及其所有資料。",
|
||||
dropTableCascade: "使用 CASCADE 強制刪除",
|
||||
dropTableCascadeHint: "同時刪除依賴此資料表的物件(例如檢視)。未勾選時使用 PostgreSQL 預設的 RESTRICT。",
|
||||
truncateTableCascade: "使用 CASCADE 強制截斷",
|
||||
truncateTableCascadeHint: "同時截斷透過外鍵參照此資料表的資料表。未勾選時使用 PostgreSQL 預設的 RESTRICT。",
|
||||
confirmEmptyTableTitle: "清空資料",
|
||||
confirmEmptyTableMessage: "確定要刪除「{name}」中的所有資料嗎?",
|
||||
confirmTruncateTableTitle: "截斷資料表",
|
||||
|
|
@ -1375,6 +1377,7 @@ export default withEnglishFallback({
|
|||
dropForeignKey: "刪除外鍵",
|
||||
dropTrigger: "刪除觸發器",
|
||||
batchDrop: "刪除所選({count})",
|
||||
batchTruncate: "截斷所選({count})",
|
||||
executeProcedure: "執行預存程序",
|
||||
confirmExecuteProcedureTitle: "執行預存程序",
|
||||
confirmExecuteProcedureMessage: "確認執行預存程序「{name}」?可先補充或調整參數值。",
|
||||
|
|
@ -1414,6 +1417,8 @@ export default withEnglishFallback({
|
|||
confirmDropBatchIndexesMessage: "確定要從「{table}」刪除已選擇的 {count} 個索引嗎?此操作無法復原。",
|
||||
confirmBatchDropTitle: "刪除所選物件",
|
||||
confirmBatchDropMessage: "確定要刪除已選擇的 {count} 個物件嗎?此操作無法復原。",
|
||||
confirmBatchTruncateTitle: "截斷所選資料表",
|
||||
confirmBatchTruncateMessage: "確定要截斷已選擇的 {count} 張資料表嗎?這將刪除所有行。",
|
||||
confirmDropProcedureTitle: "刪除預存程序",
|
||||
confirmDropProcedureMessage: "確定要刪除預存程序「{name}」嗎?",
|
||||
confirmDropFunctionTitle: "刪除函式",
|
||||
|
|
@ -1424,6 +1429,7 @@ export default withEnglishFallback({
|
|||
dropTableChildObjectSuccess: "「{name}」已刪除",
|
||||
dropAllIndexesSuccess: "已從「{name}」刪除 {count} 個索引",
|
||||
batchDropSuccess: "已刪除 {count} 個物件",
|
||||
batchTruncateSuccess: "已截斷 {count} 張資料表",
|
||||
emptyTableSuccess: "已清空「{name}」的所有資料",
|
||||
truncateTableSuccess: "資料表「{name}」已截斷",
|
||||
duplicateStructureSuccess: "已克隆為新資料表「{name}」",
|
||||
|
|
@ -1577,10 +1583,14 @@ export default withEnglishFallback({
|
|||
selectedTables: "已選擇 {count} 張資料表",
|
||||
exportSelected: "匯出所選",
|
||||
dropSelected: "刪除所選",
|
||||
truncateSelected: "截斷所選",
|
||||
clearSelection: "清空選擇",
|
||||
confirmBatchDropTitle: "刪除所選資料表",
|
||||
confirmBatchDropMessage: "確定要刪除已選擇的 {count} 張資料表嗎?此操作無法復原。",
|
||||
confirmBatchTruncateTitle: "截斷所選資料表",
|
||||
confirmBatchTruncateMessage: "確定要截斷已選擇的 {count} 張資料表嗎?這將刪除所有行。",
|
||||
batchDropSuccess: "已刪除 {count} 張資料表",
|
||||
batchTruncateSuccess: "已截斷 {count} 張資料表",
|
||||
copyTableSelected: "複製",
|
||||
pasteTableSelected: "貼上",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -87,11 +87,16 @@ export function buildTruncateTableSql(options: TableAdminSqlOptions): Promise<st
|
|||
}
|
||||
|
||||
const DROP_TABLE_CASCADE_DATABASE_TYPES: readonly DatabaseType[] = ["postgres", "redshift", "gaussdb", "kwdb", "kingbase", "highgo", "vastbase", "opengauss"];
|
||||
const TRUNCATE_TABLE_CASCADE_DATABASE_TYPES: readonly DatabaseType[] = ["postgres", "gaussdb", "kwdb", "kingbase", "highgo", "vastbase", "opengauss"];
|
||||
|
||||
export function supportsDropTableCascade(databaseType?: DatabaseType): boolean {
|
||||
return !!databaseType && DROP_TABLE_CASCADE_DATABASE_TYPES.includes(databaseType);
|
||||
}
|
||||
|
||||
export function supportsTruncateTableCascade(databaseType?: DatabaseType): boolean {
|
||||
return !!databaseType && TRUNCATE_TABLE_CASCADE_DATABASE_TYPES.includes(databaseType);
|
||||
}
|
||||
|
||||
export function buildDropDatabaseSql(options: DatabaseNameSqlOptions): Promise<string> {
|
||||
return api.buildDropDatabaseSql(options);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -442,10 +442,31 @@ pub fn build_truncate_table_sql(options: TableAdminSqlOptions) -> String {
|
|||
} else if matches!(options.database_type, Some(DatabaseType::Sqlite | DatabaseType::DuckDb)) {
|
||||
format!("DELETE FROM {table};")
|
||||
} else {
|
||||
format!("TRUNCATE TABLE {table};")
|
||||
// TRUNCATE CASCADE is PostgreSQL-family syntax; other dialects keep their existing default.
|
||||
let cascade = if options.cascade.unwrap_or(false) && supports_truncate_table_cascade(options.database_type) {
|
||||
" CASCADE"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!("TRUNCATE TABLE {table}{cascade};")
|
||||
}
|
||||
}
|
||||
|
||||
fn supports_truncate_table_cascade(database_type: Option<DatabaseType>) -> bool {
|
||||
matches!(
|
||||
database_type,
|
||||
Some(
|
||||
DatabaseType::Postgres
|
||||
| DatabaseType::Gaussdb
|
||||
| DatabaseType::Kwdb
|
||||
| DatabaseType::Kingbase
|
||||
| DatabaseType::Highgo
|
||||
| DatabaseType::Vastbase
|
||||
| DatabaseType::OpenGauss
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_drop_database_sql(options: DatabaseNameSqlOptions) -> String {
|
||||
format!("DROP DATABASE {};", quote_table_identifier(options.database_type, &options.name))
|
||||
}
|
||||
|
|
@ -1064,7 +1085,25 @@ mod tests {
|
|||
"DROP TABLE `events`;"
|
||||
);
|
||||
assert_eq!(build_empty_table_sql(options.clone()), "DELETE FROM \"public\".\"events\";");
|
||||
assert_eq!(build_truncate_table_sql(options), "TRUNCATE TABLE \"public\".\"events\";");
|
||||
assert_eq!(build_truncate_table_sql(options.clone()), "TRUNCATE TABLE \"public\".\"events\";");
|
||||
assert_eq!(
|
||||
build_truncate_table_sql(TableAdminSqlOptions {
|
||||
database_type: Some(DatabaseType::Postgres),
|
||||
schema: Some("public".to_string()),
|
||||
table_name: "events".to_string(),
|
||||
cascade: Some(true),
|
||||
}),
|
||||
"TRUNCATE TABLE \"public\".\"events\" CASCADE;"
|
||||
);
|
||||
assert_eq!(
|
||||
build_truncate_table_sql(TableAdminSqlOptions {
|
||||
database_type: Some(DatabaseType::Mysql),
|
||||
schema: None,
|
||||
table_name: "events".to_string(),
|
||||
cascade: Some(true),
|
||||
}),
|
||||
"TRUNCATE TABLE `events`;"
|
||||
);
|
||||
assert_eq!(
|
||||
build_empty_table_sql(TableAdminSqlOptions {
|
||||
database_type: Some(DatabaseType::ClickHouse),
|
||||
|
|
|
|||
Loading…
Reference in New Issue