API: Add Schema.isOptional() - #17513
Conversation
859280a to
cb45906
Compare
pvary
left a comment
There was a problem hiding this comment.
Left some minor suggestions for the comments, but otherwise LGTM
|
|
||
| return field == null | ||
| || field.isOptional() | ||
| || ancestorFields(schema, fieldId).stream().anyMatch(Types.NestedField::isOptional); |
There was a problem hiding this comment.
ancestorFields calls TypeUtil.indexParents for each invocation and ends up traverse the schema tree. I am wondering if worth caching the indexParents in Schema. Currently it's only used for identifier field in constructor and discarded right after
There was a problem hiding this comment.
Not caching is fine if we are doing it once per predicate. But if this is headed to the hot path in stats evaluation (which is O(files)), it is worth caching it.
There was a problem hiding this comment.
we can defer caching to a separate PR. @dramaticlly would you be interested in adding that?
There was a problem hiding this comment.
I'm fine with deferring but I do agree with @anoopj and @dramaticlly that caching in this case is important especially if we're invoking this per entry. And I think this is actually another argument for maybe why this API should actually be on Schema itself since that idToParent state can be lazily kept there.
There was a problem hiding this comment.
given that multiple folks brought this up, I went ahead and moved this to Schema.isNullable() where we can cache the idToParent map
cb45906 to
e56dd25
Compare
|
|
||
| return field == null | ||
| || field.isOptional() | ||
| || ancestorFields(schema, fieldId).stream().anyMatch(Types.NestedField::isOptional); |
There was a problem hiding this comment.
I'm fine with deferring but I do agree with @anoopj and @dramaticlly that caching in this case is important especially if we're invoking this per entry. And I think this is actually another argument for maybe why this API should actually be on Schema itself since that idToParent state can be lazily kept there.
Determining whether a field may contain nulls requires checking the field itself as well as every field that contains it. Expose this as a reusable utility so callers don't have to reimplement the ancestor walk.
e56dd25 to
afd57e4
Compare
afd57e4 to
b0b7a9a
Compare
singhpk234
left a comment
There was a problem hiding this comment.
LGTM thanks @nastra !
| if (idToParent == null) { | ||
| this.idToParent = TypeUtil.indexParents(struct); | ||
| } | ||
| return idToParent; |
There was a problem hiding this comment.
i wonder if we should synchronize this if this now part of a public which can be called concurrently ? wdyt
There was a problem hiding this comment.
I was mainly keeping it in-sync with all the other methods. I think it we want to synchronize, then we should probably do this across the board?
There was a problem hiding this comment.
Agree, we can do this in follow-up too / later, this looks great to me as is !
| assertThat(schema.isOptional(5)).isFalse(); | ||
| assertThat(schema.isOptional(6)).isFalse(); | ||
|
|
||
| // an optional field is nullable regardless of the fields that contain it |
There was a problem hiding this comment.
nit: I think we renamed from isNullable to isOptional but some of the UT comment is lagging behind. Might consider rename as well, or better to inline using as(...)
| * @return true if the field may be null, false if it cannot be null | ||
| * @throws IllegalArgumentException if the field is not present in this schema | ||
| */ | ||
| public boolean isOptional(int id) { |
There was a problem hiding this comment.
I do not think this method should not be part of the public API. I pointed out a very similar problem on the other version of this method, here: https://github.com/apache/iceberg/pull/17413/changes#r3762320465
Whether a field is optional is clearly defined: there's a boolean flag that determines whether the field's value can be null. This method misuses the term "optional" to mean whether the field or any parent may be null. That difference is not clear and makes the term "optional" confusing. I also don't think that this gets any better by introducing a different "nullable" term.
My point on the other PR wasn't to move this method to the public API, it was that we should rename the utility method to be clear. To do that, I think we should switch to using required. If the field and all parents are required ("fully required" or "always present"?) that is the opposite of this method. Using a name like alwaysPresent combined with isRequired for the leaf makes the most sense to me, but we can come up with better ideas.
rdblue
left a comment
There was a problem hiding this comment.
I think that this PR should be closed in favor of a utility method in #17413.
This method does not need to be part of the public API and including it in this form makes the API harder to use because it is more confusing.
In addition, I don't think Schema is a good place for this. The calling code in #17413 converts a struct to a Schema every time because this uses an id to parent map. I think it's a bad practice to convert to a schema just to call a util method for a struct and would like to see a better implementation that can reuse an id to parent map.
Let's close this and continue the work on the other PR.
Determining whether a field may contain nulls requires checking the field itself as well as every field that contains it. Expose this as a reusable utility so callers don't have to reimplement the ancestor walk.
I've pulled this out of c59ed0d and the plan is to use this functionality later in the stats evaluation