Skip to content

[default values] Add NestedField column-default APIs and Expressions.lit() (~upstream #9502) - #250

Merged
cbb330 merged 4 commits into
linkedin:openhouse-1.2.0from
shanthoosh:openhouse-1.2.0-nestedfield-defaults
Jun 26, 2026
Merged

[default values] Add NestedField column-default APIs and Expressions.lit() (~upstream #9502)#250
cbb330 merged 4 commits into
linkedin:openhouse-1.2.0from
shanthoosh:openhouse-1.2.0-nestedfield-defaults

Conversation

@shanthoosh

@shanthoosh shanthoosh commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

What

A faithful re-baseline of upstream apache/iceberg:main's column default-value API onto openhouse-1.2.0 — the API (Types.NestedField defaults, Expressions.lit()) plus SchemaParser serialization, so defaults are format-compatible end to end. Adapted only where 1.2 genuinely lacks the feature (v3-only bits are dropped); everything else lands as a copy from main. Not a git cherry-pick.

Types.NestedField

  • Store defaults as Literal<?> (per main / #12211).
  • Builder: withInitialDefault(Literal) / withWriteDefault(Literal) (+ deprecated Object overloads), initialDefault() / writeDefault() value accessors, initialDefaultLiteral() / writeDefaultLiteral(), and main's builder() entry points.
  • castDefault surfaces a clear Cannot cast default value error instead of NPE-ing on an invalid cast.
  • Dropped the v3-only UnknownType check.

SchemaParser

  • Backport main's toJson/fromJson (per #11832) so initial-default / write-default round-trip through the schema JSON.
  • Inert until a producer sets a default — schemas with no defaults serialize exactly as before.

Scope

  • In scope: the column-default API + SchemaParser serialization.
  • Out of scope: UpdateSchema, Avro, and reader wiring (the v3-only bits don't exist in 1.2).
  • v2 on-disk compatibility is preserved at the catalog server, which strips the extra schema attributes before persisting metadata to disk.

Testing

  • core TestSchemaParsermain's canonical testDocStrings + testPrimitiveTypeDefaultValues serialize→parse round-trip (the assertion that proves format compatibility), minus the two v3-only TimestampNano rows. 14 tests, 0 failures.
  • Full :iceberg-api:test and schema-related :iceberg-core tests pass; :iceberg-api:revapi and spotlessCheck pass.

revapi

Keeps the NestedField serialization-id accepted break (still required, adding fields to a Serializable class). No unrelated churn.

🤖 Generated with Claude Code

…ream #9502)

Hand-port the column default-value API from newer Iceberg onto the
openhouse-1.2.0 branch (not a git cherry-pick).

Types.NestedField:
- Add initialDefault and writeDefault fields with initialDefault()/
  writeDefault() accessors.
- Add a NestedField.Builder (optional(name)/required(name)/from(field)
  + withId/ofType/withDoc/withInitialDefault/withWriteDefault/build).
- castDefault() normalizes a supplied default to the field type via
  Expressions.lit(value).to(type).value(), and rejects non-null defaults
  on nested types.
- equals() now compares both defaults; existing factory methods remain
  backward compatible (defaults are null).

Expressions:
- Add lit(value) returning Literals.from(value).

By design, defaults are NOT serialized to metadata.json (SchemaParser is
untouched); the API exists in-memory only as a foundation for the glue
work to follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
shanthoosh and others added 2 commits June 23, 2026 20:15
…ion break

Tests:
- TestTypes: cover NestedField default-value API — builder construction,
  null defaults, initialDefault/writeDefault accessors, default casting to
  the field type (Long -> Integer via Expressions.lit), build() requires id,
  nested-type non-null default rejection, from() copy, asOptional/asRequired
  preserving defaults, and equals() distinguishing defaults.
- TestExpressionHelpers: cover Expressions.lit() and lit().to(type) conversion.

Build fix:
- Accept the java.class.defaultSerializationChanged revapi break for
  Types.NestedField (adding initialDefault/writeDefault fields changes the
  default serialization id only), matching existing precedent in
  .palantir/revapi.yml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into openhouse-1.2.0-nestedfield-defaults

# Conflicts:
#	.palantir/revapi.yml
@shanthoosh shanthoosh changed the title API: Add NestedField column-default APIs and Expressions.lit() (~upstream #9502) Add NestedField column-default APIs and Expressions.lit() (~upstream #9502) Jun 24, 2026
@cbb330

cbb330 commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

thanks for the change!

  • This tracks upstream's #9502 as it merged (Oct 2024), not current apache/iceberg:main. Since then main moved default storage Object → Literal<?> (#12211) and wired defaults through SchemaParser (#11832). Let's re-baseline what this feature needs onto main so it stays a faithful cherry-pick and is format-compatible end to end — e.g. NestedField should take main's Literal<?>.

  • SchemaParser is untouched here. I've shifted the requirements a bit on the client compatibility layer (sorry!) — let's also backport SchemaParser from main so we get the toJson/fromJson that persist initial-default. This is safe: the plan is for the OH server to parse initial-default back OUT of the schema before persisting to disk (that's what preserves v2 compatibility). It simplifies your design and the client, at the cost of some scope on the server — which we're fine with, since server logic is easier to iterate on.

  • Update the PR description to reflect the new v2-compat requirement. Drop the "By design: defaults are NOT serialized — SchemaParser is untouched" line — that's the requirement we're changing. Reframe as: a faithful re-baseline of upstream/main's column-default API + SchemaParser serialization, inert until a producer sets a default, with no UpdateSchema/Avro/reader scope. Later, the catalog server strips the extra schema attributes before persisting to disk.

  • For castDefault, prefer main's shape — it surfaces a clear error on an invalid cast instead of NPE-ing. Minor, and it comes for free with the copy.

  • Tests: lean on main's canonical tests and drop our bespoke ones. Make sure a serialize→parse round-trip is among them — that's the assertion that actually proves format compatibility. Details in [0].

  • revapi: keep the serialization-id accepted-break (still needed), but please revert the unrelated BaseMetadataTable::table() reordering — auto-accept churn, not part of this change.

General idea (which you've mostly followed): adapt only where 1.2 genuinely lacks the feature (the v3-only bits don't exist here); everything else should land as a copy from main.

[0] tests:

TestSchemaParser.testPrimitiveTypeDefaultValues — core/src/test/java/org/apache/iceberg/TestSchemaParser.java:139-157,
with its @MethodSource provider primitiveTypesAndDefaults at :108-137.

Exactly the proof we want — builds a schema with withInitialDefault/withWriteDefault, runs
SchemaParser.fromJson(SchemaParser.toJson(schema)), and asserts both survive:

  Schema serialized = SchemaParser.fromJson(SchemaParser.toJson(schema));
  assertThat(serialized.findField("col_with_default").initialDefault()).isEqualTo(defaultValue.value());
  assertThat(serialized.findField("col_with_default").writeDefault()).isEqualTo(defaultValue.value());

Copy both methods. When backporting to 1.2, drop the two v3-only rows from primitiveTypesAndDefaults — they use
types/APIs that don't exist in 1.2 (same bits you're dropping in the impl):
  - :120-123  Types.TimestampNanoType.withZone()    + Expressions.nanos(...)
  - :127-129  Types.TimestampNanoType.withoutZone() + Expressions.nanos(...)

The remaining 12 rows (boolean, int, long, float, double, date, timestamp tz/no-tz, string, uuid, fixed, binary,
decimal) all exist in 1.2 and cover the type matrix that matters. Imports: org.apache.iceberg.expressions.Literal,
plus already-present DateTimeUtil, ByteBuffer, BigDecimal, UUID.

Also worth grabbing: testDocStrings (:92-106) exercises the new NestedField builder path through SchemaParser —
cheap extra coverage of the builder you're re-baselining, no v3 deps.

Re-baseline the column default-value feature onto upstream apache/iceberg:main
(faithful copy, adapting only where 1.2 lacks the feature) instead of the
narrower #9502 hand-port, and make it format-compatible end to end.

- Types.NestedField: store defaults as Literal<?> (per main / #12211). Add
  withInitialDefault(Literal)/withWriteDefault(Literal) plus the deprecated
  Object overloads, initialDefaultLiteral()/writeDefaultLiteral(), and the
  builder() entry points from main. Drop the v3-only UnknownType check.
  castDefault now surfaces a clear "Cannot cast default value" error instead
  of NPE-ing on an invalid cast.

- SchemaParser: backport main's toJson/fromJson so initial-default and
  write-default round-trip through the schema JSON (per #11832). Inert until
  a producer sets a default. (The catalog server strips these attributes
  before persisting to disk to preserve v2 compatibility.)

- Tests: drop the bespoke api tests; add core TestSchemaParser with main's
  canonical testDocStrings + testPrimitiveTypeDefaultValues serialize->parse
  round-trip. Dropped the two v3-only TimestampNano rows from the provider.

- revapi: keep the NestedField serialization-id accepted break; revert the
  unrelated BaseMetadataTable::table() reordering churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the CORE label Jun 25, 2026
@cbb330

cbb330 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

great work, looks like the needed changes (and no more) are faithfully cherry picked from master.

rollout is safe because no exiting client utilizes initial-default in any capacity, and in a future PR in openhouse catalog (direct consumer of this) we will strip the initial-default for v2 persisted tables.

@cbb330
cbb330 merged commit 40ed6c0 into linkedin:openhouse-1.2.0 Jun 26, 2026
19 checks passed
cbb330 added a commit to linkedin/openhouse that referenced this pull request Jun 26, 2026
…s) (#642)

Picks up
[linkedin/iceberg#250](linkedin/iceberg#250)
(released as `com.linkedin.iceberg` tag `v1.2.0.19`).

## Summary

Bumps the Iceberg 1.2 line from `1.2.0.18` → `1.2.0.19`. The only change
in `v1.2.0.19` is linkedin/iceberg#250, which re-baselines upstream's
**column default-value API** (`Types.NestedField` initial/write
defaults, `Expressions.lit()`, plus `SchemaParser` serialization) onto
`openhouse-1.2.0`, bringing the 1.2 line to parity with the 1.5 line on
this API.

- Defaults are stored as `Literal<?>` with `withInitialDefault` /
`withWriteDefault` builders and matching accessors (`initialDefault()` /
`writeDefault()` / `*Literal()`).
- `SchemaParser` serializes the defaults, so they are format-compatible
end to end.
- v3-only bits (e.g. `UnknownType`) are dropped since 1.2 lacks them;
everything else is a faithful copy from upstream `main` rather than a
cherry-pick.

## Changes

- [ ] Client-facing API Changes
- [ ] Internal API Changes
- [ ] Bug Fixes
- [ ] New Features
- [ ] Performance Improvements
- [ ] Code Style
- [ ] Refactoring
- [ ] Documentation
- [ ] Tests

Dependency version bump only — single line in `build.gradle`. The 1.5
line is already at its latest (`1.5.2.15`); this brings the 1.2 line to
its latest within-line release.

## Testing Done

- [ ] Manually Tested on local docker setup.
- [ ] Added new tests for the changes made.
- [ ] Updated existing tests to reflect the changes made.
- [X] No tests added or updated. Tests for the new APIs live in
linkedin/iceberg#250; this PR is a version bump.
- [ ] Some other form of testing.

# Additional Information

- [ ] Breaking Changes
- [ ] Deprecations
@cbb330 cbb330 changed the title Add NestedField column-default APIs and Expressions.lit() (~upstream #9502) [default values] Add NestedField column-default APIs and Expressions.lit() (~upstream #9502) Jun 29, 2026
cbb330 added a commit that referenced this pull request Jun 29, 2026
…ream #9502) (#251)

Port the column default-value feature from upstream apache/iceberg:main onto
the openhouse-1.5.2 branch (analog of the 1.2.x port in #250).

Types.NestedField:
- Store initial/write defaults as Literal<?> (per main / #12211). Add a
  NestedField.Builder (optional(name)/required(name)/from(field)/builder() +
  withId/ofType/withDoc/withInitialDefault/withWriteDefault/build), the
  deprecated Object overloads, and initialDefault()/writeDefault() plus the
  initialDefaultLiteral()/writeDefaultLiteral() accessors.
- castDefault() normalizes a supplied default to the field type via to(type)
  and rejects non-null defaults on nested types, surfacing a clear
  "Cannot cast default value" error instead of NPE-ing on an invalid cast.
- equals() now compares both defaults; existing factory methods remain
  backward compatible (defaults are null).

Expressions:
- Add lit(value) returning Literals.from(value).

SchemaParser:
- Backport main's toJson/fromJson so initial-default and write-default
  round-trip through the schema JSON (per #11832). Inert until a producer sets
  a default.

Tests:
- core TestSchemaParser with main's canonical testDocStrings +
  testPrimitiveTypeDefaultValues serialize->parse round-trip.

revapi:
- Accept the NestedField defaultSerializationChanged break (adding the
  initial/write default fields changes the default serialization id only).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
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