|
42 | 42 | Reference, |
43 | 43 | StartsWith, |
44 | 44 | ) |
45 | | -from pyiceberg.expressions.literals import DecimalLiteral, LongLiteral, literal |
| 45 | +from pyiceberg.expressions.literals import DecimalLiteral, LongLiteral, StringLiteral, literal |
| 46 | +from pyiceberg.transforms import ( |
| 47 | + DayTransform, |
| 48 | + HourTransform, |
| 49 | + MonthTransform, |
| 50 | + UnboundTransform, |
| 51 | + YearTransform, |
| 52 | +) |
46 | 53 |
|
47 | 54 |
|
48 | 55 | def test_always_true() -> None: |
@@ -272,3 +279,86 @@ def test_valid_between_with_numerics() -> None: |
272 | 279 | ) == parser.parse("foo between '2025-01-01T00:00:00.000000' and '2025-01-10T12:00:00.000000'") |
273 | 280 |
|
274 | 281 | assert parser.parse("foo between 1 and 3") == parser.parse("1 <= foo and foo <= 3") |
| 282 | + |
| 283 | + |
| 284 | +def test_cast_date_comparison() -> None: |
| 285 | + expected = EqualTo( |
| 286 | + UnboundTransform(Reference("created_at"), DayTransform()), |
| 287 | + StringLiteral("2024-01-01"), |
| 288 | + ) |
| 289 | + assert expected == parser.parse("CAST(created_at AS date) = '2024-01-01'") |
| 290 | + |
| 291 | + |
| 292 | +def test_cast_year_comparison() -> None: |
| 293 | + expected = GreaterThan( |
| 294 | + UnboundTransform(Reference("ts"), YearTransform()), |
| 295 | + LongLiteral(2020), |
| 296 | + ) |
| 297 | + assert expected == parser.parse("CAST(ts AS year) > 2020") |
| 298 | + |
| 299 | + |
| 300 | +def test_cast_month_comparison() -> None: |
| 301 | + expected = EqualTo( |
| 302 | + UnboundTransform(Reference("ts"), MonthTransform()), |
| 303 | + LongLiteral(6), |
| 304 | + ) |
| 305 | + assert expected == parser.parse("CAST(ts AS month) = 6") |
| 306 | + |
| 307 | + |
| 308 | +def test_cast_hour_comparison() -> None: |
| 309 | + expected = GreaterThanOrEqual( |
| 310 | + UnboundTransform(Reference("ts"), HourTransform()), |
| 311 | + LongLiteral(12), |
| 312 | + ) |
| 313 | + assert expected == parser.parse("CAST(ts AS hour) >= 12") |
| 314 | + |
| 315 | + |
| 316 | +def test_cast_case_insensitive() -> None: |
| 317 | + """CAST keyword and type name should be case-insensitive.""" |
| 318 | + expected = EqualTo( |
| 319 | + UnboundTransform(Reference("created_at"), DayTransform()), |
| 320 | + StringLiteral("2024-01-01"), |
| 321 | + ) |
| 322 | + assert expected == parser.parse("cast(created_at as DATE) = '2024-01-01'") |
| 323 | + |
| 324 | + |
| 325 | +def test_cast_nested_field() -> None: |
| 326 | + """CAST should work with dotted column references.""" |
| 327 | + expected = EqualTo( |
| 328 | + UnboundTransform(Reference("event.timestamp"), DayTransform()), |
| 329 | + StringLiteral("2024-01-01"), |
| 330 | + ) |
| 331 | + assert expected == parser.parse("CAST(event.timestamp AS date) = '2024-01-01'") |
| 332 | + |
| 333 | + |
| 334 | +def test_cast_unsupported_type() -> None: |
| 335 | + with pytest.raises(ValueError, match="Unsupported CAST target type"): |
| 336 | + parser.parse("CAST(col AS foobar) = 5") |
| 337 | + |
| 338 | + |
| 339 | +def test_cast_not_equal() -> None: |
| 340 | + expected = NotEqualTo( |
| 341 | + UnboundTransform(Reference("ts"), YearTransform()), |
| 342 | + LongLiteral(2020), |
| 343 | + ) |
| 344 | + assert expected == parser.parse("CAST(ts AS year) != 2020") |
| 345 | + |
| 346 | + |
| 347 | +def test_cast_less_than_or_equal() -> None: |
| 348 | + expected = LessThanOrEqual( |
| 349 | + UnboundTransform(Reference("ts"), MonthTransform()), |
| 350 | + LongLiteral(6), |
| 351 | + ) |
| 352 | + assert expected == parser.parse("CAST(ts AS month) <= 6") |
| 353 | + |
| 354 | + |
| 355 | +def test_cast_with_and() -> None: |
| 356 | + """CAST predicates compose with AND/OR via infix_notation.""" |
| 357 | + result = parser.parse("CAST(ts AS date) = '2024-01-01' and status = 'active'") |
| 358 | + assert isinstance(result, And) |
| 359 | + |
| 360 | + |
| 361 | +def test_cast_with_not() -> None: |
| 362 | + """NOT should negate a CAST predicate.""" |
| 363 | + result = parser.parse("not CAST(ts AS date) = '2024-01-01'") |
| 364 | + assert isinstance(result, Not) |
0 commit comments