From 0bb6ae87bf4b7f9f0c6a189ab2d492d06ebdd470 Mon Sep 17 00:00:00 2001 From: Vlad Vladov Date: Tue, 19 May 2026 14:23:00 +0300 Subject: [PATCH] Fix BEGIN READ WRITE regression for Redshift syntax Redshift allows BEGIN with transaction mode modifiers such as READ WRITE or ISOLATION LEVEL. Treat those tokens like transaction starters so split() does not mistake BEGIN for a procedural block. --- sqlparse/engine/statement_splitter.py | 3 ++- tests/test_split.py | 29 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/sqlparse/engine/statement_splitter.py b/sqlparse/engine/statement_splitter.py index bc57d170..b101724e 100644 --- a/sqlparse/engine/statement_splitter.py +++ b/sqlparse/engine/statement_splitter.py @@ -132,7 +132,8 @@ def _change_splitlevel(self, ttype, value): (ttype is T.Keyword or ttype is T.Name) and \ unified in ('TRANSACTION', 'WORK', 'TRAN', 'DISTRIBUTED', 'DEFERRED', - 'IMMEDIATE', 'EXCLUSIVE'): + 'IMMEDIATE', 'EXCLUSIVE', + 'ISOLATION', 'READ'): self._seen_begin = False if self._block_stack and self._block_stack[-1] == 'BEGIN': self._block_stack.pop() diff --git a/tests/test_split.py b/tests/test_split.py index 92c3fefe..1ea7b019 100644 --- a/tests/test_split.py +++ b/tests/test_split.py @@ -285,6 +285,33 @@ def test_split_begin_transaction_formatted(): # issue826 assert stmts[2].startswith('INSERT') assert stmts[3] == 'END\nTRANSACTION;' +# https://github.com/andialbrecht/sqlparse/issues/843 +def test_split_begin_read_write(): + # Redshift BEGIN READ WRITE should not be treated as a block start + sql = """BEGIN READ WRITE; +DELETE FROM schema.table_a USING table_a_temp +WHERE schema.table_a.id = table_a_temp.id; +INSERT INTO schema.table_a SELECT * FROM table_a_temp; +END TRANSACTION;""" + stmts = sqlparse.split(sql) + assert len(stmts) == 4 + assert stmts[0] == 'BEGIN READ WRITE;' + assert stmts[1].startswith('DELETE') + assert stmts[2].startswith('INSERT') + assert stmts[3] == 'END TRANSACTION;' + + +# https://github.com/andialbrecht/sqlparse/issues/843 +def test_split_begin_isolation_level_read_only(): + sql = """BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY; +SELECT 1; +END TRANSACTION;""" + stmts = sqlparse.split(sql) + assert len(stmts) == 3 + assert stmts[0] == 'BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY;' + assert stmts[1] == 'SELECT 1;' + assert stmts[2] == 'END TRANSACTION;' + def test_split_anonymous_begin_end_for(): # issue845 Case 1 sql = """ @@ -372,5 +399,3 @@ def test_split_standalone_for_update(): assert len(stmts) == 2 assert stmts[0] == "SELECT * FROM foo FOR UPDATE;" assert stmts[1] == "SELECT 3;" - -