in common/variant/src/main/java/org/apache/spark/types/variant/VariantShreddingWriter.java [155:295]
private static Object tryTypedShred(
Variant v,
VariantUtil.Type variantType,
VariantSchema.ScalarType targetType,
ShreddedResultBuilder builder) {
switch (variantType) {
case LONG:
if (targetType instanceof VariantSchema.IntegralType integralType) {
// Check that the target type can hold the actual value.
VariantSchema.IntegralSize size = integralType.size;
long value = v.getLong();
switch (size) {
case BYTE:
if (value == (byte) value) {
return (byte) value;
}
break;
case SHORT:
if (value == (short) value) {
return (short) value;
}
break;
case INT:
if (value == (int) value) {
return (int) value;
}
break;
case LONG:
return value;
}
} else if (targetType instanceof VariantSchema.DecimalType decimalType &&
builder.allowNumericScaleChanges()) {
// If the integer can fit in the given decimal precision, allow it.
long value = v.getLong();
// Set to the requested scale, and check if the precision is large enough.
BigDecimal decimalValue = BigDecimal.valueOf(value);
BigDecimal scaledValue = decimalValue.setScale(decimalType.scale);
// The initial value should have scale 0, so rescaling shouldn't lose information.
assert(decimalValue.compareTo(scaledValue) == 0);
if (scaledValue.precision() <= decimalType.precision) {
return scaledValue;
}
}
break;
case DECIMAL:
if (targetType instanceof VariantSchema.DecimalType decimalType) {
// Use getDecimalWithOriginalScale so that we retain scale information if
// allowNumericScaleChanges() is false.
BigDecimal value = VariantUtil.getDecimalWithOriginalScale(v.value, v.pos);
if (value.precision() <= decimalType.precision &&
value.scale() == decimalType.scale) {
return value;
}
if (builder.allowNumericScaleChanges()) {
// Convert to the target scale, and see if it fits. Rounding mode doesn't matter,
// since we'll reject it if it turned out to require rounding.
BigDecimal scaledValue = value.setScale(decimalType.scale, RoundingMode.FLOOR);
if (scaledValue.compareTo(value) == 0 &&
scaledValue.precision() <= decimalType.precision) {
return scaledValue;
}
}
} else if (targetType instanceof VariantSchema.IntegralType integralType &&
builder.allowNumericScaleChanges()) {
// Check if the decimal happens to be an integer.
BigDecimal value = v.getDecimal();
VariantSchema.IntegralSize size = integralType.size;
// Try to cast to the appropriate type, and check if any information is lost.
switch (size) {
case BYTE:
if (value.compareTo(BigDecimal.valueOf(value.byteValue())) == 0) {
return value.byteValue();
}
break;
case SHORT:
if (value.compareTo(BigDecimal.valueOf(value.shortValue())) == 0) {
return value.shortValue();
}
break;
case INT:
if (value.compareTo(BigDecimal.valueOf(value.intValue())) == 0) {
return value.intValue();
}
break;
case LONG:
if (value.compareTo(BigDecimal.valueOf(value.longValue())) == 0) {
return value.longValue();
}
}
}
break;
case BOOLEAN:
if (targetType instanceof VariantSchema.BooleanType) {
return v.getBoolean();
}
break;
case STRING:
if (targetType instanceof VariantSchema.StringType) {
return v.getString();
}
break;
case DOUBLE:
if (targetType instanceof VariantSchema.DoubleType) {
return v.getDouble();
}
break;
case DATE:
if (targetType instanceof VariantSchema.DateType) {
return (int) v.getLong();
}
break;
case TIMESTAMP:
if (targetType instanceof VariantSchema.TimestampType) {
return v.getLong();
}
break;
case TIMESTAMP_NTZ:
if (targetType instanceof VariantSchema.TimestampNTZType) {
return v.getLong();
}
break;
case FLOAT:
if (targetType instanceof VariantSchema.FloatType) {
return v.getFloat();
}
break;
case BINARY:
if (targetType instanceof VariantSchema.BinaryType) {
return v.getBinary();
}
break;
case UUID:
if (targetType instanceof VariantSchema.UuidType) {
return v.getUuid();
}
break;
}
// The stored type does not match the requested shredding type. Return null, and the caller
// will store the result in untyped_value.
return null;
}