From 66ce21985acb1da3e2e4c5e5ba799bd53d5dd0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle?= Date: Mon, 23 Mar 2026 16:44:39 +0100 Subject: [PATCH 1/4] fix(NoTicket): vendor sqlparse CASE...END fix to allow PyPI publishing Replace git dependency on lorenzhs/sqlparse fork with a monkey-patch of StatementSplitter._change_splitlevel in statement_formatter.py. The upstream sqlparse project isn't very active and the fix for CASE...END level tracking (https://github.com/andialbrecht/sqlparse/pull/839) was not yet released. PyPI rejects packages with VCS dependencies, so vendoring the fix inline unblocks publishing. --- setup.cfg | 2 +- src/firebolt/common/statement_formatter.py | 76 ++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 136b00d645b..d41efdb7125 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,7 @@ install_requires = pydantic[dotenv]>=1.8.2,<3.0.0 python-dateutil>=2.8.2 readerwriterlock>=1.0.9 - sqlparse@git+https://github.com/lorenzhs/sqlparse.git@8d379386c1c3e103ee67ef6582ea1b7c2296aa5b + sqlparse>=0.5.5 trio>=0.22.0 truststore>=0.10;python_version>="3.10" python_requires = >=3.9 diff --git a/src/firebolt/common/statement_formatter.py b/src/firebolt/common/statement_formatter.py index 27a59aea71f..017ebf180e0 100644 --- a/src/firebolt/common/statement_formatter.py +++ b/src/firebolt/common/statement_formatter.py @@ -3,6 +3,10 @@ from typing import Dict, List, Optional, Sequence, Union from sqlparse import parse as parse_sql # type: ignore +from sqlparse import tokens as _T +from sqlparse.engine.statement_splitter import ( + StatementSplitter as _StatementSplitter, +) from sqlparse.sql import ( # type: ignore Comment, Comparison, @@ -15,6 +19,78 @@ from sqlparse.tokens import Whitespace # type: ignore from sqlparse.tokens import Token as TokenType # type: ignore + +def _patched_change_splitlevel(self, ttype, value): # type: ignore[no-untyped-def] + """Patched version of StatementSplitter._change_splitlevel. + + Fixes CASE...END level tracking outside of CREATE blocks. + See: https://github.com/andialbrecht/sqlparse/pull/839 + """ + if ttype is _T.Punctuation and value == "(": + return 1 + elif ttype is _T.Punctuation and value == ")": + return -1 + elif ttype not in _T.Keyword: + return 0 + + unified = value.upper() + + if ttype is _T.Keyword.DDL and unified.startswith("CREATE"): + self._is_create = True + return 0 + + if unified == "DECLARE" and self._is_create and self._begin_depth == 0: + self._in_declare = True + return 1 + + if unified == "BEGIN": + self._begin_depth += 1 + self._seen_begin = True + if self._is_create: + return 1 + return 0 + + if ( + self._seen_begin + and (ttype is _T.Keyword or ttype is _T.Name) + and unified + in ( + "TRANSACTION", + "WORK", + "TRAN", + "DISTRIBUTED", + "DEFERRED", + "IMMEDIATE", + "EXCLUSIVE", + ) + ): + self._begin_depth = max(0, self._begin_depth - 1) + self._seen_begin = False + return 0 + + if unified == "END": + if not self._in_case: + self._begin_depth = max(0, self._begin_depth - 1) + else: + self._in_case = False + return -1 + + if unified == "CASE": + self._in_case = True + return 1 + + if unified in ("IF", "FOR", "WHILE") and self._is_create and self._begin_depth > 0: + return 1 + + if unified in ("END IF", "END FOR", "END WHILE"): + return -1 + + return 0 + + +_StatementSplitter._change_splitlevel = _patched_change_splitlevel # type: ignore[method-assign] + + from firebolt.common._types import ParameterType, SetParameter from firebolt.utils.exception import ( DataError, From 6f3b9a338ea5f34e6ef4564150ca74b92292a044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle?= Date: Mon, 23 Mar 2026 17:04:18 +0100 Subject: [PATCH 2/4] make flake8 happy --- src/firebolt/common/statement_formatter.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/firebolt/common/statement_formatter.py b/src/firebolt/common/statement_formatter.py index 017ebf180e0..cd7b43e0b2f 100644 --- a/src/firebolt/common/statement_formatter.py +++ b/src/firebolt/common/statement_formatter.py @@ -19,6 +19,13 @@ from sqlparse.tokens import Whitespace # type: ignore from sqlparse.tokens import Token as TokenType # type: ignore +from firebolt.common._types import ParameterType, SetParameter +from firebolt.utils.exception import ( + DataError, + InterfaceError, + NotSupportedError, +) + def _patched_change_splitlevel(self, ttype, value): # type: ignore[no-untyped-def] """Patched version of StatementSplitter._change_splitlevel. @@ -88,16 +95,10 @@ def _patched_change_splitlevel(self, ttype, value): # type: ignore[no-untyped-d return 0 -_StatementSplitter._change_splitlevel = _patched_change_splitlevel # type: ignore[method-assign] +_StatementSplitter._change_splitlevel = \ + _patched_change_splitlevel # type: ignore[method-assign] -from firebolt.common._types import ParameterType, SetParameter -from firebolt.utils.exception import ( - DataError, - InterfaceError, - NotSupportedError, -) - escape_chars_v2 = { "\0": "\\0", "'": "''", From 71bae3e4c3e6fda9756ae6b52b57d1a1c96396bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle?= Date: Mon, 23 Mar 2026 17:18:56 +0100 Subject: [PATCH 3/4] make mypy happy --- src/firebolt/common/statement_formatter.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/firebolt/common/statement_formatter.py b/src/firebolt/common/statement_formatter.py index cd7b43e0b2f..636b92a21e5 100644 --- a/src/firebolt/common/statement_formatter.py +++ b/src/firebolt/common/statement_formatter.py @@ -95,8 +95,7 @@ def _patched_change_splitlevel(self, ttype, value): # type: ignore[no-untyped-d return 0 -_StatementSplitter._change_splitlevel = \ - _patched_change_splitlevel # type: ignore[method-assign] +setattr(_StatementSplitter, "_change_splitlevel", _patched_change_splitlevel) escape_chars_v2 = { From 6d21bd84ef66fcb70ab7ceff7bf351479bb502a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle?= Date: Mon, 23 Mar 2026 17:32:42 +0100 Subject: [PATCH 4/4] pin --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index d41efdb7125..be198a8a537 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,7 @@ install_requires = pydantic[dotenv]>=1.8.2,<3.0.0 python-dateutil>=2.8.2 readerwriterlock>=1.0.9 - sqlparse>=0.5.5 + sqlparse==0.5.5 trio>=0.22.0 truststore>=0.10;python_version>="3.10" python_requires = >=3.9