fix(table): validate write batch schema at the core entry#602
Open
liwuhen wants to merge 4 commits into
Open
Conversation
QuakeWang
reviewed
Jul 24, 2026
| let actual_schema = batch.schema(); | ||
| let table_field_count = expected_schema.fields().len(); | ||
| let actual_field_count = actual_schema.fields().len(); | ||
| let allows_value_kind = self.changelog_producer == ChangelogProducer::Input; |
Member
There was a problem hiding this comment.
changelog-producer=input is also permitted on cross-partition tables, so this condition accepts a caller-provided _VALUE_KIND in that mode. When a row moves partitions, the routing path appends a second _VALUE_KIND for the generated delete, causing a schema/column-count error in the KV writer. The new entry validation therefore still accepts a batch that fails downstream.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
TableWrite::write_arrow_batchcurrently accepts an ArrowRecordBatchwithout validating that its schema matches the table's write contract.
Malformed batches can therefore reach downstream writers. In particular, the
dedicated BLOB and VECTOR writer assumes that input columns match the table
schema and indexes them directly. A batch with missing fields can panic instead
of returning a normal Paimon error.
The empty-batch fast path also returns successfully before any downstream
component can notice an invalid schema.
This change validates the batch schema at the public table-write entry before
the empty-batch fast path, row-kind enrichment, partition routing, or writer
dispatch.
Validation is performed in the following order:
The accepted schemas are:
changelog-producer=inputaccepts either the table fields or the tablefields followed by
_VALUE_KIND: Int8.rowkind.fieldwrites continue to accept only the tablefields. Their internal
_VALUE_KINDgeneration remains unchanged.Every schema mismatch returns
Error::DataInvalidwith expected and actualcontext for the first mismatch. No casting, field reordering, column insertion,
or schema coercion is performed.
Brief change log
TableWriteusing theexisting
build_target_arrow_schemaconversion.TableWrite::write_arrow_batch.types.
_VALUE_KIND: Int8only forchangelog-producer=input._VALUE_KINDon normal writes._VALUE_KINDon input changelog writes._VALUE_KINDin cross-partition androwkind.fieldmodes.
rowkind.fieldwrites.
Tests
test_write_arrow_batch_rejects_missing_blob_field(
table_write.rs): verifies that a BLOB batch with a missing field returnsDataInvalidinstead of reaching the dedicated writer and panicking.Fails on the pre-fix code.
test_write_arrow_batch_rejects_missing_vector_field(
table_write.rs): verifies the same behavior for a dedicated VECTORwriter. Fails on the pre-fix code.
test_write_arrow_batch_rejects_mismatched_table_fields(
table_write.rs): covers field reordering, incorrect names, incorrectArrow types, and unexpected columns.
test_write_arrow_batch_rejects_invalid_empty_batch_schema(
table_write.rs): verifies that schema validation occurs before the emptybatch fast path. Fails on the pre-fix code.
test_normal_write_rejects_appended_value_kind(
table_write.rs): verifies that normal writes reject a caller-provided_VALUE_KIND.test_input_changelog_rejects_misplaced_or_wrong_type_value_kind(
table_write.rs): verifies that input changelog writes accept_VALUE_KINDonly as the finalInt8field.test_rowkind_field_accepts_table_schema_and_rejects_caller_value_kind(
table_write.rs): verifies thatrowkind.fieldretains its existingcaller schema contract and internal value-kind generation.
test_cross_partition_write_rejects_caller_value_kind(
table_write.rs): verifies that cross-partition writes continue to acceptonly table fields.
cross-partition writes.
cargo test -p paimon --lib table_writecargo test -p paimon --libcargo fmt --all --checkcargo check -p paimon --all-targetscargo clippy -p paimon --all-targets -- -D warningsgit diff --checkAPI and Format
No public API or persisted data format changes.
The change only rejects Arrow batch schemas that do not satisfy the existing
table-write contract. It does not cast, reorder, pad, or coerce input columns.
Top-level Arrow field nullability and field metadata are not added to the
validation contract. Existing schema evolution behavior is unchanged.
Changelog, cross-partition, and
rowkind.fieldsemantics remain unchanged.The dedicated BLOB and VECTOR formats and their downstream writing logic are
not modified.
Documentation
No documentation changes required.