fix: validate primary key nullability on every path that can change it - #8101
Open
xuanyu-z wants to merge 1 commit into
Open
fix: validate primary key nullability on every path that can change it#8101xuanyu-z wants to merge 1 commit into
xuanyu-z wants to merge 1 commit into
Conversation
xuanyu-z
force-pushed
the
fix/validate-primary-key-nullability
branch
2 times, most recently
from
July 30, 2026 17:04
4e6d380 to
c7b4611
Compare
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
`Schema::verify_primary_key` rejects a primary key column that permits nulls, but its only caller was `TryFrom<&ArrowSchema> for Schema` — table creation and nothing else. It matters because a multi-column key match treats a null as unequal to everything, including another null. A row holding a null in its key never matches its own earlier copy, so `merge_insert` inserts a second row rather than overwriting, and every repeat write adds another. MemWAL compaction folds flushed data in by matching on the key, so such a table duplicates rows silently. The invariant is enforced at the boundary where a schema becomes durable: `write_manifest_file`, which every manifest write funnels through. That covers restore and clone, which rebuild a manifest from a stored one and so never pass through the Arrow-schema conversion, and it covers the public transaction API, where `Operation::Project`, `Merge` and `Overwrite` carry a schema that `validate_operation` checks only against fragments. Three API-level checks report the same violation earlier, before work is done: installing the key through `UpdateConfig` field metadata, altering a column's nullability, and initializing MemWAL on a table that already carries a bad key. Four tests in `dataset/metadata.rs` installed a key on a `gen_batch` column, which is nullable. Their subject is metadata translation and key immutability, so they now build from an explicit non-nullable schema. Clustering-key tests are untouched, since `verify_primary_key` does not govern them.
xuanyu-z
force-pushed
the
fix/validate-primary-key-nullability
branch
from
July 30, 2026 18:56
c7b4611 to
2beeaa5
Compare
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.
Schema::verify_primary_keyrejects a primary key column that permits nulls, but its only caller isTryFrom<&ArrowSchema> for Schema— table creation and nothing else.It matters because a multi-column key match treats a null as unequal to everything, including another null. A row holding a null in its key never matches its own earlier copy, so
merge_insertinserts a second row rather than overwriting, and every repeat write adds another. MemWAL compaction folds flushed data in by matching on the key, so such a table duplicates rows silently.Enforced where a schema becomes durable
The check goes in
write_manifest_file, which every manifest write funnels through. Four kinds of path reach a manifest without the Arrow-schema conversion:UpdateConfigfield metadataColumnAlteration::set_nullable(true)Operation::Clonecarries no schema at allOperation::Project/Merge/Overwritevalidate_operationchecks the schema only against fragmentsEnumerating them per operation would leave the next schema-carrying operation uncovered. One check at the manifest boundary does not.
Early errors
Three API-level checks report the same violation before work is done — installing the key through field metadata, altering nullability, and initializing MemWAL on a table already carrying a bad key.
InitializeMemWalBuilder::executeis also the gate for callers outside this crate, which is why it is checked there rather than by each caller.Tests
Four, each verified to fail with its own check removed:
write_manifest_file_rejects_a_nullable_primary_key— forges a manifest, since a stored one can no longer be produced. Restore and clone are covered by routing through this call.test_unenforced_primary_key_rejects_a_nullable_columntest_alter_columns_cannot_make_a_primary_key_nullable— also alters a non-key column, so the rejection is specific to the keytest_initialize_mem_wal_rejects_a_nullable_primary_keyFour tests in
dataset/metadata.rsinstalled a key on agen_batchcolumn, which is nullable. Their subject is metadata translation and key immutability, so they now build from an explicit non-nullable schema. Clustering-key tests are untouched —verify_primary_keydoes not govern them.Full
lance --libsuite: 2630 pass.Not addressed here
Casting a primary key column rebuilds the field from a freshly constructed Arrow field carrying no metadata, which drops the key marker. Unchanged by this;
verify_primary_keythen passes because the schema has no key.Related
#6324 — a sibling symptom of the same gap, about protobuf population on the metadata path rather than validation.