From 414f479cde73ca19bf2bcc50b75242bb7afb49cf Mon Sep 17 00:00:00 2001 From: Rodrigo-Palma Date: Sat, 19 Sep 2026 18:28:41 -0300 Subject: [PATCH] fix(expressions): keep tabs inside string literals pyparsing expands tabs in the input before matching, so a tab inside a string literal was rewritten into spaces, and the number of spaces depended on where the literal sat in the expression: parse("a = 'x\ty'") and parse("ab = 'x\ty'") returned different values for the same literal. A filter on a value containing a tab silently matched nothing. --- pyiceberg/expressions/parser.py | 4 ++++ tests/expressions/test_parser.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/pyiceberg/expressions/parser.py b/pyiceberg/expressions/parser.py index d813bcaa07..73da05c2ec 100644 --- a/pyiceberg/expressions/parser.py +++ b/pyiceberg/expressions/parser.py @@ -298,6 +298,10 @@ def handle_or(result: ParseResults) -> Or: ], ).set_name("expr") +# pyparsing expands tabs in the input by default, which would rewrite a tab inside a string literal +# into spaces, and the number of spaces would depend on where the literal sits in the expression. +boolean_expression.parse_with_tabs() + def parse(expr: str) -> BooleanExpression: """Parse a boolean expression.""" diff --git a/tests/expressions/test_parser.py b/tests/expressions/test_parser.py index 581ee90b72..88e6b69b0f 100644 --- a/tests/expressions/test_parser.py +++ b/tests/expressions/test_parser.py @@ -294,3 +294,14 @@ def test_boolean_as_operand(expression: str, expected: BooleanExpression) -> Non def test_boolean_as_literal_is_unchanged() -> None: assert parser.parse("foo = true") == EqualTo(Reference("foo"), literal(True)) assert parser.parse("foo in (true, false)") == In(Reference("foo"), {literal(True), literal(False)}) + + +def test_literal_with_tab_is_preserved() -> None: + assert EqualTo("foo", "a\tb") == parser.parse("foo = 'a\tb'") + + +def test_literal_with_tab_does_not_depend_on_its_position() -> None: + one_char_column = parser.parse("a = 'x\ty'") + two_char_column = parser.parse("ab = 'x\ty'") + + assert one_char_column.literal.value == two_char_column.literal.value