fix(sql): recognize MySQL truncate partition statements
This commit is contained in:
2 files changed
+81
-2
No files matched your search
@@ -443,6 +443,35 @@ COMMENT = '测试';`;
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "mysql"))).toEqual([sql.slice(0, -1)]);
|
||||
});
|
||||
|
||||
it("keeps MySQL ALTER TABLE truncate partition clauses with the statement", () => {
|
||||
const sql = "ALTER TABLE ems.r_r_curve_e_hour\nTRUNCATE PARTITION p202602, p202603, p202604, p202605, p202606;";
|
||||
|
||||
expect(statementRangeAtCursor(sql, indexOf(sql, "ALTER"), "mysql")?.sql.trim()).toBe(sql.slice(0, -1));
|
||||
expect(statementRangeAtCursor(sql, indexOf(sql, "TRUNCATE"), "mysql")?.sql.trim()).toBe(sql.slice(0, -1));
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "mysql"))).toEqual([sql.slice(0, -1)]);
|
||||
});
|
||||
|
||||
it("keeps MySQL truncate partition clauses when comments separate ALTER TABLE", () => {
|
||||
const sql = "ALTER /* online ddl */ TABLE t\nTRUNCATE PARTITION p0;";
|
||||
|
||||
expect(statementRangeAtCursor(sql, indexOf(sql, "TRUNCATE"), "mysql")?.sql.trim()).toBe(sql.slice(0, -1));
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "mysql"))).toEqual([sql.slice(0, -1)]);
|
||||
});
|
||||
|
||||
it("keeps MySQL truncate partition clauses when comments precede PARTITION", () => {
|
||||
const sql = "ALTER TABLE t\nTRUNCATE /* keep */ PARTITION p0;";
|
||||
|
||||
expect(statementRangeAtCursor(sql, indexOf(sql, "TRUNCATE"), "mysql")?.sql.trim()).toBe(sql.slice(0, -1));
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "mysql"))).toEqual([sql.slice(0, -1)]);
|
||||
});
|
||||
|
||||
it("keeps standalone truncate table statements separate from preceding alter statements", () => {
|
||||
const sql = "ALTER TABLE events ADD COLUMN source varchar(100)\nTRUNCATE TABLE events;";
|
||||
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "mysql"))).toEqual(["ALTER TABLE events ADD COLUMN source varchar(100)", "TRUNCATE TABLE events"]);
|
||||
expect(rangeSqlTexts(executableStatementRanges(sql, "postgres"))).toEqual(["ALTER TABLE events ADD COLUMN source varchar(100)", "TRUNCATE TABLE events"]);
|
||||
});
|
||||
|
||||
it("keeps issue #4045 ClickHouse ALTER TABLE UPDATE mutation together", () => {
|
||||
const sql = `ALTER TABLE m2_db.history_data5
|
||||
UPDATE value = 14.06
|
||||
|
||||
@@ -689,6 +689,10 @@ function splitStatementRangeAtSoftStarts(sql: string, statement: RawStatement, d
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentBodyKeyword === "ALTER" && isMysqlAlterTableTruncatePartitionContinuation(sql, boundaries[boundaries.length - 1].from, lineStart.from, lineStart.keyword, databaseType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentBodyKeyword === "ALTER" && ALTER_BODY_KEYWORDS.has(lineStart.keyword)) {
|
||||
continue;
|
||||
}
|
||||
@@ -929,6 +933,12 @@ function isClickHouseAlterTableUpdateContinuation(sql: string, statementFrom: nu
|
||||
return CLICKHOUSE_ALTER_TABLE_HEADER.test(sql.slice(statementFrom, lineStartFrom));
|
||||
}
|
||||
|
||||
function isMysqlAlterTableTruncatePartitionContinuation(sql: string, statementFrom: number, lineStartFrom: number, keyword: string, databaseType?: DatabaseType): boolean {
|
||||
if (databaseType !== "mysql" || keyword !== "TRUNCATE") return false;
|
||||
if (!startsWithSqlWords(sql, statementFrom, ["ALTER", "TABLE"])) return false;
|
||||
return nextSqlWord(sql, lineStartFrom + keyword.length) === "PARTITION";
|
||||
}
|
||||
|
||||
function startsWithMysqlCreateTable(sql: string, statementFrom: number): boolean {
|
||||
const text = sql.slice(statementFrom, statementFrom + 256);
|
||||
return /^CREATE\s+(?:TEMPORARY\s+)?TABLE\b/i.test(text);
|
||||
@@ -1097,9 +1107,49 @@ function nextNonWhitespaceChar(sql: string, pos: number): string | null {
|
||||
}
|
||||
|
||||
function nextSqlWord(sql: string, pos: number): string | null {
|
||||
return nextSqlWordToken(sql, pos)?.word ?? null;
|
||||
}
|
||||
|
||||
function startsWithSqlWords(sql: string, pos: number, expectedWords: readonly string[]): boolean {
|
||||
let i = pos;
|
||||
while (i < sql.length && isSqlWhitespace(sql[i])) i += 1;
|
||||
return /^[A-Za-z_][\w$]*/.exec(sql.slice(i))?.[0]?.toUpperCase() ?? null;
|
||||
for (const expectedWord of expectedWords) {
|
||||
const token = nextSqlWordToken(sql, i);
|
||||
if (token?.word !== expectedWord) return false;
|
||||
i = token.end;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function nextSqlWordToken(sql: string, pos: number): { word: string; end: number } | null {
|
||||
let i = pos;
|
||||
// SQL comments may legally appear anywhere whitespace can separate keywords.
|
||||
while (i < sql.length) {
|
||||
if (isSqlWhitespace(sql[i])) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (sql[i] === "-" && sql[i + 1] === "-") {
|
||||
i += 2;
|
||||
while (i < sql.length && sql[i] !== "\n" && sql[i] !== "\r") i += 1;
|
||||
continue;
|
||||
}
|
||||
if (sql[i] === "#") {
|
||||
i += 1;
|
||||
while (i < sql.length && sql[i] !== "\n" && sql[i] !== "\r") i += 1;
|
||||
continue;
|
||||
}
|
||||
if (sql[i] === "/" && sql[i + 1] === "*") {
|
||||
const commentEnd = sql.indexOf("*/", i + 2);
|
||||
if (commentEnd < 0) return null;
|
||||
i = commentEnd + 2;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const match = /^[A-Za-z_][\w$]*/.exec(sql.slice(i));
|
||||
if (!match) return null;
|
||||
return { word: match[0].toUpperCase(), end: i + match[0].length };
|
||||
}
|
||||
|
||||
function isExplainLikeKeyword(keyword: string | null): boolean {
|
||||
|
||||
Reference in new issue
Block a user