From 8d379386c1c3e103ee67ef6582ea1b7c2296aa5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle?= Date: Thu, 26 Feb 2026 12:13:01 +0100 Subject: [PATCH] Fix end unconditionally decrementing the split level, but case not incrementing it in a plain select --- sqlparse/engine/statement_splitter.py | 8 ++++--- tests/test_split.py | 31 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/sqlparse/engine/statement_splitter.py b/sqlparse/engine/statement_splitter.py index 62aa9d9e..57905c81 100644 --- a/sqlparse/engine/statement_splitter.py +++ b/sqlparse/engine/statement_splitter.py @@ -80,10 +80,12 @@ def _change_splitlevel(self, ttype, value): self._in_case = False return -1 - if (unified in ('IF', 'FOR', 'WHILE', 'CASE') + if unified == 'CASE': + self._in_case = True + return 1 + + if (unified in ('IF', 'FOR', 'WHILE') and self._is_create and self._begin_depth > 0): - if unified == 'CASE': - self._in_case = True return 1 if unified in ('END IF', 'END FOR', 'END WHILE'): diff --git a/tests/test_split.py b/tests/test_split.py index 2b91b6a3..12360b70 100644 --- a/tests/test_split.py +++ b/tests/test_split.py @@ -284,3 +284,34 @@ def test_split_begin_transaction_formatted(): # issue826 assert stmts[1].startswith('DELETE') assert stmts[2].startswith('INSERT') assert stmts[3] == 'END\nTRANSACTION;' + + +def test_splitlevel_case_end(): + # CASE in a plain SELECT did not increment the level, but its matching END + # decremented it unconditionally. This led to levels being wrong after the + # CASE WHEN ... END block. + s = sqlparse.engine.statement_splitter.StatementSplitter() + level = s.level + + token_stream = [ + (sqlparse.tokens.Keyword.DML, 'SELECT'), + (sqlparse.tokens.Keyword, 'CASE'), + (sqlparse.tokens.Keyword, 'WHEN'), + (sqlparse.tokens.Name, 'foo'), + (sqlparse.tokens.Keyword, 'THEN'), + (sqlparse.tokens.Number, '1'), + (sqlparse.tokens.Keyword, 'END'), + (sqlparse.tokens.Keyword, 'FROM'), + (sqlparse.tokens.Name, 't'), + ] + + for ttype, value in token_stream: + level += s._change_splitlevel(ttype, value) + + assert level == 0 + + # This issue could lead to incorrectly treating a semicolon inside a text + # literal as a statement terminator and incorrectly splitting the query. + assert len(sqlparse.parse( + "SELECT CASE WHEN 1 THEN 2 END, test IN ('foo \\', 'foo;') FROM t" + )) == 1