Skip to content

Commit

Permalink
[SPARK-47487][SQL] Simplify code in AnsiTypeCoercion
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?

Simplify the code in `AnsiTypeCoercion.implicitCast`, to merge common code paths.

### Why are the changes needed?

improve code readability

### Does this PR introduce _any_ user-facing change?

no

### How was this patch tested?

existing tests

### Was this patch authored or co-authored using generative AI tooling?

No

Closes apache#45612 from cloud-fan/type-coercion.

Authored-by: Wenchen Fan <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
  • Loading branch information
cloud-fan authored and dongjoon-hyun committed Mar 21, 2024
1 parent 32f3d4d commit 25ecde9
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ abstract class DataType extends AbstractDataType {
*/
private[spark] def existsRecursively(f: (DataType) => Boolean): Boolean = f(this)

override private[sql] def defaultConcreteType: DataType = this
final override private[sql] def defaultConcreteType: DataType = this

override private[sql] def acceptsType(other: DataType): Boolean = sameType(other)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,56 +180,30 @@ object AnsiTypeCoercion extends TypeCoercionBase {
// cast the input to decimal.
case (n: NumericType, DecimalType) => Some(DecimalType.forType(n))

// Cast null type (usually from null literals) into target types
// By default, the result type is `target.defaultConcreteType`. When the target type is
// `TypeCollection`, there is another branch to find the "closet convertible data type" below.
case (NullType, target) if !target.isInstanceOf[TypeCollection] =>
Some(target.defaultConcreteType)

// If a function expects a StringType, no StringType instance should be implicitly cast to
// StringType with a collation that's not accepted (aka. lockdown unsupported collations).
case (_: StringType, StringType) => None
case (_: StringType, _: StringTypeCollated) => None

// This type coercion system will allow implicit converting String type as other
// primitive types, in case of breaking too many existing Spark SQL queries.
case (StringType, a: AtomicType) =>
Some(a)

// If the target type is any Numeric type, convert the String type as Double type.
case (StringType, NumericType) =>
Some(DoubleType)

// If the target type is any Decimal type, convert the String type as the default
// Decimal type.
case (StringType, DecimalType) =>
Some(DecimalType.SYSTEM_DEFAULT)

// If the target type is any timestamp type, convert the String type as the default
// Timestamp type.
case (StringType, AnyTimestampType) =>
Some(AnyTimestampType.defaultConcreteType)

case (DateType, AnyTimestampType) =>
Some(AnyTimestampType.defaultConcreteType)

case (_, target: DataType) =>
if (Cast.canANSIStoreAssign(inType, target)) {
Some(target)
// If a function expects integral type, fractional input is not allowed.
case (_: FractionalType, IntegralType) => None

// Ideally the implicit cast rule should be the same as `Cast.canANSIStoreAssign` so that it's
// consistent with table insertion. To avoid breaking too many existing Spark SQL queries,
// we make the system to allow implicitly converting String type as other primitive types.
case (StringType, a @ (_: AtomicType | NumericType | DecimalType | AnyTimestampType)) =>
Some(a.defaultConcreteType)

// When the target type is `TypeCollection`, there is another branch to find the
// "closet convertible data type" below.
case (_, target) if !target.isInstanceOf[TypeCollection] =>
val concreteType = target.defaultConcreteType
if (Cast.canANSIStoreAssign(inType, concreteType)) {
Some(concreteType)
} else {
None
}

// "canANSIStoreAssign" doesn't account for targets extending StringTypeCollated, but
// ANSIStoreAssign is generally expected to work with StringTypes
case (_, st: StringTypeCollated) =>
if (Cast.canANSIStoreAssign(inType, st.defaultConcreteType)) {
Some(st.defaultConcreteType)
}
else {
None
}

// When we reach here, input type is not acceptable for any types in this type collection,
// try to find the first one we can implicitly cast.
case (_, TypeCollection(types)) =>
Expand Down

0 comments on commit 25ecde9

Please sign in to comment.