1 parent
0ba2021181
commit
2d321b3537
2 files changed
+70
No files matched your search
@@ -89,6 +89,17 @@ BEGIN
|
||||
SELECT 1 + 2 INTO PRE_TRD_DATE FROM DUAL;
|
||||
END;`;
|
||||
|
||||
const oracleConsecutiveNestedBlocks = `CREATE OR REPLACE PROCEDURE dbx_consecutive_blocks AS
|
||||
BEGIN
|
||||
BEGIN
|
||||
NULL;
|
||||
END;
|
||||
BEGIN
|
||||
NULL;
|
||||
END;
|
||||
NULL;
|
||||
END;`;
|
||||
|
||||
const gaussDbNestedProcedure = `CREATE OR REPLACE PROCEDURE public.dbx_issue_4318()
|
||||
AS
|
||||
BEGIN
|
||||
@@ -263,6 +274,18 @@ BEGIN
|
||||
END;
|
||||
SELECT 2;`;
|
||||
|
||||
const mysqlRoutineWithConsecutiveBlocksFixture = `CREATE PROCEDURE p_consecutive_blocks()
|
||||
BEGIN
|
||||
BEGIN
|
||||
SELECT 1;
|
||||
END;
|
||||
BEGIN
|
||||
SELECT 2;
|
||||
END;
|
||||
SELECT 3;
|
||||
END;
|
||||
SELECT 4;`;
|
||||
|
||||
const mysqlDelimitedRoutineFixture = `DELIMITER //
|
||||
CREATE PROCEDURE sp_insert_random_users(IN p_count INT)
|
||||
BEGIN
|
||||
@@ -285,6 +308,17 @@ BEGIN
|
||||
END;
|
||||
SELECT 2 FROM DUMMY;`;
|
||||
|
||||
const sapHanaDoBlockWithConsecutiveBlocksFixture = `DO
|
||||
BEGIN
|
||||
BEGIN
|
||||
SELECT 1 AS "First" FROM DUMMY;
|
||||
END;
|
||||
BEGIN
|
||||
SELECT 2 AS "Second" FROM DUMMY;
|
||||
END;
|
||||
END;
|
||||
SELECT 3 FROM DUMMY;`;
|
||||
|
||||
describe("splitSqlStatementRanges", () => {
|
||||
it("splits multiple top-level statements", () => {
|
||||
const sql = "SELECT 1;\nSELECT 2;\nSELECT 3;";
|
||||
@@ -379,6 +413,10 @@ describe("splitSqlStatementRanges", () => {
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges(mysqlRoutineWithNestedCaseBlockFixture, "mysql"))).toEqual([mysqlRoutineWithNestedCaseBlockFixture.slice(0, mysqlRoutineWithNestedCaseBlockFixture.indexOf("\nSELECT 2;")).replace(/;$/, "").trim(), "SELECT 2"]);
|
||||
});
|
||||
|
||||
it("keeps consecutive nested MySQL blocks inside their procedure", () => {
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges(mysqlRoutineWithConsecutiveBlocksFixture, "mysql"))).toEqual([mysqlRoutineWithConsecutiveBlocksFixture.slice(0, mysqlRoutineWithConsecutiveBlocksFixture.indexOf("\nSELECT 4;")).replace(/;$/, "").trim(), "SELECT 4"]);
|
||||
});
|
||||
|
||||
it("does not merge regular MySQL transaction statements as routine blocks", () => {
|
||||
const sql = "BEGIN; INSERT INTO t VALUES (1); COMMIT;";
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges(sql, "mysql"))).toEqual(["BEGIN", "INSERT INTO t VALUES (1)", "COMMIT"]);
|
||||
@@ -406,6 +444,32 @@ describe("splitSqlStatementRanges", () => {
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges(oracleIssue2405PlSql, "oracle"))).toEqual([oracleIssue2405PlSql]);
|
||||
});
|
||||
|
||||
it("keeps consecutive nested Oracle blocks inside their procedure", () => {
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges(`${oracleConsecutiveNestedBlocks}\nSELECT 1;`, "oracle"))).toEqual([oracleConsecutiveNestedBlocks, "SELECT 1"]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["BEGIN", "BEGIN\n NULL;\n END;"],
|
||||
["IF", "IF 1 = 1 THEN\n NULL;\n END IF;"],
|
||||
["LOOP", "LOOP\n EXIT;\n END LOOP;"],
|
||||
["CASE", "CASE WHEN 1 = 1 THEN\n NULL;\n END CASE;"],
|
||||
])("treats %s after a semicolon as a new Oracle scope", (_starter, scope) => {
|
||||
const procedure = `CREATE OR REPLACE PROCEDURE dbx_delimited_scope AS
|
||||
BEGIN
|
||||
BEGIN
|
||||
NULL;
|
||||
END;
|
||||
${scope}
|
||||
NULL;
|
||||
END;`;
|
||||
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges(`${procedure}\nSELECT 1;`, "oracle"))).toEqual([procedure, "SELECT 1"]);
|
||||
});
|
||||
|
||||
it("keeps BEGIN TRANSACTION outside Oracle PL/SQL block handling", () => {
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges("BEGIN TRANSACTION;\nSELECT 1;", "oracle"))).toEqual(["BEGIN TRANSACTION", "SELECT 1"]);
|
||||
});
|
||||
|
||||
it("splits large Dameng package bodies without repeated prefix parsing", () => {
|
||||
const body = Array.from({ length: 3000 }, (_, index) => ` v_value := v_value + ${index % 10};`).join("\n");
|
||||
const packageBody = `CREATE OR REPLACE PACKAGE BODY app.big_pkg AS
|
||||
@@ -496,6 +560,10 @@ END;`;
|
||||
expect(ranges[0].sql).toContain('SELECT 1 AS "Result" FROM DUMMY;');
|
||||
expect(ranges[0].sql).toContain("END;");
|
||||
});
|
||||
|
||||
it("keeps consecutive nested SAP HANA blocks inside their DO block", () => {
|
||||
expect(rangeSqlTexts(splitSqlStatementRanges(sapHanaDoBlockWithConsecutiveBlocksFixture, "saphana"))).toEqual([sapHanaDoBlockWithConsecutiveBlocksFixture.slice(0, sapHanaDoBlockWithConsecutiveBlocksFixture.indexOf("\nSELECT 3")), "SELECT 3 FROM DUMMY"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("statementRangeAtCursor", () => {
|
||||
|
||||
@@ -1952,6 +1952,7 @@ function oraclePlSqlTokens(sql: string): OraclePlSqlToken[] {
|
||||
|
||||
function previousWordToken(tokens: Array<{ kind: "word" | "semicolon"; value: string }>, index: number): string | null {
|
||||
for (let i = index - 1; i >= 0; i -= 1) {
|
||||
if (tokens[i].kind === "semicolon") return null;
|
||||
if (tokens[i].kind === "word") return tokens[i].value;
|
||||
}
|
||||
return null;
|
||||
@@ -1959,6 +1960,7 @@ function previousWordToken(tokens: Array<{ kind: "word" | "semicolon"; value: st
|
||||
|
||||
function nextWordToken(tokens: Array<{ kind: "word" | "semicolon"; value: string }>, index: number): string | null {
|
||||
for (let i = index + 1; i < tokens.length; i += 1) {
|
||||
if (tokens[i].kind === "semicolon") return null;
|
||||
if (tokens[i].kind === "word") return tokens[i].value;
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user