Conversation
1cfbee7 to
aee375d
Compare
There was a problem hiding this comment.
Hi @manuzhang,
I am not that familiar with the codebase, but your PR was a good read.
Generally I think that erroring in the case of ambiguity is a great change. I wondered if someone may unknowingly built upon this behavior, but as this ambiguity should lead to inconsistent field selection it's unlikely.
I just had concerns over test coverage. Maybe you could also cover that:
- One can still select case insensitive unambiguous field names, even if a schema has case insensitive ambiguous field names.
- One can still build a schema with case insensitive ambiguous field names.
- General coverage for
field_by_name_case_insensitive_checked
Co-authored-by: Codex <codex@openai.com>
aee375d to
9a61a3e
Compare
laskoviymishka
left a comment
There was a problem hiding this comment.
The direction is right — rejecting genuinely ambiguous case-insensitive names is the correct call, and the internal callers (bind, resolve_field_id) are converted cleanly. The tests Stefan asked for look like they've landed too.
What I'd want to settle before merge is the public surface. field_by_name_case_insensitive is a stable pub fn, and this quietly changes its contract: an ambiguous name now returns None, indistinguishable from "field not found" — where before it always handed back some field. The variant that can actually surface the ambiguity, _checked, is pub(crate), so external callers have no way to reach the new behavior. I'd promote _checked to pub and point the old doc comment at it, or at minimum document that None now means "missing or ambiguous."
A couple of smaller things while we're in here:
_checkedhas no doc comment, and it's the right spot to note that our per-name check is intentionally a Rust-only refinement — Java is all-or-nothing (any collision poisons every case-insensitive lookup) and PyIceberg silently keeps the last collider, so cross-engine users can see three different outcomes for the same table.- The sentinel drops the colliding IDs at build time, so the error can only name the lookup key, not what it collided with. If we want a Java-style "id and ID collide" message we'd need the map to hold the colliders — fine either way, but worth deciding now.
None of it is a big lift — sort out the public-surface piece and I think this is good to go.
| self.lowercase_name_to_id | ||
| .get(&field_name.to_lowercase()) | ||
| .and_then(|id| self.field_by_id(*id)) | ||
| .and_then(|id| id.and_then(|id| self.field_by_id(id))) |
There was a problem hiding this comment.
I'd promote _checked to pub and point this doc comment at it, or at minimum document that None now means "missing or ambiguous" — because this is stable public surface (it's in public-api.txt) and the contract just moved silently.
Before, a caller always got some field back for a name with case-variants; now an ambiguous name returns None, identical to "field doesn't exist," and the only variant that can tell the two apart is pub(crate). So external callers both lose the old behavior and can't reach the new one. The doc still just says "in a case-insensitive way," which no longer tells the whole story.
|
Thank you for changing the tests @manuzhang . Looks good 👍 |
Expose the checked lookup, document collision semantics, and cover the public fallback. Simplify the binding and scan callers. Co-authored-by: Codex <codex@openai.com>
Which issue does this PR close?
What changes are included in this PR?
Case-insensitive schema lookup now records lower-cased name collisions instead of silently selecting one field. Scan projection and expression binding propagate a data-invalid error when a requested name is ambiguous, while case-sensitive lookup is unchanged.
This change was extracted from #2773 so the Unknown-type work can be reviewed independently.
Are these changes tested?
cargo test -p iceberg --lib test_case_insensitive_scan_rejects_ambiguous_column_namecargo fmt --all -- --checkcargo clippy -p iceberg --all-targets --all-features -- -D warningsAI Disclosure
This PR was prepared with assistance from Codex.