Fix:支持 SQL Server 新增字段设置自增 (#2254)
Co-authored-by: staff <staff@qimaos-MacBook-Pro.local>
This commit is contained in:
parent
16a1d4b1dd
commit
ea2b5d1bfd
|
|
@ -224,6 +224,7 @@ const structureDensityMetrics: Record<
|
|||
indexes: number[];
|
||||
minColumnWidth: number;
|
||||
minIndexColumnWidth: number;
|
||||
actionButtonWidth: number;
|
||||
fontSize: number;
|
||||
shellPadding: number;
|
||||
cellPaddingX: number;
|
||||
|
|
@ -241,6 +242,7 @@ const structureDensityMetrics: Record<
|
|||
indexes: [120, 180, 60, 88, 124, 144, 120, 70],
|
||||
minColumnWidth: 24,
|
||||
minIndexColumnWidth: 48,
|
||||
actionButtonWidth: 24,
|
||||
fontSize: 11,
|
||||
shellPadding: 10,
|
||||
cellPaddingX: 6,
|
||||
|
|
@ -257,6 +259,7 @@ const structureDensityMetrics: Record<
|
|||
indexes: [148, 224, 72, 108, 148, 180, 148, 84],
|
||||
minColumnWidth: 28,
|
||||
minIndexColumnWidth: 60,
|
||||
actionButtonWidth: 28,
|
||||
fontSize: 12,
|
||||
shellPadding: 12,
|
||||
cellPaddingX: 8,
|
||||
|
|
@ -273,6 +276,7 @@ const structureDensityMetrics: Record<
|
|||
indexes: [176, 260, 84, 124, 176, 216, 176, 104],
|
||||
minColumnWidth: 32,
|
||||
minIndexColumnWidth: 64,
|
||||
actionButtonWidth: 32,
|
||||
fontSize: 13,
|
||||
shellPadding: 16,
|
||||
cellPaddingX: 10,
|
||||
|
|
@ -546,8 +550,15 @@ const showExtendedProperties = computed(() => {
|
|||
return dt === "mysql" || dt === "manticoresearch" || isPostgresIdentityType(dt) || dt === "sqlserver";
|
||||
});
|
||||
const extendedPropertiesColumnIndex = 8;
|
||||
const actionButtonGap = 2;
|
||||
const columnActionButtonCount = computed(() => (canShowColumnDragControls.value ? 2 : 1));
|
||||
const columnActionsWidth = computed(() => {
|
||||
const metric = structureDensityMetric.value;
|
||||
const count = columnActionButtonCount.value;
|
||||
return metric.actionButtonWidth * count + actionButtonGap * Math.max(0, count - 1) + metric.cellPaddingX * 2;
|
||||
});
|
||||
const visibleColumnIndexes = computed(() => colLabels.value.map((column) => column.widthIndex));
|
||||
const visibleColWidths = computed(() => visibleColumnIndexes.value.map((index) => colWidths.value[index] ?? structureDensityMetric.value.minColumnWidth));
|
||||
const visibleColWidths = computed(() => colLabels.value.map((column) => (column.key === "actions" ? columnActionsWidth.value : (colWidths.value[column.widthIndex] ?? structureDensityMetric.value.minColumnWidth))));
|
||||
|
||||
function columnWidthIndex(visibleIndex: number) {
|
||||
return visibleColumnIndexes.value[visibleIndex] ?? visibleIndex;
|
||||
|
|
@ -983,7 +994,6 @@ function canDragColumn(index: number): boolean {
|
|||
if (!Number.isInteger(index) || index < 0 || index >= columns.value.length) return false;
|
||||
const column = columns.value[index];
|
||||
if (!column || column.markedForDrop) return false;
|
||||
if (!column.original) return true;
|
||||
return canShowColumnDragControls.value;
|
||||
}
|
||||
|
||||
|
|
@ -1002,6 +1012,53 @@ function canDropColumnAt(sourceIndex: number, insertionIndex: number): boolean {
|
|||
|
||||
const canShowColumnDragControls = computed(() => isCreateMode.value || structureCapabilities.value.reorderColumn);
|
||||
|
||||
function isSqlServerIdentityChecked(column: EditableStructureColumn): boolean {
|
||||
return !!column.extra.autoIncrement || !!column.extra.identity;
|
||||
}
|
||||
|
||||
function canEditSqlServerIdentity(column: EditableStructureColumn): boolean {
|
||||
return !column.original && !column.markedForDrop;
|
||||
}
|
||||
|
||||
function ensureSqlServerIdentity(column: EditableStructureColumn) {
|
||||
column.extra.autoIncrement = true;
|
||||
column.extra.identity = {
|
||||
seed: column.extra.identity?.seed ?? 1,
|
||||
increment: column.extra.identity?.increment ?? 1,
|
||||
};
|
||||
}
|
||||
|
||||
function setSqlServerIdentity(column: EditableStructureColumn, checked: boolean) {
|
||||
if (!canEditSqlServerIdentity(column)) return;
|
||||
if (checked) {
|
||||
ensureSqlServerIdentity(column);
|
||||
column.isNullable = false;
|
||||
} else {
|
||||
column.extra.autoIncrement = false;
|
||||
column.extra.identity = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function parseOptionalNumberInput(value: string | number): number | undefined {
|
||||
if (typeof value === "number") return Number.isFinite(value) ? value : undefined;
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return undefined;
|
||||
const numeric = Number(trimmed);
|
||||
return Number.isFinite(numeric) ? numeric : undefined;
|
||||
}
|
||||
|
||||
function updateSqlServerIdentitySeed(column: EditableStructureColumn, value: string | number) {
|
||||
if (!canEditSqlServerIdentity(column)) return;
|
||||
ensureSqlServerIdentity(column);
|
||||
column.extra.identity!.seed = parseOptionalNumberInput(value);
|
||||
}
|
||||
|
||||
function updateSqlServerIdentityIncrement(column: EditableStructureColumn, value: string | number) {
|
||||
if (!canEditSqlServerIdentity(column)) return;
|
||||
ensureSqlServerIdentity(column);
|
||||
column.extra.identity!.increment = parseOptionalNumberInput(value);
|
||||
}
|
||||
|
||||
function moveColumnTo(index: number, insertionIndex: number) {
|
||||
if (!canDropColumnAt(index, insertionIndex)) return;
|
||||
const nextColumns = [...columns.value];
|
||||
|
|
@ -1911,42 +1968,34 @@ watch(activeTab, (tab) => {
|
|||
<!-- SQL Server: IDENTITY -->
|
||||
<template v-else-if="structureDialect === 'sqlserver'">
|
||||
<label :class="structurePropertyLabelClass" :title="t('structureEditor.identity')">
|
||||
<input v-model="column.extra.autoIncrement" type="checkbox" :class="[structureCheckboxClass, 'shrink-0']" />
|
||||
<span class="min-w-0 truncate">{{ t("structureEditor.identity") }}</span>
|
||||
<input :checked="isSqlServerIdentityChecked(column)" type="checkbox" :class="[structureCheckboxClass, 'shrink-0']" :disabled="!canEditSqlServerIdentity(column)" @change="setSqlServerIdentity(column, ($event.target as HTMLInputElement).checked)" />
|
||||
<span class="min-w-0 truncate">{{ t("structureEditor.autoIncrement") }}</span>
|
||||
</label>
|
||||
<template v-if="column.extra.autoIncrement">
|
||||
<template v-if="isSqlServerIdentityChecked(column)">
|
||||
<Input
|
||||
:model-value="column.extra.identity?.seed?.toString() ?? '1'"
|
||||
type="number"
|
||||
:class="[structureControlClass, 'w-14']"
|
||||
:placeholder="t('structureEditor.identitySeed')"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
if (!column.extra.identity) column.extra.identity = {};
|
||||
column.extra.identity.seed = v ? Number(v) : undefined;
|
||||
}
|
||||
"
|
||||
:disabled="!canEditSqlServerIdentity(column)"
|
||||
@update:model-value="(v) => updateSqlServerIdentitySeed(column, v)"
|
||||
/>
|
||||
<Input
|
||||
:model-value="column.extra.identity?.increment?.toString() ?? '1'"
|
||||
type="number"
|
||||
:class="[structureControlClass, 'w-14']"
|
||||
:placeholder="t('structureEditor.identityIncrement')"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
if (!column.extra.identity) column.extra.identity = {};
|
||||
column.extra.identity.increment = v ? Number(v) : undefined;
|
||||
}
|
||||
"
|
||||
:disabled="!canEditSqlServerIdentity(column)"
|
||||
@update:model-value="(v) => updateSqlServerIdentityIncrement(column, v)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
<td :class="structureLastCellClass">
|
||||
<div class="flex min-w-0 items-center justify-start gap-0.5 overflow-hidden">
|
||||
<div class="flex min-w-0 items-center justify-start gap-0.5">
|
||||
<Button
|
||||
v-if="canShowColumnDragControls || !column.original"
|
||||
v-if="canShowColumnDragControls"
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
|
|
|
|||
|
|
@ -37,4 +37,13 @@ describe("jdbc dialect inference", () => {
|
|||
}),
|
||||
).toBe("iris");
|
||||
});
|
||||
|
||||
it("uses JDBC driver profiles when inferring dialect", () => {
|
||||
expect(
|
||||
inferJdbcDialect({
|
||||
db_type: "jdbc",
|
||||
driver_profile: "sqlserver",
|
||||
}),
|
||||
).toBe("sqlserver");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const JDBC_DIALECT_MATCHERS: Array<{ type: DatabaseType; patterns: RegExp[] }> =
|
|||
|
||||
export function inferJdbcDialect(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
||||
if (!connection || connection.db_type !== "jdbc") return undefined;
|
||||
const haystack = [connection.connection_string, connection.jdbc_driver_class, ...(connection.jdbc_driver_paths ?? [])].filter(Boolean).join("\n");
|
||||
const haystack = [connection.driver_profile, connection.connection_string, connection.jdbc_driver_class, ...(connection.jdbc_driver_paths ?? [])].filter(Boolean).join("\n");
|
||||
if (!haystack) return undefined;
|
||||
return JDBC_DIALECT_MATCHERS.find((matcher) => matcher.patterns.some((pattern) => pattern.test(haystack)))?.type;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ pub(super) fn capabilities_for(database_type: Option<DatabaseType>) -> TableStru
|
|||
add_column: true,
|
||||
drop_column: true,
|
||||
rename_column: true,
|
||||
reorder_column: true,
|
||||
..base
|
||||
},
|
||||
Some(
|
||||
|
|
|
|||
|
|
@ -439,6 +439,60 @@ fn gbase8a_uses_limited_mysql_ddl() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gbase8a_allows_mysql_style_column_reorder() {
|
||||
let mut id = column("id");
|
||||
id.original_position = Some(0);
|
||||
id.original = Some(ColumnInfo {
|
||||
name: "id".to_string(),
|
||||
data_type: "varchar(255)".to_string(),
|
||||
is_nullable: true,
|
||||
column_default: None,
|
||||
is_primary_key: false,
|
||||
extra: None,
|
||||
comment: None,
|
||||
});
|
||||
|
||||
let mut name = column("name");
|
||||
name.original_position = Some(1);
|
||||
name.original = Some(ColumnInfo {
|
||||
name: "name".to_string(),
|
||||
data_type: "varchar(255)".to_string(),
|
||||
is_nullable: true,
|
||||
column_default: None,
|
||||
is_primary_key: false,
|
||||
extra: None,
|
||||
comment: None,
|
||||
});
|
||||
|
||||
let mut email = column("email");
|
||||
email.original_position = Some(2);
|
||||
email.original = Some(ColumnInfo {
|
||||
name: "email".to_string(),
|
||||
data_type: "varchar(255)".to_string(),
|
||||
is_nullable: true,
|
||||
column_default: None,
|
||||
is_primary_key: false,
|
||||
extra: None,
|
||||
comment: None,
|
||||
});
|
||||
|
||||
let result = build_table_structure_change_sql(TableStructureSqlOptions {
|
||||
database_type: Some(DatabaseType::Gbase),
|
||||
schema: None,
|
||||
table_name: "users".to_string(),
|
||||
columns: vec![id, email, name],
|
||||
indexes: Vec::new(),
|
||||
foreign_keys: Vec::new(),
|
||||
triggers: Vec::new(),
|
||||
table_comment: None,
|
||||
original_table_comment: None,
|
||||
});
|
||||
|
||||
assert_eq!(result.warnings, Vec::<String>::new());
|
||||
assert_eq!(result.statements, vec!["ALTER TABLE `users` MODIFY COLUMN `email` varchar(255) AFTER `id`;"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manticoresearch_does_not_drop_id_column() {
|
||||
let mut id = column("id");
|
||||
|
|
@ -1127,6 +1181,33 @@ fn sqlserver_unchanged_foreign_key_does_not_warn_when_saving_other_changes() {
|
|||
assert_eq!(result.statements, vec!["ALTER TABLE [dbo].[orders] ADD [email] nvarchar(255) NOT NULL;"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_add_column_with_identity() {
|
||||
let mut id = column("id");
|
||||
id.data_type = "int".to_string();
|
||||
id.is_nullable = false;
|
||||
id.extra = Some(ColumnExtra {
|
||||
auto_increment: Some(true),
|
||||
identity: Some(ColumnIdentity { generation: None, seed: Some(10), increment: Some(2) }),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let result = build_table_structure_change_sql(TableStructureSqlOptions {
|
||||
database_type: Some(DatabaseType::SqlServer),
|
||||
schema: Some("dbo".to_string()),
|
||||
table_name: "orders".to_string(),
|
||||
columns: vec![id],
|
||||
indexes: Vec::new(),
|
||||
foreign_keys: Vec::new(),
|
||||
triggers: Vec::new(),
|
||||
table_comment: None,
|
||||
original_table_comment: None,
|
||||
});
|
||||
|
||||
assert_eq!(result.warnings, Vec::<String>::new());
|
||||
assert_eq!(result.statements, vec!["ALTER TABLE [dbo].[orders] ADD [id] int NOT NULL IDENTITY(10, 2);"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_changed_foreign_key_still_warns_as_unsupported() {
|
||||
let mut user_fk = foreign_key("fk_orders_user_id", "user_id", "accounts", "id");
|
||||
|
|
|
|||
|
|
@ -18,3 +18,13 @@ test("infers GoldenDB for generic JDBC connections", () => {
|
|||
"goldendb",
|
||||
);
|
||||
});
|
||||
|
||||
test("infers JDBC dialect from driver profile", () => {
|
||||
assert.equal(
|
||||
inferJdbcDialect({
|
||||
db_type: "jdbc",
|
||||
driver_profile: "sqlserver",
|
||||
}),
|
||||
"sqlserver",
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue