feat(dameng): support toggling identity on existing columns
Closes #6906
This commit is contained in:
1 parent
bf029cd342
commit
946677a579
5 files changed
+436
-33
No files matched your search
+65
-7
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user