diff --git a/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts b/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts index 201dc7ace0..10eb926fb9 100644 --- a/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts +++ b/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts @@ -531,6 +531,15 @@ END pkg_utils_without_replace;`; expect(rangeSqlTexts(splitSqlStatementRanges(`${packageSpecWithoutReplace}\nSELECT 1;`, "xugu"))).toEqual([packageSpecWithoutReplace, "SELECT 1"]); }); + it("splits a declaration-only Oracle package body before following DML", () => { + const packageBody = `CREATE OR REPLACE PACKAGE BODY packageName IS +null; +END packageName;`; + + expect(rangeSqlTexts(splitSqlStatementRanges(`${packageBody}\nSELECT * FROM goods;`, "oracle"))).toEqual([packageBody, "SELECT * FROM goods"]); + expect(rangeSqlTexts(splitSqlStatementRanges(`${packageBody}\n/\nSELECT * FROM goods;`, "oracle"))).toEqual([packageBody, "SELECT * FROM goods"]); + }); + it("splits plain CREATE TYPE AS OBJECT on semicolon without waiting for END", () => { const sql = "CREATE OR REPLACE TYPE address_t AS OBJECT (id INT);\nSELECT 1;"; expect(rangeSqlTexts(splitSqlStatementRanges(sql, "xugu"))).toEqual(["CREATE OR REPLACE TYPE address_t AS OBJECT (id INT)", "SELECT 1"]); diff --git a/apps/desktop/src/lib/sql/sqlStatementRanges.ts b/apps/desktop/src/lib/sql/sqlStatementRanges.ts index 8c052ab1bd..ba6d5a8797 100644 --- a/apps/desktop/src/lib/sql/sqlStatementRanges.ts +++ b/apps/desktop/src/lib/sql/sqlStatementRanges.ts @@ -1782,7 +1782,7 @@ function oraclePlSqlBlockEnd(sql: string): number | null { for (let index = 0; index < tokens.length; index += 1) { const token = tokens[index]; if (token.kind === "semicolon") { - const complete = objectKind === "spec" ? stack.length === 0 : sawBegin && stack.length === 0; + const complete = objectKind !== null ? stack.length === 0 : sawBegin && stack.length === 0; if (complete) return token.to; continue; } diff --git a/crates/dbx-core/src/sql.rs b/crates/dbx-core/src/sql.rs index 76c5b485fd..6c68872d88 100644 --- a/crates/dbx-core/src/sql.rs +++ b/crates/dbx-core/src/sql.rs @@ -2288,8 +2288,10 @@ impl OraclePlSqlBlock { } match object_kind { - // Specs complete on outer END [name]; without requiring BEGIN. - Some(OraclePlSqlCreateObjectKind::Spec) => complete, + // Package/type objects complete on their outer END [name]; a package + // body may omit an initialization BEGIN when it has no executable + // initialization section. + Some(OraclePlSqlCreateObjectKind::Body | OraclePlSqlCreateObjectKind::Spec) => complete, _ => saw_begin && complete, } } @@ -3927,6 +3929,22 @@ SELECT 1;"; ); } + #[test] + fn oracle_like_split_declaration_only_package_body_before_following_dml() { + let package_body = "CREATE OR REPLACE PACKAGE BODY packageName IS\nnull;\nEND packageName;"; + assert_eq!( + split_sql_statements_for_database(&format!("{package_body}\nSELECT * FROM goods;"), DatabaseType::Oracle), + vec![package_body, "SELECT * FROM goods"] + ); + assert_eq!( + split_sql_statements_for_database( + &format!("{package_body}\n/\nSELECT * FROM goods;"), + DatabaseType::Oracle + ), + vec![package_body, "SELECT * FROM goods"] + ); + } + #[test] fn xugu_split_keeps_create_type_body_together() { let sql = "\