fix(sql): keep merge update set line in statement continuation

This commit is contained in:
dienaso authored and GitHub committed 2026-09-19 10:55:37 +08:00
1 parent ffb915061f
commit 280af02120
2 files changed
+45 -1

No files matched your search

@@ -1472,6 +1472,45 @@ FROM orders;`;
}
});
it("keeps an Oracle MERGE together when each action starts its own line (#9516)", () => {
const merge = `MERGE INTO bom_template t USING (SELECT id, no FROM stage_bom) s ON (t.id = s.id)
WHEN MATCHED THEN
UPDATE SET
t.no = s.no
WHERE
t.no IS NULL
WHEN NOT MATCHED THEN
INSERT (id, no)
VALUES (s.id, s.no)`;
for (const needle of ["MERGE INTO", "UPDATE SET", "WHERE", "INSERT (id, no)", "VALUES (s.id, s.no)"]) {
expect(statementRangeAtCursor(merge, indexOf(merge, needle), "oracle")?.sql.trim()).toBe(merge);
}
expect(rangeSqlTexts(executableStatementRanges(merge, "oracle"))).toEqual([merge]);
});
it("keeps an Oracle MERGE delete action that starts its own line together", () => {
const merge = `MERGE INTO bom_template t USING (SELECT id FROM stage_bom) s ON (t.id = s.id)
WHEN MATCHED THEN
DELETE WHERE t.no IS NULL`;
expect(rangeSqlTexts(executableStatementRanges(merge, "oracle"))).toEqual([merge]);
});
it("keeps an Oracle MERGE together when the UPDATE action's SET starts its own line", () => {
const merge = `MERGE INTO bom_template t USING (SELECT id, no FROM stage_bom) s ON (t.id = s.id)
WHEN MATCHED THEN
UPDATE
SET t.no = s.no`;
const update = "UPDATE bom_template\nSET no = 'x'";
expect(rangeSqlTexts(executableStatementRanges(merge, "oracle"))).toEqual([merge]);
expect(rangeSqlTexts(executableStatementRanges(`${merge};\n\n${update};`, "oracle"))).toEqual([merge, update]);
});
it("still splits a standalone UPDATE that follows a terminated MERGE", () => {
const merge = "MERGE INTO bom_template t USING (SELECT id FROM stage_bom) s ON (t.id = s.id)\nWHEN MATCHED THEN UPDATE SET t.no = s.no";
const update = "UPDATE bom_template SET no = 'x'";
expect(rangeSqlTexts(executableStatementRanges(`${merge};\n\n${update};`, "oracle"))).toEqual([merge, update]);
});
it("returns the full SAP HANA DO block for cursors inside nested statements", () => {
const range = statementRangeAtCursor(sapHanaDoBlockFixture, indexOf(sapHanaDoBlockFixture, "Result"), "saphana");
@@ -1106,8 +1106,13 @@ function isMysqlAlterTableTruncatePartitionContinuation(sql: string, statementFr
}
function isMergeActionContinuation(sql: string, statementFrom: number, lineStartFrom: number, keyword: string, databaseType?: DatabaseType, parameterOptions?: SqlParameterOptions): boolean {
if (keyword !== "INSERT" || !startsWithSqlWords(sql, statementFrom, ["MERGE"], databaseType, parameterOptions)) return false;
// Oracle (and friends) allow each MERGE action on its own line after
// `WHEN ... MATCHED THEN`, e.g. `UPDATE SET ...` (#9516); only INSERT was
// recognized, so UPDATE/DELETE action lines split the statement in two.
if (keyword !== "INSERT" && keyword !== "UPDATE" && keyword !== "DELETE" && keyword !== "SET") return false;
if (!startsWithSqlWords(sql, statementFrom, ["MERGE"], databaseType, parameterOptions)) return false;
const words = topLevelWordsBefore(sql, statementFrom, lineStartFrom, 5, databaseType, parameterOptions);
if (keyword === "SET") return words[words.length - 1] === "UPDATE" && words.includes("THEN") && words.includes("MATCHED");
return words[words.length - 1] === "THEN" && words.includes("WHEN") && words.includes("MATCHED");
}