fix(sql): keep Oracle anonymous blocks with local routines whole

This commit is contained in:
Melody Ruch authored and GitHub committed 2026-09-20 12:17:15 +08:00
1 parent 929c72407d
commit ffe1e3a2ee
3 files changed
+70 -3

No files matched your search

+46 -2
View File
@@ -2535,11 +2535,30 @@ impl OraclePlSqlBlock {
for (index, token) in self.tokens.iter().enumerate() {
if token.is_semicolon() {
if matches!(scopes.last(), Some(OraclePlSqlScope::RoutineHeader)) {
scopes.pop();
}
continue;
}
if token.is_word("BEGIN") {
scopes.push(OraclePlSqlScope::Block);
if token.is_word("DECLARE") {
if !matches!(scopes.last(), Some(OraclePlSqlScope::Declaration)) {
scopes.push(OraclePlSqlScope::Declaration);
}
} else if token.is_any_word(&["PROCEDURE", "FUNCTION"])
&& matches!(scopes.last(), Some(OraclePlSqlScope::Declaration | OraclePlSqlScope::Routine))
{
scopes.push(OraclePlSqlScope::RoutineHeader);
} else if token.is_any_word(&["IS", "AS"]) && matches!(scopes.last(), Some(OraclePlSqlScope::RoutineHeader))
{
*scopes.last_mut().unwrap() = OraclePlSqlScope::Routine;
} else if token.is_word("BEGIN") {
// A local routine's BEGIN belongs to that routine, not to the outer block.
if matches!(scopes.last(), Some(OraclePlSqlScope::Declaration | OraclePlSqlScope::Routine)) {
*scopes.last_mut().unwrap() = OraclePlSqlScope::Block;
} else {
scopes.push(OraclePlSqlScope::Block);
}
saw_begin = true;
complete = false;
} else if token.is_word("CASE") {
@@ -2631,6 +2650,12 @@ enum OraclePlSqlCreateObjectKind {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OraclePlSqlScope {
Object,
/// DECLARE section awaiting its own BEGIN.
Declaration,
/// Local PROCEDURE/FUNCTION header, before IS/AS (a `;` here is a forward declaration).
RoutineHeader,
/// Local PROCEDURE/FUNCTION after IS/AS, awaiting its own BEGIN.
Routine,
Block,
Case,
}
@@ -3833,6 +3858,25 @@ SELECT 2 FROM DUMMY;";
);
}
#[test]
fn oracle_split_keeps_anonymous_block_with_local_routines_together() {
let cases = [
"DECLARE\nPROCEDURE local_proc IS\nBEGIN\n NULL;\nEND;\n\nBEGIN\nlocal_proc;\nEND;",
"DECLARE\n v NUMBER;\n PROCEDURE p1 IS BEGIN NULL; END;\n FUNCTION f1 RETURN NUMBER IS BEGIN RETURN 1; END f1;\nBEGIN\n p1;\n v := f1;\nEND;",
"DECLARE\n PROCEDURE fwd(x NUMBER);\n PROCEDURE fwd(x NUMBER) IS BEGIN NULL; END;\nBEGIN\n fwd(1);\nEND;",
"DECLARE\n PROCEDURE outer_p IS\n PROCEDURE inner_p IS BEGIN NULL; END;\n BEGIN\n inner_p;\n END;\nBEGIN\n outer_p;\nEND;",
];
for sql in cases {
assert_eq!(split_sql_statements_for_database(sql, DatabaseType::Oracle), vec![sql.to_string()], "{sql}");
}
let block = cases[0];
let sql = format!("{block}\nSELECT 1 FROM dual;");
assert_eq!(
split_sql_statements_for_database(&sql, DatabaseType::Oracle),
vec![block.to_string(), "SELECT 1 FROM dual".to_string()]
);
}
#[test]
fn oracle_like_split_keeps_anonymous_plsql_block_together() {
let sql = "\