|
32 | 32 | And, |
33 | 33 | EqualTo, |
34 | 34 | In, |
| 35 | + LessThan, |
| 36 | + Or, |
35 | 37 | ) |
36 | 38 | from pyiceberg.expressions.visitors import bind |
37 | 39 | from pyiceberg.io import PY_IO_IMPL, FileIO, load_file_io |
@@ -356,6 +358,44 @@ def test_data_scan_plan_files_no_current_snapshot(example_table_metadata_no_snap |
356 | 358 | assert len(scan.to_arrow()) == 0 |
357 | 359 |
|
358 | 360 |
|
| 361 | +def test_data_scan_count_with_less_than_on_null_identity_partition(catalog: Catalog) -> None: |
| 362 | + import pyarrow as pa |
| 363 | + |
| 364 | + catalog.create_namespace("default") |
| 365 | + schema = Schema( |
| 366 | + NestedField(1, "x", IntegerType(), required=False), |
| 367 | + NestedField(2, "y", IntegerType(), required=False), |
| 368 | + ) |
| 369 | + spec = PartitionSpec(PartitionField(1, 1000, IdentityTransform(), "x")) |
| 370 | + table = catalog.create_table("default.null_identity_partition", schema=schema, partition_spec=spec) |
| 371 | + table.append( |
| 372 | + pa.table( |
| 373 | + { |
| 374 | + "x": pa.array([None, None], type=pa.int32()), |
| 375 | + "y": pa.array([0, 2], type=pa.int32()), |
| 376 | + } |
| 377 | + ) |
| 378 | + ) |
| 379 | + |
| 380 | + # To exercise the residual evaluator code path, include y == 2 so partition pruning keeps the file. |
| 381 | + # |
| 382 | + # Partition pruning: |
| 383 | + # x < 1 -> false for the null x partition |
| 384 | + # y == 2 -> unknown because y is not partitioned |
| 385 | + # false OR unknown -> keep the file |
| 386 | + # |
| 387 | + # Residual evaluation: |
| 388 | + # x < 1 -> false for the null x partition |
| 389 | + # y == 2 -> retained because no partition value is available for y |
| 390 | + # false OR y == 2 -> residual is y == 2 |
| 391 | + scan = table.scan(row_filter=Or(LessThan("x", 1), EqualTo("y", 2))) |
| 392 | + tasks = list(scan.plan_files()) |
| 393 | + |
| 394 | + assert len(tasks) == 1 |
| 395 | + assert tasks[0].residual == EqualTo("y", 2) |
| 396 | + assert scan.count() == 1 # Only the y == 2 row matches. |
| 397 | + |
| 398 | + |
359 | 399 | def test_incremental_append_scan_default(table_v2: Table) -> None: |
360 | 400 | scan = table_v2.incremental_append_scan() |
361 | 401 | assert scan.row_filter == AlwaysTrue() |
|
0 commit comments