Skip to content

[common] Fix inferred variant shredding failing on decimals with trailing zeros - #9811

Open
zhuxiangyi wants to merge 1 commit into
apache:masterfrom
zhuxiangyi:variant-infer-decimal-negative-scale
Open

[common] Fix inferred variant shredding failing on decimals with trailing zeros#9811
zhuxiangyi wants to merge 1 commit into
apache:masterfrom
zhuxiangyi:variant-infer-decimal-negative-scale

Conversation

@zhuxiangyi

Copy link
Copy Markdown
Contributor

Purpose

This is a bug fix. With variant.inferShreddingSchema=true, writing a Variant that contains a decimal with trailing zeros fails the whole file:

CREATE TABLE T (id INT, v VARIANT) TBLPROPERTIES ('variant.inferShreddingSchema' = 'true');
INSERT INTO T VALUES (1, parse_json('{"price":10.0}')), (2, parse_json('{"price":20.5}'));
-- Job aborted ... java.lang.IllegalArgumentException: Decimal scale must be between 0 and the precision 1 (both inclusive).
--   at org.apache.paimon.types.DataTypes.DECIMAL(DataTypes.java:115)
--   at org.apache.paimon.data.variant.InferVariantShreddingSchema.schemaOf(InferVariantShreddingSchema.java:367)

InferVariantShreddingSchema.schemaOf derives the shredded decimal type from getDecimal(), which strips trailing zeros: 10.0 arrives as 1E+1 and 100.00 as 1E+2, both with a negative scale that DecimalType rejects. The existing guard only covered precision < scale (values below 0.1). Integers beyond the long range that end in zeros (1000000000000000000001E+20) hit the same path. Any engine that writes through the inference path (Spark, Flink) is affected, and 10.0-style values are common in real data.

The fix folds a negative scale back into the digits (setScale(0)) before building the type, the same normalisation VariantGet gained in #9672, and keeps the precision at least the scale. 10.0 now infers as DECIMAL(2, 0), merges with 20.5 into DECIMAL(18, 1), and a lone 100.00 widens to BIGINT as an integer-like decimal, which the shredding writer accepts because it compares against the original scale and allows exact rescaling.

Tests

The new tests reproduce the bug: on master they fail with the IllegalArgumentException above (both the unit test and the two inferred-shredding Spark suites), and pass with this change.

  • InferVariantShreddingSchemaTest#testInferSchemaWithDecimalTrailingZeros: 10.0 / 100.00 / 0.05 / 100000000000000000000 infer to DECIMAL(18, 1) / BIGINT / DECIMAL(18, 3) / DECIMAL(38, 0).
  • InferVariantShreddingWriteTest#testInferSchemaWithDecimalTrailingZeros: writes such rows with inference on, checks the physical Parquet schema, the reconstructed Variant and typed extraction of $.price / $.whole.
  • VariantTestBase: inserts 10.0 / 100.00 and reads them back; runs under all four Spark 4.x configurations. Verified on Spark 4.1.2: 116 tests pass.

API and Format

No.

Documentation

No.

…ling zeros

InferVariantShreddingSchema builds the shredded type of a decimal from
getDecimal(), which strips trailing zeros: 10.0 becomes 1E+1 and 100.00
becomes 1E+2, both with a negative scale. DataTypes.DECIMAL rejects that,
so any file whose sampled rows contain such a value failed to write with
"Decimal scale must be between 0 and the precision 1" as soon as
variant.inferShreddingSchema was enabled. Integers beyond the long range
with trailing zeros hit the same path.

Fold a negative scale back into the digits before building the type, the
way VariantGet already does since apache#9672, and keep the precision at least
the scale for values below 0.1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant