fix(structure): initialize dialogs and edit clickhouse columns
This commit is contained in:
parent
5b3b69f7fb
commit
69ee7f2d8b
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ pub async fn list_tables(client: &ChClient, database: &str) -> Result<Vec<TableI
|
|||
|
||||
pub async fn get_columns(client: &ChClient, database: &str, table: &str) -> Result<Vec<ColumnInfo>, String> {
|
||||
let sql = format!(
|
||||
"SELECT name, type, default_kind, default_expression, is_in_primary_key \
|
||||
"SELECT name, type, default_kind, default_expression, is_in_primary_key, comment \
|
||||
FROM system.columns WHERE database = '{}' AND table = '{}' ORDER BY position",
|
||||
database.replace('\'', "\\'"),
|
||||
table.replace('\'', "\\'")
|
||||
|
|
@ -183,7 +183,7 @@ pub async fn get_columns(client: &ChClient, database: &str, table: &str) -> Resu
|
|||
column_default,
|
||||
is_primary_key: is_pk,
|
||||
extra: None,
|
||||
comment: None,
|
||||
comment: row.get(5).and_then(|v| v.as_str()).filter(|value| !value.is_empty()).map(str::to_string),
|
||||
numeric_precision: None,
|
||||
numeric_scale: None,
|
||||
character_maximum_length: None,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
const mountedOpenDialogs = [
|
||||
"apps/desktop/src/components/connection/ConnectionDialog.vue",
|
||||
"apps/desktop/src/components/transfer/DataTransferDialog.vue",
|
||||
"apps/desktop/src/components/diff/SchemaDiffDialog.vue",
|
||||
"apps/desktop/src/components/diff/DataCompareDialog.vue",
|
||||
"apps/desktop/src/components/sql-file/SqlFileExecutionDialog.vue",
|
||||
"apps/desktop/src/components/diagram/SchemaDiagramDialog.vue",
|
||||
"apps/desktop/src/components/import/TableImportDialog.vue",
|
||||
"apps/desktop/src/components/structure/TableStructureEditorDialog.vue",
|
||||
"apps/desktop/src/components/lineage/FieldLineageDialog.vue",
|
||||
"apps/desktop/src/components/search/DatabaseSearchDialog.vue",
|
||||
"apps/desktop/src/components/export/DatabaseExportDialog.vue",
|
||||
"apps/desktop/src/components/config/ConfigPassphraseDialog.vue",
|
||||
] as const;
|
||||
|
||||
test("dialogs initialized through v-if run their open watcher on mount", () => {
|
||||
for (const filePath of mountedOpenDialogs) {
|
||||
const source = readFileSync(filePath, "utf8");
|
||||
assert.match(
|
||||
source,
|
||||
/watch\(\s*(open|dialogOpen),[\s\S]*?\{\s*immediate:\s*true\s*\},?\s*\)/,
|
||||
`${filePath} should use an immediate open watcher`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -59,9 +59,12 @@ test("limited analytic engines can open the editor for supported operations only
|
|||
assert.equal(clickhouse.createTable, true);
|
||||
assert.equal(clickhouse.addColumn, true);
|
||||
assert.equal(clickhouse.dropColumn, true);
|
||||
assert.equal(clickhouse.renameColumn, false);
|
||||
assert.equal(clickhouse.alterExistingColumn, false);
|
||||
assert.equal(clickhouse.comment, false);
|
||||
assert.equal(clickhouse.renameColumn, true);
|
||||
assert.equal(clickhouse.alterExistingColumn, true);
|
||||
assert.equal(clickhouse.alterType, true);
|
||||
assert.equal(clickhouse.alterNullability, true);
|
||||
assert.equal(clickhouse.alterDefault, true);
|
||||
assert.equal(clickhouse.comment, true);
|
||||
assert.equal(clickhouse.createIndex, false);
|
||||
assert.equal(canEditTableStructure("clickhouse"), true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { strict as assert } from "node:assert";
|
|||
import test from "node:test";
|
||||
|
||||
const source = readFileSync("apps/desktop/src/components/structure/TableStructureEditorDialog.vue", "utf8");
|
||||
const clickhouseSource = readFileSync("crates/dbx-core/src/db/clickhouse_driver.rs", "utf8");
|
||||
|
||||
test("column comments can be expanded into a multiline editor", () => {
|
||||
assert.match(source, /PopoverContent/);
|
||||
|
|
@ -18,6 +19,11 @@ test("structure editor keeps columns when optional metadata fails", () => {
|
|||
assert.match(source, /api\s*\n\s*\.listTriggers[\s\S]*\.catch\(\(\) => \[\]\)/);
|
||||
});
|
||||
|
||||
test("ClickHouse column metadata preserves comments for structure editing", () => {
|
||||
assert.match(clickhouseSource, /SELECT name, type, default_kind, default_expression, is_in_primary_key, comment/);
|
||||
assert.match(clickhouseSource, /comment:\s*row\.get\(5\)/);
|
||||
});
|
||||
|
||||
test("structure editor loads immediately when mounted open", () => {
|
||||
assert.match(source, /watch\(\s*open,[\s\S]*\{\s*immediate:\s*true\s*\},?\s*\)/);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -657,12 +657,12 @@ test("Redshift skips unsupported index operations while keeping column DDL", ()
|
|||
assert.deepEqual(result.warnings, ['Creating indexes is not supported for redshift from this editor.']);
|
||||
});
|
||||
|
||||
test("builds ClickHouse limited column DDL and skips indexes", () => {
|
||||
test("builds ClickHouse column DDL and skips indexes", () => {
|
||||
const result = buildTableStructureChangeSql({
|
||||
databaseType: "clickhouse",
|
||||
tableName: "events",
|
||||
columns: [
|
||||
column({ id: "new", name: "name", dataType: "String", isNullable: true }),
|
||||
column({ id: "new", name: "name", dataType: "String", isNullable: false }),
|
||||
column({
|
||||
id: "legacy",
|
||||
name: "legacy",
|
||||
|
|
@ -679,12 +679,14 @@ test("builds ClickHouse limited column DDL and skips indexes", () => {
|
|||
column({
|
||||
id: "kind",
|
||||
name: "event_kind",
|
||||
dataType: "String",
|
||||
dataType: "LowCardinality(String)",
|
||||
isNullable: false,
|
||||
defaultValue: "'view'",
|
||||
original: {
|
||||
name: "kind",
|
||||
data_type: "String",
|
||||
is_nullable: true,
|
||||
column_default: null,
|
||||
is_nullable: false,
|
||||
column_default: "'click'",
|
||||
is_primary_key: false,
|
||||
extra: null,
|
||||
},
|
||||
|
|
@ -696,10 +698,52 @@ test("builds ClickHouse limited column DDL and skips indexes", () => {
|
|||
assert.deepEqual(result.statements, [
|
||||
'ALTER TABLE "events" ADD COLUMN "name" String;',
|
||||
'ALTER TABLE "events" DROP COLUMN "legacy";',
|
||||
'ALTER TABLE "events" RENAME COLUMN "kind" TO "event_kind";',
|
||||
'ALTER TABLE "events" MODIFY COLUMN "event_kind" LowCardinality(String) DEFAULT \'view\';',
|
||||
]);
|
||||
assert.deepEqual(result.warnings, [
|
||||
'Renaming columns is not supported for clickhouse from this editor.',
|
||||
'Creating indexes is not supported for clickhouse from this editor.',
|
||||
assert.deepEqual(result.warnings, ['Creating indexes is not supported for clickhouse from this editor.']);
|
||||
});
|
||||
|
||||
test("builds ClickHouse nullable and comment column changes", () => {
|
||||
const result = buildTableStructureChangeSql({
|
||||
databaseType: "clickhouse",
|
||||
tableName: "events",
|
||||
columns: [
|
||||
column({
|
||||
id: "new",
|
||||
name: "source",
|
||||
dataType: "String",
|
||||
isNullable: true,
|
||||
comment: "traffic source",
|
||||
}),
|
||||
column({
|
||||
id: "status",
|
||||
name: "status",
|
||||
dataType: "Nullable(String)",
|
||||
isNullable: false,
|
||||
defaultValue: "",
|
||||
comment: "current status",
|
||||
original: {
|
||||
name: "status",
|
||||
data_type: "Nullable(String)",
|
||||
is_nullable: true,
|
||||
column_default: "'pending'",
|
||||
is_primary_key: false,
|
||||
extra: null,
|
||||
comment: "old status",
|
||||
},
|
||||
}),
|
||||
],
|
||||
indexes: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(result.warnings, []);
|
||||
assert.deepEqual(result.statements, [
|
||||
'ALTER TABLE "events" ADD COLUMN "source" Nullable(String);',
|
||||
'ALTER TABLE "events" COMMENT COLUMN "source" \'traffic source\';',
|
||||
'ALTER TABLE "events" MODIFY COLUMN "status" REMOVE DEFAULT;',
|
||||
'ALTER TABLE "events" MODIFY COLUMN "status" String;',
|
||||
'ALTER TABLE "events" COMMENT COLUMN "status" \'current status\';',
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue