Fix missing Iceberg field-ids in ClickHouse-written manifest and manifest-list files - #111786
Conversation
…nifest-field-ids-111763
The bundled avro-cpp JSON compiler drops the Iceberg field-id/element-id
attributes, so the schema it serialized into the avro.schema header of a
ClickHouse-written manifest / manifest-list omitted them. External readers
(PyIceberg, Spark) reject such a schema during scan planning ("Cannot convert
field, missing field-id"), while ClickHouse itself reads via the Iceberg
schema metadata key and did not notice.
generateManifestFile and generateManifestList now write the original
id-carrying JSON schema string as the avro.schema header. Encoded data is
unchanged; only the header schema now carries the spec field-ids it should.
Also derive the manifest partition-struct field-id from the persisted
partition spec instead of the hardcoded 1000+i: ClickHouse numbers partition
fields from 1001, and Iceberg projects partition values by field-id, so the
previously-invisible mismatch would break external readers once the id is
emitted. Legacy v1 specs that do not track partition field-ids fall back to
the sequential 1000+i default.
Closes: ClickHouse#111763
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-PR validation gate (click to expand)
Session id: cron:clickhouse-author-slot-0:20260724-113800 |
Internal second-model review — adjudication log (click to expand)Pre-publication review by an independent model (engine: codex; bounded: 1 full pass + 1 fix round + 1 delta recheck). Full pass (sha e1708e9): 0 findings. Coverage: all changed files in full, every manifest-writer caller, sibling Avro schemas / avro-cpp writer internals, partition-spec generation, the partitioner, and the test fixtures/helpers. Delta recheck (sha fbb19f2, comment-only tightening): 0 findings. No blockers, majors, or nits raised. Nothing to adjudicate. Session id: cron:clickhouse-author-slot-0:20260724-113800 |
|
cc @scanhex12 — could you review this? It completes the Iceberg field-id story on the metadata side: the avro-cpp JSON compiler drops the |
|
Workflow [PR], commit [fbb19f2] Summary: ❌
AI ReviewSummaryThis PR fixes the missing Iceberg Final VerdictStatus: ✅ Approve |
LLVM Coverage Report
Changed lines: Changed C/C++ lines covered: 27/30 (90.00%) · Uncovered code |
CI finish ledger — fbb19f2Every failure below has an owner. Only
No PR-caused failure: #111786's diff is |
d047827
Closes: #111763
Related: #109994
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Fix Iceberg tables written by ClickHouse being unreadable by external readers (PyIceberg, Spark). The Avro schemas embedded in the manifest-list and manifest files omitted the Iceberg
field-idproperties, so scan planning failed withCannot convert field, missing field-id.Description
The bundled avro-cpp JSON schema compiler discards the Iceberg
field-id/element-idattributes, so the schema it re-serialized into theavro.schemaheader of a written manifest / manifest-list omitted them. ClickHouse reads its own manifests via the Icebergschemametadata key so did not notice, but PyIceberg and Spark read the Avro header schema during scan planning and reject a schema whose fields lackfield-id(ValueError: Cannot convert field, missing field-id) - before ever opening the (correctly written) data file.Fix: in
generateManifestFileandgenerateManifestList, write the original id-carrying JSON schema string (the same one used to build the writer) as theavro.schemaheader, instead of the id-stripped one the avro-cpp compiler produces. This is spec-conformant (Iceberg mandates fixed field-ids likemanifest_path=500,status=0,data_file=2, and the manifest-entry map key/value ids) and matches the encoded record layout exactly; the encoded data bytes are unchanged.This also fixes a latent partition-struct field-id mismatch the change would otherwise expose:
extendSchemaForPartitionshardcoded the manifestpartitionfield-id as1000 + i, but ClickHouse numbers partition-spec fields from1001. Iceberg projects manifest partition values onto the spec by field-id, so once the id becomes visible the mismatch would break external readers. The id is now derived from the persisted partition spec, falling back to the1000 + isequential default only for legacy v1 specs that do not track partition field-ids.Regression test (
test_writes_manifest_field_ids_spark_read) asserts the emitted manifest-list and manifest schemas carry the required field-ids and that Spark can plan and read a ClickHouse-written table. A ClickHouse round-trip cannot catch this (ClickHouse reads via the Icebergschemakey, not the Avro field-ids), so the test is grounded in an external reader.