-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-26965][SQL] Makes ElementAt nullability more precise for array cases #23867
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 4 commits
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 | ||
|---|---|---|---|---|
|
|
@@ -221,7 +221,8 @@ case class GetArrayStructFields( | |||
| * We need to do type checking here as `ordinal` expression maybe unresolved. | ||||
| */ | ||||
| case class GetArrayItem(child: Expression, ordinal: Expression) | ||||
| extends BinaryExpression with ExpectsInputTypes with ExtractValue with NullIntolerant { | ||||
| extends BinaryExpression with GetArrayItemUtil with ExpectsInputTypes with ExtractValue | ||||
| with NullIntolerant { | ||||
|
|
||||
| // We have done type checking for child in `ExtractValue`, so only need to check the `ordinal`. | ||||
| override def inputTypes: Seq[AbstractDataType] = Seq(AnyDataType, IntegralType) | ||||
|
|
@@ -231,23 +232,7 @@ case class GetArrayItem(child: Expression, ordinal: Expression) | |||
|
|
||||
| override def left: Expression = child | ||||
| override def right: Expression = ordinal | ||||
|
|
||||
| /** `Null` is returned for invalid ordinals. */ | ||||
| override def nullable: Boolean = if (ordinal.foldable && !ordinal.nullable) { | ||||
| val intOrdinal = ordinal.eval().asInstanceOf[Number].intValue() | ||||
| child match { | ||||
| case CreateArray(ar) if intOrdinal < ar.length => | ||||
| ar(intOrdinal).nullable | ||||
| case GetArrayStructFields(CreateArray(elements), field, _, _, _) | ||||
| if intOrdinal < elements.length => | ||||
| elements(intOrdinal).nullable || field.nullable | ||||
| case _ => | ||||
| true | ||||
| } | ||||
| } else { | ||||
| true | ||||
| } | ||||
|
|
||||
| override def nullable: Boolean = computeNullabilityFromArray(left, right) | ||||
| override def dataType: DataType = child.dataType.asInstanceOf[ArrayType].elementType | ||||
|
|
||||
| protected override def nullSafeEval(value: Any, ordinal: Any): Any = { | ||||
|
|
@@ -281,10 +266,50 @@ case class GetArrayItem(child: Expression, ordinal: Expression) | |||
| } | ||||
|
|
||||
| /** | ||||
| * Common base class for [[GetMapValue]] and [[ElementAt]]. | ||||
| * Common trait for [[GetArrayItem]] and [[ElementAt]]. | ||||
| */ | ||||
| trait GetArrayItemUtil { | ||||
|
|
||||
| /** `Null` is returned for invalid ordinals. */ | ||||
| protected def computeNullabilityFromArray(child: Expression, ordinal: Expression): Boolean = { | ||||
| if (ordinal.foldable && !ordinal.nullable) { | ||||
| val intOrdinal = ordinal.eval().asInstanceOf[Number].intValue() | ||||
| child match { | ||||
| case CreateArray(ar) if intOrdinal < ar.length => | ||||
| ar(intOrdinal).nullable | ||||
| case GetArrayStructFields(CreateArray(elements), field, _, _, _) | ||||
| if intOrdinal < elements.length => | ||||
| elements(intOrdinal).nullable || field.nullable | ||||
| case _ => | ||||
| true | ||||
| } | ||||
| } else { | ||||
| true | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
||||
| /** | ||||
| * Common trait for [[GetMapValue]] and [[ElementAt]]. | ||||
| */ | ||||
| trait GetMapValueUtil extends BinaryExpression with ImplicitCastInputTypes { | ||||
|
Member
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. Does
Member
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. It seems Line 326 in 2d2fb34
Member
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. Thanks. Then, what about
Member
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. ah, ok. I'll simplify it. |
||||
|
|
||||
| /** `Null` is returned for invalid ordinals. */ | ||||
| protected def computeNullabilityFromMap: Boolean = if (right.foldable && !right.nullable) { | ||||
| val keyObj = right.eval() | ||||
| left match { | ||||
| case m: CreateMap if m.resolved => | ||||
| m.keys.zip(m.values).filter { case (k, _) => k.foldable && !k.nullable }.find { | ||||
| case (k, _) if k.eval() == keyObj => true | ||||
|
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 we rely on I know this is existing code, but after a hindsight maybe it's not worth to linear scan the map entries and just to get the precise nullability.
Member
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. Ah, I see. I'll update soon.
Member
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 dropped the nullablity computation for the map side and added the TODO comment there. How about this? |
||||
| case _ => false | ||||
| }.map(_._2.nullable).getOrElse(true) | ||||
| case _ => | ||||
| true | ||||
| } | ||||
| } else { | ||||
| true | ||||
| } | ||||
|
|
||||
| abstract class GetMapValueUtil extends BinaryExpression with ImplicitCastInputTypes { | ||||
| // todo: current search is O(n), improve it. | ||||
| def getValueEval(value: Any, ordinal: Any, keyType: DataType, ordering: Ordering[Any]): Any = { | ||||
| val map = value.asInstanceOf[MapData] | ||||
|
|
@@ -379,24 +404,7 @@ case class GetMapValue(child: Expression, key: Expression) | |||
|
|
||||
| override def left: Expression = child | ||||
| override def right: Expression = key | ||||
|
|
||||
| /** `Null` is returned for invalid ordinals. */ | ||||
| override def nullable: Boolean = if (key.foldable && !key.nullable) { | ||||
| val keyObj = key.eval() | ||||
| child match { | ||||
| case m: CreateMap if m.resolved => | ||||
| m.keys.zip(m.values).filter { case (k, _) => k.foldable && !k.nullable }.find { | ||||
| case (k, _) if k.eval() == keyObj => true | ||||
| case _ => false | ||||
| }.map(_._2.nullable).getOrElse(true) | ||||
| case _ => | ||||
| true | ||||
| } | ||||
| } else { | ||||
| true | ||||
| } | ||||
|
|
||||
|
|
||||
| override def nullable: Boolean = computeNullabilityFromMap | ||||
| override def dataType: DataType = child.dataType.asInstanceOf[MapType].valueType | ||||
|
|
||||
| // todo: current search is O(n), improve it. | ||||
|
|
||||
Uh oh!
There was an error while loading. Please reload this page.
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.
If
Mapis out of the scope, shall we update the title and use the following code path instead?extends GetMapValueUtil.computeNullabilityFromMapand use the following.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.
fixed.