mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 17:09:56 +00:00
fix(clickhouse): keep ALTER TABLE UPDATE mutations together
This commit is contained in:
@@ -443,6 +443,44 @@ COMMENT = '测试';`;
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "mysql"))).toEqual([sql.slice(0, -1)]);
|
||||
});
|
||||
|
||||
it("keeps issue #4045 ClickHouse ALTER TABLE UPDATE mutation together", () => {
|
||||
const sql = `ALTER TABLE m2_db.history_data5
|
||||
UPDATE value = 14.06
|
||||
WHERE uuid IN (
|
||||
SELECT uuid
|
||||
FROM m2_db.pt_uuid_mapping
|
||||
WHERE point_id = 'TEST_Location_1_L2_METER_2_AE_DIFF_H'
|
||||
)
|
||||
AND ts = toDateTime('2026-07-15 12:00:00');`;
|
||||
|
||||
expect(statementRangeAtCursor(sql, indexOf(sql, "ALTER"), "clickhouse")?.sql.trim()).toBe(sql.slice(0, -1));
|
||||
expect(statementRangeAtCursor(sql, indexOf(sql, "UPDATE"), "clickhouse")?.sql.trim()).toBe(sql.slice(0, -1));
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "clickhouse"))).toEqual([sql.slice(0, -1)]);
|
||||
});
|
||||
|
||||
it("keeps a simple ClickHouse ALTER TABLE UPDATE mutation together", () => {
|
||||
const sql = "ALTER TABLE `events` ON CLUSTER 'analytics'\nUPDATE value = 1\nWHERE id = 42;";
|
||||
|
||||
expect(statementRangeAtCursor(sql, indexOf(sql, "UPDATE"), "clickhouse")?.sql.trim()).toBe(sql.slice(0, -1));
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "clickhouse"))).toEqual([sql.slice(0, -1)]);
|
||||
});
|
||||
|
||||
it("separates statements after a ClickHouse ALTER TABLE UPDATE mutation", () => {
|
||||
const mutation = "ALTER TABLE events\nUPDATE value = 1\nWHERE id = 42";
|
||||
const sql = `${mutation};\nSELECT * FROM events;\nUPDATE audit SET processed = 1;`;
|
||||
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "clickhouse"))).toEqual([mutation, "SELECT * FROM events", "UPDATE audit SET processed = 1"]);
|
||||
});
|
||||
|
||||
it("does not merge independent UPDATE statements into other ALTER statements", () => {
|
||||
const otherDatabaseSql = "ALTER TABLE events\nUPDATE audit SET processed = 1;";
|
||||
const clickHouseSql = "ALTER TABLE events ADD COLUMN source String\nUPDATE audit SET processed = 1;";
|
||||
|
||||
expect(rangeSqlTexts(executableStatementRanges(otherDatabaseSql, "postgres"))).toEqual(["ALTER TABLE events", "UPDATE audit SET processed = 1"]);
|
||||
expect(rangeSqlTexts(executableStatementRanges(otherDatabaseSql, "mysql"))).toEqual(["ALTER TABLE events", "UPDATE audit SET processed = 1"]);
|
||||
expect(rangeSqlTexts(executableStatementRanges(clickHouseSql, "clickhouse"))).toEqual(["ALTER TABLE events ADD COLUMN source String", "UPDATE audit SET processed = 1"]);
|
||||
});
|
||||
|
||||
it("keeps insert-select with the INSERT statement", () => {
|
||||
const sql = "INSERT INTO archived_users (id, name)\nSELECT id, name FROM users\nUPDATE users SET archived = 1;";
|
||||
const range = statementRangeAtCursor(sql, indexOf(sql, "archived_users"));
|
||||
|
||||
@@ -246,6 +246,7 @@ const EXPLAIN_STATEMENT_KEYWORDS = new Set(["SELECT", "WITH", "INSERT", "UPDATE"
|
||||
const CREATE_BODY_KEYWORDS = new Set(["SELECT", "WITH", "BEGIN", "DECLARE"]);
|
||||
const INSERT_BODY_KEYWORDS = new Set(["SELECT", "WITH"]);
|
||||
const ALTER_BODY_KEYWORDS = new Set(["ADD", "ALTER", "COMMENT", "DROP", "MODIFY", "RENAME", "SET"]);
|
||||
const CLICKHOUSE_ALTER_TABLE_HEADER = /^ALTER\s+TABLE\s+(?:(?:[A-Za-z_][\w$]*|`(?:``|[^`])+`|"(?:""|[^"])+")\s*\.\s*)?(?:[A-Za-z_][\w$]*|`(?:``|[^`])+`|"(?:""|[^"])+")(?:\s+ON\s+CLUSTER\s+(?:[A-Za-z_][\w$]*|`(?:``|[^`])+`|"(?:""|[^"])+"|'(?:''|[^'])+'))?\s*$/i;
|
||||
const SET_OPERATION_KEYWORDS = new Set(["UNION", "INTERSECT", "EXCEPT", "MINUS"]);
|
||||
const SET_OPERATION_MODIFIER_KEYWORDS = new Set(["ALL", "DISTINCT"]);
|
||||
const ORACLE_LIKE_PL_SQL_DATABASES: ReadonlySet<DatabaseType> = new Set(["oracle", "dameng", "gaussdb", "yashandb", "oscar", "oceanbase-oracle"]);
|
||||
@@ -682,6 +683,12 @@ function splitStatementRangeAtSoftStarts(sql: string, statement: RawStatement, d
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentBodyKeyword === "ALTER" && isClickHouseAlterTableUpdateContinuation(sql, boundaries[boundaries.length - 1].from, lineStart.from, lineStart.keyword, databaseType)) {
|
||||
// ClickHouse mutations use UPDATE as the first ALTER TABLE action, not as
|
||||
// a standalone statement. Keep this dialect-specific to preserve soft boundaries elsewhere.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentBodyKeyword === "ALTER" && ALTER_BODY_KEYWORDS.has(lineStart.keyword)) {
|
||||
continue;
|
||||
}
|
||||
@@ -917,6 +924,11 @@ function isMysqlCreateTableOptionContinuation(sql: string, statementFrom: number
|
||||
return next === "=" || next === "'" || next === '"';
|
||||
}
|
||||
|
||||
function isClickHouseAlterTableUpdateContinuation(sql: string, statementFrom: number, lineStartFrom: number, keyword: string, databaseType?: DatabaseType): boolean {
|
||||
if (databaseType !== "clickhouse" || keyword !== "UPDATE") return false;
|
||||
return CLICKHOUSE_ALTER_TABLE_HEADER.test(sql.slice(statementFrom, lineStartFrom));
|
||||
}
|
||||
|
||||
function startsWithMysqlCreateTable(sql: string, statementFrom: number): boolean {
|
||||
const text = sql.slice(statementFrom, statementFrom + 256);
|
||||
return /^CREATE\s+(?:TEMPORARY\s+)?TABLE\b/i.test(text);
|
||||
|
||||
Reference in New Issue
Block a user