You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@iceberg.apache.org by "GayathriSrividya (via GitHub)" <gi...@apache.org> on 2026/06/18 12:13:17 UTC
[PR] fix: correct NOT STARTS WITH projection for truncated partitions [iceberg-python]
GayathriSrividya opened a new pull request, #3528:
URL: https://github.com/apache/iceberg-python/pull/3528
closes #3493
## Summary
Fixes incorrect projection of `NOT STARTS WITH` predicates for truncated string/binary partition fields. The current implementation unsafely truncates the filter literal without checking its length relative to the truncate width.
## Root Cause
The `TruncateTransform.project` method calls `_truncate_array` which blindly truncates the literal for both `STARTS WITH` and `NOT STARTS WITH` predicates:
```python
elif isinstance(pred, BoundNotStartsWith):
return NotStartsWith(Reference(name), _transform_literal(transform, boundary))
```
For `NOT STARTS WITH "hello"` with `truncate[2]`, this produces:
- Current (unsafe): `NOT STARTS WITH "he"`
- Problem: The truncated partition contains all values starting with "he" (from "hello", "heat", "hear", etc.), so we cannot safely exclude all non-"hello" rows
## Solution
Add special handling for `BoundNotStartsWith` in the `project` method following the Java/Go reference behavior:
- **prefix_length < truncate_width**: Keep original `NOT STARTS WITH` literal (safe)
- **prefix_length == truncate_width**: Project to `!=` instead (safe equality check)
- **prefix_length > truncate_width**: Return `None` (no inclusive projection possible)
### pyiceberg/transforms.py
- Add explicit `NOT STARTS WITH` handling before calling `_truncate_array`
- Check literal length vs truncate width and apply correct projection rules
### tests/test_transforms.py
- Update `test_projection_truncate_string_not_starts_with` to expect `None` (prefix_length > width is unsafe)
- Add `test_projection_truncate_string_not_starts_with_shorter_literal` (prefix_length == width → `!=`)
- Add `test_projection_truncate_string_not_starts_with_original_literal` (prefix_length < width → original)
## Validation
- `make lint` ✓ (all pre-commit hooks pass)
- `pytest tests/test_transforms.py` → 280 passed ✓
- All 13 string truncate projection tests pass
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org
Re: [PR] fix: correct NOT STARTS WITH projection for truncated partitions [iceberg-python]
Posted by "kevinjqliu (via GitHub)" <gi...@apache.org>.
kevinjqliu commented on PR #3528:
URL: https://github.com/apache/iceberg-python/pull/3528#issuecomment-4763089433
Thanks for the contribution @GayathriSrividya
Just FYI there was already another PR opened before this one, #3519. Would be great to coordinate on the original issue next time and not duplicate work. I merged this one because it has a more complete solution than the other PR.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org
Re: [PR] fix: correct NOT STARTS WITH projection for truncated partitions [iceberg-python]
Posted by "kevinjqliu (via GitHub)" <gi...@apache.org>.
kevinjqliu merged PR #3528:
URL: https://github.com/apache/iceberg-python/pull/3528
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org
Re: [PR] fix: correct NOT STARTS WITH projection for truncated partitions [iceberg-python]
Posted by "kevinjqliu (via GitHub)" <gi...@apache.org>.
kevinjqliu commented on code in PR #3528:
URL: https://github.com/apache/iceberg-python/pull/3528#discussion_r3448915556
##########
tests/test_transforms.py:
##########
@@ -1026,9 +1026,22 @@ def test_projection_truncate_string_starts_with(bound_reference_str: BoundRefere
def test_projection_truncate_string_not_starts_with(bound_reference_str: BoundReference) -> None:
+ # literal_width (5) > truncate width (2): no inclusive projection possible (unsafe)
Review Comment:
```suggestion
def test_projection_truncate_string_not_starts_with_longer_literal(bound_reference_str: BoundReference) -> None:
# Not a valid projection: return None because pruning on "he" could drop qualifying rows like "help".
```
##########
tests/test_transforms.py:
##########
@@ -1026,9 +1026,22 @@ def test_projection_truncate_string_starts_with(bound_reference_str: BoundRefere
def test_projection_truncate_string_not_starts_with(bound_reference_str: BoundReference) -> None:
+ # literal_width (5) > truncate width (2): no inclusive projection possible (unsafe)
+ assert TruncateTransform(2).project("name", BoundNotStartsWith(term=bound_reference_str, literal=literal("hello"))) is None
+
+
+def test_projection_truncate_string_not_starts_with_shorter_literal(bound_reference_str: BoundReference) -> None:
+ # literal_width (2) == truncate width (2): project to !=
Review Comment:
```suggestion
def test_projection_truncate_string_not_starts_with_equal_width_literal(bound_reference_str: BoundReference) -> None:
# Valid projection: improve NOT STARTS WITH "he" to partition != "he".
```
##########
tests/test_transforms.py:
##########
@@ -1026,9 +1026,22 @@ def test_projection_truncate_string_starts_with(bound_reference_str: BoundRefere
def test_projection_truncate_string_not_starts_with(bound_reference_str: BoundReference) -> None:
+ # literal_width (5) > truncate width (2): no inclusive projection possible (unsafe)
+ assert TruncateTransform(2).project("name", BoundNotStartsWith(term=bound_reference_str, literal=literal("hello"))) is None
+
+
+def test_projection_truncate_string_not_starts_with_shorter_literal(bound_reference_str: BoundReference) -> None:
+ # literal_width (2) == truncate width (2): project to !=
+ assert TruncateTransform(2).project(
+ "name", BoundNotStartsWith(term=bound_reference_str, literal=literal("he"))
+ ) == NotEqualTo(term="name", literal=literal("he"))
+
+
+def test_projection_truncate_string_not_starts_with_original_literal(bound_reference_str: BoundReference) -> None:
+ # literal_width (1) < truncate width (2): keep original literal
Review Comment:
```suggestion
def test_projection_truncate_string_not_starts_with_shorter_literal(bound_reference_str: BoundReference) -> None:
# Valid projection: pass the NOT STARTS WITH literal "h" through unchanged.
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org