fix(oceanbase): preserve partition DDL and toggle storage options
This commit is contained in:
1 parent
8eba12ea2f
commit
fe0dd02f2b
36 files changed
+870
-125
No files matched your search
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { useId } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
defineProps<{ databaseType?: DatabaseType; disabled?: boolean }>();
|
||||
const settings = useSettingsStore();
|
||||
const { t } = useI18n();
|
||||
const id = useId();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="databaseType === 'oceanbase-oracle'" class="flex shrink-0 items-center gap-2 text-xs" :title="t('contextMenu.excludeDdlStorageHint')">
|
||||
<Switch :id="id" size="sm" :disabled="disabled" :model-value="settings.editorSettings.excludeDdlStorage !== false" @update:model-value="settings.updateEditorSettings({ excludeDdlStorage: $event })" />
|
||||
<label :for="id" class="cursor-pointer">{{ t("contextMenu.excludeDdlStorage") }}</label>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { applyDdlStoragePreference } from "@/lib/sql/ddlStorage";
|
||||
import DdlStorageToggle from "@/components/objects/DdlStorageToggle.vue";
|
||||
import { computed, nextTick, onUnmounted, ref, shallowRef, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Clipboard, Loader2, RefreshCw } from "@lucide/vue";
|
||||
@@ -47,7 +49,8 @@ const { toast } = useToast();
|
||||
const { isDark, themePalette } = useTheme();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
const ddlContent = ref("");
|
||||
const rawDdlContent = ref("");
|
||||
const ddlContent = computed(() => applyDdlStoragePreference(rawDdlContent.value, props.databaseType, settingsStore.editorSettings.excludeDdlStorage));
|
||||
const ddlLoading = ref(false);
|
||||
const ddlError = ref("");
|
||||
const ddlEditorContainer = ref<HTMLDivElement>();
|
||||
@@ -128,7 +131,7 @@ async function loadDdl(force = false) {
|
||||
);
|
||||
const formatDialect = props.formatDialect ?? props.dialect;
|
||||
const formatted = await formatSqlForDisplay(ddl, formatDialect, settingsStore.editorSettings.sqlFormatter);
|
||||
ddlContent.value = settingsStore.editorSettings.generateSqlQuoteIdentifiers ? formatted : omitDdlIdentifierQuotes(formatted, formatDialect);
|
||||
rawDdlContent.value = settingsStore.editorSettings.generateSqlQuoteIdentifiers ? formatted : omitDdlIdentifierQuotes(formatted, formatDialect);
|
||||
} catch (e: any) {
|
||||
ddlError.value = e?.message || String(e);
|
||||
} finally {
|
||||
@@ -144,7 +147,7 @@ watch(
|
||||
if (!open) return;
|
||||
const active = document.activeElement;
|
||||
editorRootToRestoreFocus = active instanceof HTMLElement ? active.closest(".cm-editor") : null;
|
||||
ddlContent.value = "";
|
||||
rawDdlContent.value = "";
|
||||
await loadDdl(settingsStore.editorSettings.refreshDdlOnOpen);
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -247,6 +250,11 @@ function copyDdlContent() {
|
||||
}
|
||||
}
|
||||
|
||||
watch(ddlContent, (content) => {
|
||||
const view = ddlEditorView.value;
|
||||
if (view) view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: content } });
|
||||
});
|
||||
|
||||
// When DDL finishes loading, create the editor inside the dialog.
|
||||
watch(ddlLoading, (loading) => {
|
||||
if (!loading && ddlContent.value && props.open) {
|
||||
@@ -270,7 +278,7 @@ onUnmounted(() => {
|
||||
});
|
||||
|
||||
function retry() {
|
||||
ddlContent.value = "";
|
||||
rawDdlContent.value = "";
|
||||
void loadDdl(true);
|
||||
}
|
||||
|
||||
@@ -307,6 +315,7 @@ function onClose() {
|
||||
<EditorSearchPanel v-if="ddlEditorView" ref="ddlSearchPanelRef" :view="ddlEditorView" />
|
||||
</div>
|
||||
</div>
|
||||
<DdlStorageToggle :database-type="props.databaseType" :disabled="ddlLoading || !!ddlError" />
|
||||
<DialogFooter>
|
||||
<div class="mr-auto flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Switch id="ddl-refresh-on-open" size="sm" :model-value="settingsStore.editorSettings.refreshDdlOnOpen" @update:model-value="setRefreshDdlOnOpen" />
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { applyDdlStoragePreference } from "@/lib/sql/ddlStorage";
|
||||
import DdlStorageToggle from "@/components/objects/DdlStorageToggle.vue";
|
||||
|
||||
import { computed, createApp, nextTick, onActivated, onBeforeUnmount, ref, watch, type Component } from "vue";
|
||||
import { RecycleScroller } from "vue-virtual-scroller";
|
||||
import { useSqlHighlighter } from "@/composables/useSqlHighlighter";
|
||||
@@ -246,7 +249,8 @@ const tableInfoTab = ref<TableInfoTab>("ddl");
|
||||
const tableColumns = ref<ColumnInfo[]>([]);
|
||||
const tableColumnsLoading = ref(false);
|
||||
const tableColumnsLoaded = ref(false);
|
||||
const tableDdlContent = ref("");
|
||||
const rawTableDdlContent = ref("");
|
||||
const tableDdlContent = computed(() => applyDdlStoragePreference(rawTableDdlContent.value, effectiveDatabaseType.value, settingsStore.editorSettings.excludeDdlStorage));
|
||||
const tableDdlLoading = ref(false);
|
||||
const tableDdlLoaded = ref(false);
|
||||
const tableIndexes = ref<IndexInfo[]>([]);
|
||||
@@ -1134,7 +1138,7 @@ async function openTableInfo(row: ObjectBrowserRow, initialTab?: TableInfoTab) {
|
||||
sidePanelGuard.bump();
|
||||
// Reset state
|
||||
tableColumns.value = [];
|
||||
tableDdlContent.value = "";
|
||||
rawTableDdlContent.value = "";
|
||||
tableIndexes.value = [];
|
||||
tableForeignKeys.value = [];
|
||||
tableTriggers.value = [];
|
||||
@@ -1185,11 +1189,11 @@ async function fetchTableDdl(force = settingsStore.editorSettings.refreshDdlOnOp
|
||||
const { ddl } = await loadObjectDdl(tableMetadataRequest(row), { force });
|
||||
if (sidePanelGuard.isStale(epoch)) return;
|
||||
const formatDialect = sqlFormatDialectForDbType(effectiveDatabaseType.value);
|
||||
tableDdlContent.value = settingsStore.editorSettings.generateSqlQuoteIdentifiers ? ddl : omitDdlIdentifierQuotes(ddl, formatDialect);
|
||||
rawTableDdlContent.value = settingsStore.editorSettings.generateSqlQuoteIdentifiers ? ddl : omitDdlIdentifierQuotes(ddl, formatDialect);
|
||||
loadedSuccessfully = true;
|
||||
} catch (e: any) {
|
||||
if (sidePanelGuard.isStale(epoch)) return;
|
||||
tableDdlContent.value = `-- Error: ${e?.message || e}`;
|
||||
rawTableDdlContent.value = `-- Error: ${e?.message || e}`;
|
||||
} finally {
|
||||
if (sidePanelGuard.isFresh(epoch)) {
|
||||
tableDdlLoaded.value = loadedSuccessfully;
|
||||
@@ -1323,7 +1327,7 @@ async function refreshActiveTableInfo() {
|
||||
sidePanelGuard.bump();
|
||||
|
||||
if (tableInfoTab.value === "ddl") {
|
||||
tableDdlContent.value = "";
|
||||
rawTableDdlContent.value = "";
|
||||
tableDdlLoaded.value = false;
|
||||
await fetchTableDdl(true);
|
||||
} else if (tableInfoTab.value === "columns") {
|
||||
@@ -2090,7 +2094,7 @@ async function exportStructure(row: ObjectBrowserRow) {
|
||||
try {
|
||||
const schema = row.schema || selectedSchema.value || props.database;
|
||||
const ddl = await api.getTableDdl(props.connection.id, props.database, schema, row.name, tableDdlObjectType(row.type), props.catalog, true);
|
||||
await saveFileContent(buildSingleDdlExportFileContent(ddl), `${row.name}.sql`, "SQL", "sql");
|
||||
await saveFileContent(buildSingleDdlExportFileContent(applyDdlStoragePreference(ddl, effectiveDatabaseType.value, settingsStore.editorSettings.excludeDdlStorage)), `${row.name}.sql`, "SQL", "sql");
|
||||
} catch (e: any) {
|
||||
console.error("Export structure failed:", e);
|
||||
}
|
||||
@@ -3790,6 +3794,9 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
||||
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': activeTableInfoLoading }" />
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="tableInfoTab === 'ddl' && effectiveDatabaseType === 'oceanbase-oracle'" class="border-b px-3 py-2">
|
||||
<DdlStorageToggle :database-type="effectiveDatabaseType" :disabled="tableDdlLoading" />
|
||||
</div>
|
||||
<div v-if="tableInfoTab === 'columns'" class="flex-1 min-h-0 overflow-auto">
|
||||
<div v-if="tableColumnsLoading" class="h-full flex items-center justify-center">
|
||||
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ describe("ObjectBrowser table metadata refresh", () => {
|
||||
|
||||
expect(refreshTableInfo).toContain('sidePanelMode.value !== "table-info" || !sidePanelRow.value');
|
||||
expect(refreshTableInfo).toContain("sidePanelGuard.bump();");
|
||||
expect(refreshTableInfo).toMatch(/tableInfoTab\.value === "ddl"[\s\S]*?tableDdlContent\.value = "";[\s\S]*?await fetchTableDdl\(true\);/);
|
||||
expect(refreshTableInfo).toMatch(/tableInfoTab\.value === "ddl"[\s\S]*?rawTableDdlContent\.value = "";[\s\S]*?await fetchTableDdl\(true\);/);
|
||||
expect(refreshTableInfo).toMatch(/tableInfoTab\.value === "columns"[\s\S]*?tableColumns\.value = \[\];[\s\S]*?await fetchTableColumns\(true\);/);
|
||||
expect(refreshTableInfo).toMatch(/tableInfoTab\.value === "indexes"[\s\S]*?tableIndexes\.value = \[\];[\s\S]*?await fetchTableIndexes\(true\);/);
|
||||
expect(refreshTableInfo).toMatch(/tableInfoTab\.value === "foreignKeys"[\s\S]*?tableForeignKeys\.value = \[\];[\s\S]*?await fetchTableForeignKeys\(true\);/);
|
||||
|
||||
Reference in new issue
Block a user