fix(structure): initialize dialogs and edit clickhouse columns
This commit is contained in:
1 parent
5b3b69f7fb
commit
69ee7f2d8b
18 files changed
+325
-120
No files matched your search
@@ -28,13 +28,17 @@ const passphrase = ref("");
|
||||
const passphraseConfirm = ref("");
|
||||
const error = ref("");
|
||||
|
||||
watch(dialogOpen, (open) => {
|
||||
if (open) {
|
||||
passphrase.value = "";
|
||||
passphraseConfirm.value = "";
|
||||
error.value = "";
|
||||
}
|
||||
});
|
||||
watch(
|
||||
dialogOpen,
|
||||
(open) => {
|
||||
if (open) {
|
||||
passphrase.value = "";
|
||||
passphraseConfirm.value = "";
|
||||
error.value = "";
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function confirm() {
|
||||
if (!passphrase.value) {
|
||||
|
||||
@@ -667,17 +667,21 @@ function resetForm() {
|
||||
resetTestState();
|
||||
}
|
||||
|
||||
watch(open, (value) => {
|
||||
if (!value) {
|
||||
resetForm();
|
||||
return;
|
||||
}
|
||||
if (!props.editConfig) {
|
||||
resetForm();
|
||||
}
|
||||
void loadJdbcDrivers();
|
||||
void loadAgentDrivers();
|
||||
});
|
||||
watch(
|
||||
open,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
resetForm();
|
||||
return;
|
||||
}
|
||||
if (!props.editConfig) {
|
||||
resetForm();
|
||||
}
|
||||
void loadJdbcDrivers();
|
||||
void loadAgentDrivers();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(canUseSsh, (value) => {
|
||||
if (!value && configTab.value === "ssh") {
|
||||
|
||||
@@ -661,9 +661,13 @@ function stopDrag() {
|
||||
window.removeEventListener("mouseup", stopDrag);
|
||||
}
|
||||
|
||||
watch(open, (value) => {
|
||||
if (value) void initialize();
|
||||
});
|
||||
watch(
|
||||
open,
|
||||
(value) => {
|
||||
if (value) void initialize();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => visibleTables.value.map((table) => table.name).join("\n"),
|
||||
|
||||
@@ -412,21 +412,25 @@ watch(sourceTable, (table, previous) => {
|
||||
}
|
||||
});
|
||||
watch(targetTable, () => clearResult());
|
||||
watch(open, async (value) => {
|
||||
if (!value) return;
|
||||
result.value = null;
|
||||
syncSql.value = "";
|
||||
if (props.prefillConnectionId) {
|
||||
sourceConnectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId, "source");
|
||||
if (props.prefillDatabase) sourceDatabase.value = props.prefillDatabase;
|
||||
if (props.prefillDatabase) await loadSchemas("source", props.prefillSchema);
|
||||
if (props.prefillTable) {
|
||||
await loadTables("source");
|
||||
if (sourceTables.value.includes(props.prefillTable)) sourceTable.value = props.prefillTable;
|
||||
watch(
|
||||
open,
|
||||
async (value) => {
|
||||
if (!value) return;
|
||||
result.value = null;
|
||||
syncSql.value = "";
|
||||
if (props.prefillConnectionId) {
|
||||
sourceConnectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId, "source");
|
||||
if (props.prefillDatabase) sourceDatabase.value = props.prefillDatabase;
|
||||
if (props.prefillDatabase) await loadSchemas("source", props.prefillSchema);
|
||||
if (props.prefillTable) {
|
||||
await loadTables("source");
|
||||
if (sourceTables.value.includes(props.prefillTable)) sourceTable.value = props.prefillTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -356,23 +356,27 @@ watch(targetDatabase, (database) => {
|
||||
watch(sourceSchema, () => resetResult());
|
||||
watch(targetSchema, () => resetResult());
|
||||
|
||||
watch(open, async (val) => {
|
||||
if (val) {
|
||||
step.value = "select";
|
||||
diffs.value = [];
|
||||
syncErrors.value = [];
|
||||
executedCount.value = 0;
|
||||
executeTotal.value = 0;
|
||||
if (props.prefillConnectionId) {
|
||||
sourceConnectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId, "source");
|
||||
if (props.prefillDatabase) {
|
||||
sourceDatabase.value = props.prefillDatabase;
|
||||
await loadSchemas("source", props.prefillSchema);
|
||||
watch(
|
||||
open,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
step.value = "select";
|
||||
diffs.value = [];
|
||||
syncErrors.value = [];
|
||||
executedCount.value = 0;
|
||||
executeTotal.value = 0;
|
||||
if (props.prefillConnectionId) {
|
||||
sourceConnectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId, "source");
|
||||
if (props.prefillDatabase) {
|
||||
sourceDatabase.value = props.prefillDatabase;
|
||||
await loadSchemas("source", props.prefillSchema);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -292,21 +292,25 @@ watch(schema, (value) => {
|
||||
if (value) loadTables(preferredTable).catch((e) => toast(String(e), 5000));
|
||||
});
|
||||
|
||||
watch(open, async (val) => {
|
||||
if (val) {
|
||||
resetState();
|
||||
pendingPrefillTable.value = props.prefillTable ?? "";
|
||||
if (props.prefillConnectionId) {
|
||||
skipConnectionWatch.value = true;
|
||||
connectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId);
|
||||
if (props.prefillDatabase) {
|
||||
database.value = props.prefillDatabase;
|
||||
await loadSchemas(props.prefillSchema);
|
||||
watch(
|
||||
open,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
resetState();
|
||||
pendingPrefillTable.value = props.prefillTable ?? "";
|
||||
if (props.prefillConnectionId) {
|
||||
skipConnectionWatch.value = true;
|
||||
connectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId);
|
||||
if (props.prefillDatabase) {
|
||||
database.value = props.prefillDatabase;
|
||||
await loadSchemas(props.prefillSchema);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -231,12 +231,16 @@ async function cancelImport() {
|
||||
await api.cancelTableImport(importId.value);
|
||||
}
|
||||
|
||||
watch(open, (value) => {
|
||||
if (value) {
|
||||
resetState();
|
||||
void loadTargetColumns();
|
||||
}
|
||||
});
|
||||
watch(
|
||||
open,
|
||||
(value) => {
|
||||
if (value) {
|
||||
resetState();
|
||||
void loadTargetColumns();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -119,15 +119,19 @@ const filteredItems = computed(() => {
|
||||
|
||||
const filteredCounts = computed(() => summarizeLineageCounts(filteredItems.value));
|
||||
|
||||
watch(dialogOpen, (open) => {
|
||||
if (open) {
|
||||
confidenceFilter.value = "all";
|
||||
searchText.value = "";
|
||||
void loadLineage();
|
||||
} else {
|
||||
cancelLoad();
|
||||
}
|
||||
});
|
||||
watch(
|
||||
dialogOpen,
|
||||
(open) => {
|
||||
if (open) {
|
||||
confidenceFilter.value = "all";
|
||||
searchText.value = "";
|
||||
void loadLineage();
|
||||
} else {
|
||||
cancelLoad();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function cancelLoad() {
|
||||
cancelled.value = true;
|
||||
|
||||
@@ -95,13 +95,17 @@ const progressLabel = computed(() =>
|
||||
t("databaseSearch.progress", { done: progressDone.value, total: progressTotal.value }),
|
||||
);
|
||||
|
||||
watch(dialogOpen, (open) => {
|
||||
if (open) {
|
||||
resetSearchState();
|
||||
} else {
|
||||
stopSearch();
|
||||
}
|
||||
});
|
||||
watch(
|
||||
dialogOpen,
|
||||
(open) => {
|
||||
if (open) {
|
||||
resetSearchState();
|
||||
} else {
|
||||
stopSearch();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function resetSearchState() {
|
||||
running.value = false;
|
||||
|
||||
@@ -376,13 +376,17 @@ watch(sqlConnections, () => {
|
||||
connectionId.value = resolveInitialConnectionId();
|
||||
});
|
||||
|
||||
watch(open, (value) => {
|
||||
if (!value) return;
|
||||
resetState();
|
||||
if (connectionId.value) {
|
||||
loadDatabasesForConnection(connectionId.value);
|
||||
}
|
||||
});
|
||||
watch(
|
||||
open,
|
||||
(value) => {
|
||||
if (!value) return;
|
||||
resetState();
|
||||
if (connectionId.value) {
|
||||
loadDatabasesForConnection(connectionId.value);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -159,19 +159,23 @@ watch(targetConnectionId, (id) => {
|
||||
loadDatabases(id, "target");
|
||||
});
|
||||
|
||||
watch(open, async (val) => {
|
||||
if (val) {
|
||||
resetState();
|
||||
if (props.prefillConnectionId) {
|
||||
skipSourceWatch.value = true;
|
||||
sourceConnectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId, "source");
|
||||
if (props.prefillDatabase) {
|
||||
sourceDatabase.value = props.prefillDatabase;
|
||||
watch(
|
||||
open,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
resetState();
|
||||
if (props.prefillConnectionId) {
|
||||
skipSourceWatch.value = true;
|
||||
sourceConnectionId.value = props.prefillConnectionId;
|
||||
await loadDatabases(props.prefillConnectionId, "source");
|
||||
if (props.prefillDatabase) {
|
||||
sourceDatabase.value = props.prefillDatabase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function resetState() {
|
||||
sourceConnectionId.value = "";
|
||||
|
||||
@@ -167,6 +167,12 @@ const clickhouseCapabilities = capabilities({
|
||||
createTable: true,
|
||||
addColumn: true,
|
||||
dropColumn: true,
|
||||
renameColumn: true,
|
||||
alterExistingColumn: true,
|
||||
alterType: true,
|
||||
alterNullability: true,
|
||||
alterDefault: true,
|
||||
comment: true,
|
||||
});
|
||||
|
||||
const capabilityByType: Partial<Record<DatabaseType, TableStructureCapabilities>> = {
|
||||
|
||||
@@ -85,9 +85,23 @@ function normalizeDefault(value: string | null | undefined): string {
|
||||
return trimmed.toLowerCase() === "null" ? "" : trimmed;
|
||||
}
|
||||
|
||||
function unwrapClickHouseNullableType(dataType: string): string {
|
||||
const match = dataType.trim().match(/^Nullable\s*\((.*)\)$/is);
|
||||
return match ? match[1].trim() : dataType.trim();
|
||||
}
|
||||
|
||||
function clickHouseColumnType(column: EditableStructureColumn): string {
|
||||
const dataType = column.dataType.trim();
|
||||
if (column.isNullable) {
|
||||
return /^Nullable\s*\(/i.test(dataType) ? dataType : `Nullable(${dataType})`;
|
||||
}
|
||||
return unwrapClickHouseNullableType(dataType);
|
||||
}
|
||||
|
||||
function columnDefinition(databaseType: StructureSqlFlavor, column: EditableStructureColumn): string {
|
||||
const parts = [quoteIdent(databaseType, column.name), column.dataType.trim()];
|
||||
if (!column.isNullable && !isOracleLike(databaseType)) parts.push("NOT NULL");
|
||||
const dataType = databaseType === "clickhouse" ? clickHouseColumnType(column) : column.dataType.trim();
|
||||
const parts = [quoteIdent(databaseType, column.name), dataType];
|
||||
if (!column.isNullable && !isOracleLike(databaseType) && databaseType !== "clickhouse") parts.push("NOT NULL");
|
||||
const defaultValue = normalizeDefault(column.defaultValue);
|
||||
if (defaultValue) parts.push(`DEFAULT ${defaultValue}`);
|
||||
if (databaseType === "mysql" && clean(column.comment)) {
|
||||
@@ -127,6 +141,11 @@ function buildAddColumnSql(databaseType: StructureSqlFlavor, table: string, colu
|
||||
`COMMENT ON COLUMN ${table}.${quoteIdent(databaseType, column.name)} IS ${quoteString(clean(column.comment))};`,
|
||||
);
|
||||
}
|
||||
if (databaseType === "clickhouse" && clean(column.comment)) {
|
||||
statements.push(
|
||||
`ALTER TABLE ${table} COMMENT COLUMN ${quoteIdent(databaseType, column.name)} ${quoteString(clean(column.comment))};`,
|
||||
);
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
@@ -240,6 +259,48 @@ function buildH2ExistingColumnSql(table: string, column: EditableStructureColumn
|
||||
return statements;
|
||||
}
|
||||
|
||||
function buildClickHouseExistingColumnSql(table: string, column: EditableStructureColumn): string[] {
|
||||
const original = column.original;
|
||||
if (!original) return [];
|
||||
|
||||
const statements: string[] = [];
|
||||
let currentName = original.name;
|
||||
if (column.name !== original.name) {
|
||||
statements.push(
|
||||
`ALTER TABLE ${table} RENAME COLUMN ${quoteIdent("clickhouse", original.name)} TO ${quoteIdent("clickhouse", column.name)};`,
|
||||
);
|
||||
currentName = column.name;
|
||||
}
|
||||
if (
|
||||
clickHouseColumnType(column) !== original.data_type.trim() ||
|
||||
normalizeDefault(column.defaultValue) !== originalDefault(column)
|
||||
) {
|
||||
const defaultValue = normalizeDefault(column.defaultValue);
|
||||
if (defaultValue) {
|
||||
statements.push(
|
||||
`ALTER TABLE ${table} MODIFY COLUMN ${quoteIdent("clickhouse", currentName)} ${clickHouseColumnType(column)} DEFAULT ${defaultValue};`,
|
||||
);
|
||||
} else if (originalDefault(column)) {
|
||||
statements.push(`ALTER TABLE ${table} MODIFY COLUMN ${quoteIdent("clickhouse", currentName)} REMOVE DEFAULT;`);
|
||||
if (clickHouseColumnType(column) !== original.data_type.trim()) {
|
||||
statements.push(
|
||||
`ALTER TABLE ${table} MODIFY COLUMN ${quoteIdent("clickhouse", currentName)} ${clickHouseColumnType(column)};`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
statements.push(
|
||||
`ALTER TABLE ${table} MODIFY COLUMN ${quoteIdent("clickhouse", currentName)} ${clickHouseColumnType(column)};`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (clean(column.comment) !== originalComment(column)) {
|
||||
statements.push(
|
||||
`ALTER TABLE ${table} COMMENT COLUMN ${quoteIdent("clickhouse", currentName)} ${quoteString(clean(column.comment))};`,
|
||||
);
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
function buildSqliteExistingColumnSql(table: string, column: EditableStructureColumn, warnings: string[]): string[] {
|
||||
const original = column.original;
|
||||
if (!original) return [];
|
||||
@@ -321,6 +382,8 @@ function buildColumnSql(options: BuildTableStructureChangeSqlOptions, warnings:
|
||||
statements.push(...buildOracleLikeExistingColumnSql(dialect, table, column));
|
||||
} else if (dialect === "h2") {
|
||||
statements.push(...buildH2ExistingColumnSql(table, column));
|
||||
} else if (dialect === "clickhouse") {
|
||||
statements.push(...buildClickHouseExistingColumnSql(table, column));
|
||||
} else if (dialect === "sqlite") {
|
||||
statements.push(...buildSqliteExistingColumnSql(table, column, warnings));
|
||||
} else {
|
||||
@@ -463,8 +526,9 @@ export function buildCreateTableSql(options: BuildTableStructureChangeSqlOptions
|
||||
|
||||
const pkColumns = activeColumns.filter((c) => c.isPrimaryKey);
|
||||
const colDefs = activeColumns.map((col) => {
|
||||
const parts = [quoteIdent(dialect, col.name), col.dataType.trim()];
|
||||
if (!col.isNullable && !col.isPrimaryKey) parts.push("NOT NULL");
|
||||
const dataType = dialect === "clickhouse" ? clickHouseColumnType(col) : col.dataType.trim();
|
||||
const parts = [quoteIdent(dialect, col.name), dataType];
|
||||
if (!col.isNullable && !col.isPrimaryKey && dialect !== "clickhouse") parts.push("NOT NULL");
|
||||
const defaultValue = normalizeDefault(col.defaultValue);
|
||||
if (defaultValue) parts.push(`DEFAULT ${defaultValue}`);
|
||||
if (dialect === "mysql" && capabilities.comment && clean(col.comment)) {
|
||||
@@ -489,6 +553,15 @@ export function buildCreateTableSql(options: BuildTableStructureChangeSqlOptions
|
||||
}
|
||||
}
|
||||
}
|
||||
if (capabilities.comment && dialect === "clickhouse") {
|
||||
for (const col of activeColumns) {
|
||||
if (clean(col.comment)) {
|
||||
statements.push(
|
||||
`ALTER TABLE ${table} COMMENT COLUMN ${quoteIdent(dialect, col.name)} ${quoteString(clean(col.comment))};`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const index of options.indexes.filter((idx) => !idx.markedForDrop && !idx.isPrimary)) {
|
||||
const name = clean(index.name);
|
||||
|
||||
Reference in new issue
Block a user