[core] Add field-id.one-based option for strictly positive Iceberg field ids - #9347
[core] Add field-id.one-based option for strictly positive Iceberg field ids#9347vbabenkoru wants to merge 2 commits into
Conversation
…eld ids Snowflake's external Iceberg reader rejects Paimon-generated Iceberg metadata with 'external field ID in metadata file cannot be zero': Paimon assigns table field ids from 0, and those ids flow verbatim into both the Iceberg schema and the Parquet footers. A metadata-only remap is not safe, because Iceberg readers resolve embedded Parquet field ids before name mapping, so metadata and physical ids must move together. Add an immutable, create-time-only boolean table option 'field-id.one-based' (default false, unchanged behavior). When enabled, all field ids of a NEW table - top-level and nested - are shifted by one at creation, so schema files, Parquet/ORC footers, Iceberg metadata, partition source ids and stats keys all agree on the same strictly positive id space. Schema evolution continues from the persisted highestFieldId, so later columns keep 1-based ids with no further changes. Existing tables cannot be migrated with this option: their data files already embed 0-based ids, so they must be rewritten into a new table created with field-id.one-based=true. Tests: default stays 0-based; shifted creation and evolution; immutability; end-to-end strict-mode primary-key + DV table on format-version 3 verifying 1-based ids in Iceberg metadata, identical ids in Parquet footers (top-level and nested), partition source-id, non-null manifest-list first_row_id, Apache Iceberg 1.11 read with the DV applied, and Paimon read-back.
c77a48f to
ea3560c
Compare
|
Although the option claims to be |
The general ALTER validation enforces immutable options only once the table has a snapshot, so between creation and the first write the value could be toggled: field ids are assigned once at creation and were correctly left untouched, but the stored option then permanently contradicted the actual id base. The value is now rejected exactly like 'type', regardless of snapshots; restating the effective value (e.g. an explicit default) stays allowed, and removing the option is rejected when the table was created one-based. Reported by JingsongLi in review.
|
Thank you, fixed. |
Purpose
Paimon starts field ids at 0. The Iceberg ecosystem starts them at 1. Because of this, Iceberg metadata for a Paimon table contains a field with id 0. Some external Iceberg readers reject these tables. For example, Snowflake refuses them. This also requires workarounds elsewhere. When a partition column has field id 0, the REST committer creates a dummy schema or falls back to partition evolution.
This PR adds an opt-in table option,
field-id.one-based. It shifts every field id by one when the table is created. This includes top-level and nested fields. The table's id space then matches what Iceberg-native readers expect from the start.Design points:
@Immutable. It is applied once inSchemaManager.createTable. Existing tables keep their ids.ShiftFieldIdvisitor moves the whole id space by a fixed offset. It keeps the relative order and any gaps. This differs fromReassignFieldId, which renumbers ids without gaps. Schema evolution then continues from the shifted highest field id.Tests
FieldIdOneBasedTest(paimon-core): checks that the default stays zero-based, nested ids are shifted, schema evolution continues from the shifted ids, and the option is create-time-only and immutable.IcebergFieldIdOneBasedCompatibilityTest(paimon-iceberg): runs end-to-end on a primary-key DV table with Iceberg format version 3. It checks that the Paimon schema, Iceberg metadata, and Parquet footers all use the same strictly positive ids. It verifies them with the Iceberg reader. It also includes a control test showing that the default remains zero-based.API and Format
New optional table option
field-id.one-based. Its default isfalse, and it is immutable. Existing tables and the default id assignment do not change.Documentation
The option is documented in its description, which is included in the generated configuration docs.
AI notice: The code is generated using Fable 5 (with reviews from Codex) but has been verified to run on a real cluster with Flink, Paimon, Iceberg, StarRocks and Snowflake.