-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Core: exclude NaN from upper/lower bound of floating columns in Parquet/ORC #2464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,25 +19,17 @@ | |
|
|
||
| package org.apache.iceberg; | ||
|
|
||
| import java.nio.ByteBuffer; | ||
|
|
||
| /** | ||
| * Iceberg internally tracked field level metrics, used by Parquet and ORC writers only. | ||
| * <p> | ||
| * Parquet/ORC keeps track of most metrics in file statistics, and only NaN counter is actually tracked by writers. | ||
| * This wrapper ensures that metrics not being updated by those writers will not be incorrectly used, by throwing | ||
| * exceptions when they are accessed. | ||
| */ | ||
| public class FloatFieldMetrics extends FieldMetrics { | ||
|
|
||
| /** | ||
| * Constructor for creating a FieldMetrics with only NaN counter. | ||
| * @param id field id being tracked by the writer | ||
| * @param nanValueCount number of NaN values, will only be non-0 for double or float field. | ||
| */ | ||
| public FloatFieldMetrics(int id, | ||
| long nanValueCount) { | ||
| super(id, 0L, 0L, nanValueCount, null, null); | ||
| public class FloatFieldMetrics extends FieldMetrics<Number> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is a public class, I think it would make more sense to split it into an implementation for float and an implementation for double and have each implementation correctly set the type that is tracked. That avoids needing to test the type of upper bound or lower bound because you know that both are going to be the same. That's an assumption that the code below makes, but it isn't necessarily the case.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good! I was hesitant to do that since I think there are too many duplicated code, I guess I was trying too hard to eliminate duplications... |
||
|
|
||
| private FloatFieldMetrics(AbstractFloatFieldMetricsContext<?> context) { | ||
| super(context.id, 0L, 0L, context.nanValueCount, context.lowerBound, context.upperBound); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -50,13 +42,64 @@ public long nullValueCount() { | |
| throw new IllegalStateException("Shouldn't access this method, as this metric is tracked in file statistics. "); | ||
| } | ||
|
|
||
| @Override | ||
| public ByteBuffer lowerBound() { | ||
| throw new IllegalStateException("Shouldn't access this method, as this metric is tracked in file statistics. "); | ||
| public static class FloatFieldMetricsContext extends AbstractFloatFieldMetricsContext<Float> { | ||
| public FloatFieldMetricsContext(int id) { | ||
| super(id); | ||
| } | ||
|
|
||
| @Override | ||
| public void updateMetricsContext(Float value) { | ||
| if (Float.isNaN(value)) { | ||
| this.nanValueCount++; | ||
| } else { | ||
| if (lowerBound == null || Float.compare(value, lowerBound) < 0) { | ||
| this.lowerBound = value; | ||
| } | ||
| if (upperBound == null || Float.compare(value, upperBound) > 0) { | ||
| this.upperBound = value; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public ByteBuffer upperBound() { | ||
| throw new IllegalStateException("Shouldn't access this method, as this metric is tracked in file statistics. "); | ||
| public static class DoubleFieldMetricsContext extends AbstractFloatFieldMetricsContext<Double> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class is suspiciously similar to the builder pattern, so I would probably just convert it to be a builder for
|
||
| public DoubleFieldMetricsContext(int id) { | ||
| super(id); | ||
| } | ||
|
|
||
| @Override | ||
| public void updateMetricsContext(Double value) { | ||
| if (Double.isNaN(value)) { | ||
| this.nanValueCount++; | ||
| } else { | ||
| if (lowerBound == null || Double.compare(value, lowerBound) < 0) { | ||
| this.lowerBound = value; | ||
| } | ||
| if (upperBound == null || Double.compare(value, upperBound) > 0) { | ||
| this.upperBound = value; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @SuppressWarnings("checkstyle:VisibilityModifier") | ||
| public abstract static class AbstractFloatFieldMetricsContext<T extends Number> { | ||
| private final int id; | ||
| protected long nanValueCount = 0; | ||
| protected T lowerBound = null; | ||
| protected T upperBound = null; | ||
|
|
||
| public AbstractFloatFieldMetricsContext(int id) { | ||
| this.id = id; | ||
| } | ||
|
|
||
| /** | ||
| * It is caller's responsibility to ensure input shouldn't be null | ||
| */ | ||
| public abstract void updateMetricsContext(T value); | ||
|
|
||
| public FloatFieldMetrics buildMetrics() { | ||
| return new FloatFieldMetrics(this); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change in this class overlaps with what #1963 contains. For now this class is not directly used/created by Parquet/ORC, and will be for Avro it will, thus should be safe to change. Same goes for
FloatFieldMetrics.