feat(schema-diff): support case-insensitive table and column matching

This commit is contained in:
jearhe authored and GitHub committed 2026-09-06 18:24:44 +08:00
1 parent 0ef8369e2b
commit 3123e962c8
23 files changed
+726 -94

No files matched your search

@@ -83,17 +83,17 @@ const restrictTables = ref(false);
const localSelectedTables = ref<string[]>([]);
const activeTableMappings = computed(() => props.options?.tableMappings ?? []);
const tableMatches = computed<SchemaDiffTableMatch[]>(() => buildSchemaDiffTableMatches(localSelectedTables.value, targetTableList.value, activeTableMappings.value));
const tableMatches = computed<SchemaDiffTableMatch[]>(() => buildSchemaDiffTableMatches(localSelectedTables.value, targetTableList.value, activeTableMappings.value, props.options.ignoreTableNameCase));
const matchedTableCount = computed(() => tableMatches.value.filter((match) => match.targetTable).length);
const missingTargetTables = computed(() => tableMatches.value.filter((match) => !match.targetTable).map((match) => match.sourceTable));
const tableMappingConflictSource = ref<string | null>(null);
function targetTableOptions(sourceTable: string): string[] {
return availableSchemaDiffTargetTables(sourceTable, targetTableList.value, activeTableMappings.value);
return availableSchemaDiffTargetTables(sourceTable, targetTableList.value, activeTableMappings.value, props.options.ignoreTableNameCase);
}
function handleTableMappingUpdate(sourceTable: string, targetTable: string) {
const update = updateSchemaDiffTableMapping(activeTableMappings.value, sourceTable, targetTable);
const update = updateSchemaDiffTableMapping(activeTableMappings.value, sourceTable, targetTable, props.options.ignoreTableNameCase);
if (update.accepted) {
tableMappingConflictSource.value = null;
emitTableMappings(update.mappings);
@@ -107,7 +107,7 @@ function emitTableMappings(nextMappings: SchemaDiffTableMapping[]) {
}
function reconcileTableMappings(selectedTables: string[], targetTables = targetTableList.value) {
const nextMappings = targetTableListLoaded.value ? reconcileSchemaDiffTableMappings(selectedTables, targetTables, activeTableMappings.value) : pruneSchemaDiffTableMappings(selectedTables, activeTableMappings.value);
const nextMappings = targetTableListLoaded.value ? reconcileSchemaDiffTableMappings(selectedTables, targetTables, activeTableMappings.value, props.options.ignoreTableNameCase) : pruneSchemaDiffTableMappings(selectedTables, activeTableMappings.value);
emitTableMappings(nextMappings);
}
@@ -591,6 +591,8 @@ async function handleCompare() {
ignoreComments: ignoreComments.value,
cascadeDelete: opts?.cascadeDelete ?? false,
compareColumnOrder: opts.compareColumnOrder,
ignoreTableNameCase: opts.ignoreTableNameCase,
ignoreColumnNameCase: opts.ignoreColumnNameCase,
detectRenames: opts?.detectRenames ?? false,
detectTableRenames: opts?.detectTableRenames ?? false,
renameThreshold: opts?.renameThreshold ?? 0.5,
@@ -112,6 +112,8 @@ describe("SchemaDiffDialog fullscreen layout", () => {
expect(configStepSource).toContain("tableMatchStatus.${match.kind}");
expect(dialogSource).toContain('@update:table-mappings="handleTableMappingsUpdate"');
expect(dialogSource).toContain("tableMappings: opts.selectedTables === undefined ? undefined : opts.tableMappings");
expect(dialogSource).toContain("ignoreTableNameCase: opts.ignoreTableNameCase");
expect(dialogSource).toContain("ignoreColumnNameCase: opts.ignoreColumnNameCase");
expect(dialogSource).toContain("const swappedMappings = swapSchemaDiffTableMappings(currentOptions.tableMappings ?? []);");
});
@@ -2,7 +2,8 @@
import { createApp, defineComponent, h, nextTick, ref, type App } from "vue";
import { afterEach, describe, expect, it, vi } from "vitest";
import { DEFAULT_POSTGRES_OPTIONS, type SchemaDiffCompareOptions, type SchemaDiffOptionItem } from "@/types/schemaDiff";
import { getSchemaDiffOptionsForDbType } from "@/lib/schema/schemaDiffOptions";
import { DEFAULT_POSTGRES_OPTIONS, normalizeSchemaDiffCompareOptions, type SchemaDiffCompareOptions, type SchemaDiffOptionItem } from "@/types/schemaDiff";
vi.mock("vue-i18n", () => ({ useI18n: () => ({ t: (key: string) => key }) }));
@@ -49,6 +50,14 @@ afterEach(() => {
});
describe("SchemaDiffOptionsPanel", () => {
it("exposes independent case-insensitive name options with false defaults", () => {
const ids = getSchemaDiffOptionsForDbType("postgres").map((item) => item.id);
expect(ids).toEqual(expect.arrayContaining(["ignoreTableNameCase", "ignoreColumnNameCase"]));
const normalized = normalizeSchemaDiffCompareOptions({});
expect(normalized.ignoreTableNameCase).toBe(false);
expect(normalized.ignoreColumnNameCase).toBe(false);
});
it("keeps unsaved checkbox edits when the parent refreshes equivalent options", async () => {
const options = ref<SchemaDiffCompareOptions>({ ...DEFAULT_POSTGRES_OPTIONS });
const optionTree: SchemaDiffOptionItem[] = [{ id: "views", labelKey: "views", defaultChecked: true }];