feat(dameng): support toggling identity on existing columns

Closes #6906
This commit is contained in:
t8y2 authored and Tara committed 2026-08-22 11:40:51 +00:00
1 parent bf029cd342
commit 946677a579
5 files changed
+436 -33

No files matched your search

@@ -226,7 +226,8 @@ import TableStructureEditor from "@/components/structure/TableStructureEditor.vu
const mountedApps: App[] = [];
function draft(isPrimaryKey = false) {
function draft(isPrimaryKey = false, identity?: { seed: number; increment: number }) {
const isNullable = identity ? false : !isPrimaryKey;
return {
initialized: true,
activeTab: "columns" as const,
@@ -238,18 +239,18 @@ function draft(isPrimaryKey = false) {
id: "existing:id",
name: "id",
dataType: "INT",
isNullable: !isPrimaryKey,
isNullable,
defaultValue: "",
comment: "",
isPrimaryKey,
extra: {},
extra: identity ? { autoIncrement: true, identity: { ...identity } } : {},
original: {
name: "id",
data_type: "INT",
is_nullable: !isPrimaryKey,
is_nullable: isNullable,
column_default: null,
is_primary_key: isPrimaryKey,
extra: null,
extra: identity ? `IDENTITY(${identity.seed}, ${identity.increment})` : null,
comment: null,
},
originalPosition: 0,
@@ -262,7 +263,7 @@ function draft(isPrimaryKey = false) {
};
}
async function mountEditor(databaseType: "sqlserver" | "postgres" | "sqlite" | "oracle" | "dameng" | "duckdb" | "informix", isPrimaryKey = false, options: { database?: string; dynamicTypes?: string[] } = {}) {
async function mountEditor(databaseType: "sqlserver" | "postgres" | "sqlite" | "oracle" | "dameng" | "duckdb" | "informix", isPrimaryKey = false, options: { database?: string; dynamicTypes?: string[]; identity?: { seed: number; increment: number } } = {}) {
mocks.connection.db_type = databaseType;
mocks.connection.name = databaseType;
mocks.connection.driver_label = databaseType;
@@ -277,7 +278,7 @@ async function mountEditor(databaseType: "sqlserver" | "postgres" | "sqlite" | "
database: options.database ?? "test",
schema: "SYSDBA",
tableName: "users",
draft: draft(isPrimaryKey),
draft: draft(isPrimaryKey, options.identity),
});
mountedApps.push(app);
app.mount(root);
@@ -324,6 +325,12 @@ function columnCheckbox(root: HTMLElement, header: string): HTMLInputElement {
return checkbox;
}
function columnPropertyCheckbox(root: HTMLElement, title: string): HTMLInputElement {
const checkbox = root.querySelector<HTMLInputElement>(`[data-column-row-index="0"] label[title="${title}"] input[type="checkbox"]`);
if (!checkbox) throw new Error(`Missing ${title} checkbox`);
return checkbox;
}
function buttonWithText(root: HTMLElement, text: string): HTMLButtonElement {
const button = Array.from(root.querySelectorAll("button")).find((item) => item.textContent?.includes(text));
if (!button) throw new Error(`Missing ${text} button`);
@@ -348,6 +355,57 @@ afterEach(() => {
});
describe("TableStructureEditor primary key editing", () => {
it("allows enabling identity on an existing Dameng integer column", async () => {
const root = await mountEditor("dameng");
const identity = columnPropertyCheckbox(root, "structureEditor.identity");
const nullable = columnCheckbox(root, "structureEditor.nullable");
expect(identity.disabled).toBe(false);
expect(nullable.checked).toBe(true);
identity.checked = true;
identity.dispatchEvent(new Event("change", { bubbles: true }));
await nextTick();
expect(identity.checked).toBe(true);
expect(nullable.checked).toBe(false);
expect(Array.from(root.querySelectorAll<HTMLInputElement>('[data-column-row-index="0"] input[type="number"]')).every((input) => !input.disabled)).toBe(true);
await vi.waitFor(() => expect(mocks.buildTableStructureChangeSql).toHaveBeenCalled());
expect(mocks.buildTableStructureChangeSql).toHaveBeenLastCalledWith(
expect.objectContaining({
columns: [
expect.objectContaining({
isNullable: false,
extra: expect.objectContaining({ autoIncrement: true, identity: { seed: 1, increment: 1 } }),
}),
],
}),
);
});
it("allows disabling existing Dameng identity while keeping its parameters read-only", async () => {
const root = await mountEditor("dameng", false, { identity: { seed: 10, increment: 2 } });
const identity = columnPropertyCheckbox(root, "structureEditor.identity");
const identityInputs = Array.from(root.querySelectorAll<HTMLInputElement>('[data-column-row-index="0"] input[type="number"]'));
expect(identity.disabled).toBe(false);
expect(identity.checked).toBe(true);
expect(identityInputs).toHaveLength(2);
expect(identityInputs.every((input) => input.disabled)).toBe(true);
identity.checked = false;
identity.dispatchEvent(new Event("change", { bubbles: true }));
await nextTick();
expect(identity.checked).toBe(false);
await vi.waitFor(() => expect(mocks.buildTableStructureChangeSql).toHaveBeenCalled());
expect(mocks.buildTableStructureChangeSql).toHaveBeenLastCalledWith(
expect.objectContaining({
columns: [expect.objectContaining({ extra: expect.objectContaining({ autoIncrement: false, identity: undefined }) })],
}),
);
});
it("enables the primary-key checkbox for an existing Dameng column and makes it not null", async () => {
const root = await mountEditor("dameng");
const primaryKey = columnCheckbox(root, "structureEditor.primaryKey");
@@ -1993,12 +1993,20 @@ function isDamengIdentityChecked(column: EditableStructureColumn): boolean {
return !!column.extra.autoIncrement || !!column.extra.identity;
}
function originalHasDamengIdentity(column: EditableStructureColumn): boolean {
return column.original?.extra?.toLowerCase().includes("identity") ?? false;
}
function canEditDamengIdentity(column: EditableStructureColumn): boolean {
if (column.original || column.markedForDrop || !isDamengIdentityCompatibleDataType(column.dataType)) return false;
if (column.markedForDrop || !isDamengIdentityCompatibleDataType(column.dataType)) return false;
// DM8 permits only one identity column per table, so prevent creating an invalid draft in the editor.
return isDamengIdentityChecked(column) || !columns.value.some((candidate) => candidate !== column && !candidate.markedForDrop && isDamengIdentityChecked(candidate));
}
function canEditDamengIdentityParameters(column: EditableStructureColumn): boolean {
return canEditDamengIdentity(column) && !originalHasDamengIdentity(column);
}
function clearDamengIdentity(column: EditableStructureColumn) {
column.extra.autoIncrement = false;
column.extra.identity = undefined;
@@ -2012,10 +2020,11 @@ function syncDamengIdentityForDataType(column: EditableStructureColumn) {
}
function ensureDamengIdentity(column: EditableStructureColumn) {
const originalIdentity = parseExtraToColumnExtra(column.original?.extra, "dameng").identity;
column.extra.autoIncrement = true;
column.extra.identity = {
seed: column.extra.identity?.seed ?? 1,
increment: column.extra.identity?.increment ?? 1,
seed: column.extra.identity?.seed ?? originalIdentity?.seed ?? 1,
increment: column.extra.identity?.increment ?? originalIdentity?.increment ?? 1,
};
}
@@ -2030,13 +2039,13 @@ function setDamengIdentity(column: EditableStructureColumn, checked: boolean) {
}
function updateDamengIdentitySeed(column: EditableStructureColumn, value: string | number) {
if (!canEditDamengIdentity(column)) return;
if (!canEditDamengIdentityParameters(column)) return;
ensureDamengIdentity(column);
column.extra.identity!.seed = parseOptionalNumberInput(value);
}
function updateDamengIdentityIncrement(column: EditableStructureColumn, value: string | number) {
if (!canEditDamengIdentity(column)) return;
if (!canEditDamengIdentityParameters(column)) return;
ensureDamengIdentity(column);
column.extra.identity!.increment = parseOptionalNumberInput(value);
}
@@ -3539,7 +3548,7 @@ watch([activeTab, ddlLoading], ([tab, loading]) => {
type="number"
:class="[structureControlClass, 'w-14']"
:placeholder="t('structureEditor.identitySeed')"
:disabled="!canEditDamengIdentity(column)"
:disabled="!canEditDamengIdentityParameters(column)"
@update:model-value="(v) => updateDamengIdentitySeed(column, v)"
/>
<Input
@@ -3547,7 +3556,7 @@ watch([activeTab, ddlLoading], ([tab, loading]) => {
type="number"
:class="[structureControlClass, 'w-14']"
:placeholder="t('structureEditor.identityIncrement')"
:disabled="!canEditDamengIdentity(column)"
:disabled="!canEditDamengIdentityParameters(column)"
@update:model-value="(v) => updateDamengIdentityIncrement(column, v)"
/>
</template>