Skip to content

Core: Update ExpressionParser to match new expressions spec - #17343

Draft
singhpk234 wants to merge 2 commits into
apache:mainfrom
singhpk234:feature/expression-parser-update
Draft

Core: Update ExpressionParser to match new expressions spec#17343
singhpk234 wants to merge 2 commits into
apache:mainfrom
singhpk234:feature/expression-parser-update

Conversation

@singhpk234

Copy link
Copy Markdown
Contributor

PR Description

Summary

Updates the Java ExpressionParser to serialize predicates in the new
form defined in format/expressions-spec.md,
while continuing to accept the deprecated term-based form for backward
compatibility.

This is the Java-side counterpart to #17138, which updates the REST
OpenAPI spec to the new Predicate / ValueExpression model.

Changes

New model classes (api module)

  • FunctionReference — value class for function references, supporting
    the four spec forms: bare name, name array, {identifier}, and
    {catalog, identifier}.
  • UnboundApply<T> — implements UnboundTerm<T> to represent general
    function applications. bind() delegates to UnboundTransform for
    known Iceberg transforms (identity, year, month, day, hour, bucket,
    truncate, void); other functions currently throw at bind time.

Parser changes (core module)

  • Writer now emits the new form only:
    • Predicates use child / left / right instead of term / value
    • References serialize as {"type": "reference", "name": ...} for
      unbound and {"type": "reference", "id": N} for bound
    • Transforms serialize as apply with the
      arguments (e.g. bucket[100](id){"type": "apply", "function": "bucket", "arguments": [100, {"type": "reference", "name": "id"}]})
  • Reader detects the format from the payload and accepts both:
    • If a predicate has a term field, the deprecated path parses it
      (existing behavior)
    • Otherwise the new path parses child / left / right,
      references (name or deprecated term), and apply expressions
    • Known Iceberg transforms parsed from apply are converted back to
      UnboundTransform so downstream binding paths are unchanged

Tests

  • Updated existing round-trip and expected-JSON assertions in
    TestExpressionParser, TestPlanTableScanRequestParser,
    TestFileScanTaskParser, TestPlanTableScanResponseParser,
    TestFetchPlanningResultResponseParser,
    TestFetchScanTasksResponseParser,
    TestAllManifestsTableTaskParser, and TestFilesTableTaskParser to
    the new writer output.
  • Added backward-compatibility reader tests for each deprecated
    predicate form (testReadDeprecatedUnaryPredicate,
    testReadDeprecatedComparisonPredicate,
    testReadDeprecatedSetPredicate,
    testReadDeprecatedTransformPredicate,
    testReadDeprecatedObjectReference).
  • Added new-format tests: testNewFormatRoundTrip,
    testApplySimpleFunctionName, testApplyWithCatalogFunction,
    testNewFormatNamedReference, testNewFormatTypedLiteral.

Test plan

  • ./gradlew :iceberg-api:test
  • ./gradlew :iceberg-core:test --tests "org.apache.iceberg.expressions.*"
  • ./gradlew :iceberg-core:test --tests "org.apache.iceberg.rest.*"
  • ./gradlew :iceberg-core:test --tests "org.apache.iceberg.metrics.*"
  • ./gradlew :iceberg-core:test --tests "org.apache.iceberg.TestFileScanTaskParser"
  • ./gradlew spotlessApply

@singhpk234 singhpk234 changed the title Core: Update ExpressionParser to match new expressions spec Core: Update ExpressionParser to match new expressions spec Jul 23, 2026
@sfc-gh-prsingh
sfc-gh-prsingh force-pushed the feature/expression-parser-update branch 2 times, most recently from 2f2a6ab to e681062 Compare July 24, 2026 00:06
Adds UnboundApply and FunctionReference to represent the APPLY value
expression from the Iceberg expressions spec, along with factory methods
on Expressions.

A function reference identifies a function by a multi-part identifier and
an optional catalog, as defined by the spec. Known Iceberg partition
transforms are recognized by name and can be converted to UnboundTransform
so that existing transform handling continues to work. Per Appendix A,
partition transforms are functions other than void, so void is not
recognized. Transform names are resolved through Transforms rather than a
separate list of names.

Construction goes through Expressions.function and Expressions.apply,
following the pattern used for UnboundPredicate, UnboundTransform, and
NamedReference: a public type with a package-private constructor.

Argument and identifier lists are stored in mutable lists so that Kryo can
deserialize them, matching UnboundPredicate.
Updates ExpressionParser to read and write the predicate and value
expression forms defined in Appendix B of format/expressions-spec.md,
while continuing to parse the deprecated term-based forms sent by older
clients.

Predicates are written with 'child' for unary and set operations and
'left'/'right' for comparisons. References are written as objects, and
transforms are written as apply expressions with the transform parameter
as the first argument.

Details worth noting:

- Bound references are written and read as field IDs, which are resolved
  through the schema. Parsing an ID reference without a schema fails
  rather than guessing a name.
- The 'data-type' on literal and literals objects is used to convert
  values, so typed literals do not decay to strings.
- In value expression position a bare string is a literal, not a
  reference. Bare strings remain references in the deprecated term
  position. A literal cannot be a predicate operand, so bare strings in
  'left' and 'child' are rejected.
- IN and NOT_IN always write 'values', including when the list is empty.
- Writing a literal value of an unsupported type fails instead of
  emitting a field name with no value.
@sfc-gh-prsingh
sfc-gh-prsingh force-pushed the feature/expression-parser-update branch from e681062 to 134e03b Compare July 28, 2026 17:54
@github-actions github-actions Bot removed the OPENAPI label Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants