-
Notifications
You must be signed in to change notification settings - Fork 138
IGNITE-27739 Fix decimal precision validation #7639
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
base: main
Are you sure you want to change the base?
Changes from all commits
5791696
c9facc1
084af6c
942ba44
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,11 +19,14 @@ | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import static org.apache.ignite.internal.lang.IgniteStringFormatter.format; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import java.math.BigDecimal; | ||||||||||||||||||||||||||
| import java.math.RoundingMode; | ||||||||||||||||||||||||||
| import java.time.Instant; | ||||||||||||||||||||||||||
| import java.time.LocalDate; | ||||||||||||||||||||||||||
| import java.time.LocalDateTime; | ||||||||||||||||||||||||||
| import org.apache.ignite.internal.tostring.IgniteToStringExclude; | ||||||||||||||||||||||||||
| import org.apache.ignite.internal.tostring.S; | ||||||||||||||||||||||||||
| import org.apache.ignite.internal.type.DecimalNativeType; | ||||||||||||||||||||||||||
| import org.apache.ignite.internal.type.NativeType; | ||||||||||||||||||||||||||
| import org.apache.ignite.internal.type.NativeTypes; | ||||||||||||||||||||||||||
| import org.apache.ignite.internal.type.VarlenNativeType; | ||||||||||||||||||||||||||
|
|
@@ -236,6 +239,8 @@ public void validate(@Nullable Object val) { | |||||||||||||||||||||||||
| checkBounds((LocalDateTime) val, SchemaUtils.DATETIME_MIN, SchemaUtils.DATETIME_MAX); | ||||||||||||||||||||||||||
| } else if (type.spec() == ColumnType.TIMESTAMP) { | ||||||||||||||||||||||||||
| checkBounds((Instant) val, SchemaUtils.TIMESTAMP_MIN, SchemaUtils.TIMESTAMP_MAX); | ||||||||||||||||||||||||||
| } else if (type.spec() == ColumnType.DECIMAL) { | ||||||||||||||||||||||||||
| checkPrecision((BigDecimal) val); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
@@ -246,6 +251,14 @@ private <T extends Comparable<T>> void checkBounds(T value, T min, T max) { | |||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private void checkPrecision(BigDecimal val) throws SchemaMismatchException { | ||||||||||||||||||||||||||
| DecimalNativeType dnt = (DecimalNativeType) type; | ||||||||||||||||||||||||||
|
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. Can this cast fail in some 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. I don't think so, we have: ignite-3/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java Lines 219 to 221 in 5791696
Moreover, the other bound validations are also casting explicitly: ignite-3/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java Lines 236 to 244 in 5791696
|
||||||||||||||||||||||||||
| BigDecimal scaled = val.setScale(dnt.scale(), RoundingMode.HALF_UP); | ||||||||||||||||||||||||||
| if (scaled.precision() > dnt.precision()) { | ||||||||||||||||||||||||||
| throw new SchemaMismatchException(format("Numeric field overflow in column '{}'", name)); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Creates copy of the column with assigned positions. | ||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||
|
|
@@ -281,14 +294,4 @@ public String toString() { | |||||||||||||||||||||||||
| public static String nullConstraintViolationMessage(String columnName) { | ||||||||||||||||||||||||||
| return format("Column '{}' does not allow NULLs", columnName); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Returns an error message for numeric field overflow error. | ||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||
| * @param columnName Column name. | ||||||||||||||||||||||||||
| * @return Error message. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| public static String numericFieldOverflow(String columnName) { | ||||||||||||||||||||||||||
| return format("Numeric field overflow in column '{}'", columnName); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,7 +18,6 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package org.apache.ignite.internal.schema.row; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.math.BigDecimal; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.math.RoundingMode; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.Instant; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDateTime; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -361,11 +360,6 @@ public RowAssembler appendDecimalNotNull(BigDecimal val) throws SchemaMismatchEx | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DecimalNativeType type = (DecimalNativeType) col.type(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BigDecimal scaled = val.setScale(type.scale(), RoundingMode.HALF_UP); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. Was there a test for this removed behavior that still passes?
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. Good question. Yes. The actual tests that are checking for the exceptions are: Lines 354 to 383 in 084af6c
Essentially, before the PR, the validation for decimals was done here. After the PR, the validation is now done on Column#validate() with seemed to me more similar to the other checks. For instance: Lines 340 to 352 in 084af6c
I also did not find much validation code on row assembler besides The only smell I found is that the validation itself is now done under the gatherStatistics ( Lines 226 to 248 in 8b8d67c
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (scaled.precision() > type.precision()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new SchemaMismatchException(Column.numericFieldOverflow(col.name())); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.appendDecimalNotNull(val, type.scale()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shiftColumn(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
validate()unconditionally castsvaltoBigDecimalfor DECIMAL columns. If an unsupported value type reaches here (i.e.,NativeTypes.fromObject(val)returnsnullso the mismatch branch is skipped), this will throw aClassCastExceptionand bypass the usual marshalling error wrapping. Consider guarding the DECIMAL branch with aninstanceof BigDecimal(or similar) so unsupported types fail via the existing, consistent type-mismatch path instead of a raw CCE.