Skip to content

Commit 0c68578

Browse files
yaooqinncloud-fan
authored andcommitted
[SPARK-29888][SQL] new interval string parser shall handle numeric with only fractional part
### What changes were proposed in this pull request? Current string to interval cast logic does not support i.e. cast('.111 second' as interval) which will fail in SIGN state and return null, actually, it is 00:00:00.111. ```scala -- !query 63 select interval '.111 seconds' -- !query 63 schema struct<0.111 seconds:interval> -- !query 63 output 0.111 seconds -- !query 64 select cast('.111 seconds' as interval) -- !query 64 schema struct<CAST(.111 seconds AS INTERVAL):interval> -- !query 64 output NULL ```` ### Why are the changes needed? bug fix. ### Does this PR introduce any user-facing change? no ### How was this patch tested? add ut Closes apache#26514 from yaooqinn/SPARK-29888. Authored-by: Kent Yao <[email protected]> Signed-off-by: Wenchen Fan <[email protected]>
1 parent d1ac25b commit 0c68578

File tree

2 files changed

+17
-6
lines changed

2 files changed

+17
-6
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/IntervalUtils.scala

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,17 @@ object IntervalUtils {
496496
state = TRIM_BEFORE_SIGN
497497
case TRIM_BEFORE_SIGN => trimToNextState(b, SIGN)
498498
case SIGN =>
499+
currentValue = 0
500+
fraction = 0
501+
// We preset next state from SIGN to TRIM_BEFORE_VALUE. If we meet '.' in the SIGN state,
502+
// it means that the interval value we deal with here is a numeric with only fractional
503+
// part, such as '.11 second', which can be parsed to 0.11 seconds. In this case, we need
504+
// to reset next state to `VALUE_FRACTIONAL_PART` to go parse the fraction part of the
505+
// interval value.
506+
state = TRIM_BEFORE_VALUE
507+
// We preset the scale to an invalid value to track fraction presence in the UNIT_BEGIN
508+
// state. If we meet '.', the scale become valid for the VALUE_FRACTIONAL_PART state.
509+
fractionScale = -1
499510
b match {
500511
case '-' =>
501512
isNegative = true
@@ -505,14 +516,13 @@ object IntervalUtils {
505516
i += 1
506517
case _ if '0' <= b && b <= '9' =>
507518
isNegative = false
519+
case '.' =>
520+
isNegative = false
521+
fractionScale = (NANOS_PER_SECOND / 10).toInt
522+
i += 1
523+
state = VALUE_FRACTIONAL_PART
508524
case _ => return null
509525
}
510-
currentValue = 0
511-
fraction = 0
512-
// Sets the scale to an invalid value to track fraction presence
513-
// in the BEGIN_UNIT_NAME state
514-
fractionScale = -1
515-
state = TRIM_BEFORE_VALUE
516526
case TRIM_BEFORE_VALUE => trimToNextState(b, VALUE)
517527
case VALUE =>
518528
b match {

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/IntervalUtilsSuite.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class IntervalUtilsSuite extends SparkFunSuite {
106106
checkFromString("-1.5 seconds", new CalendarInterval(0, 0, -1500000))
107107
// truncate nanoseconds to microseconds
108108
checkFromString("0.999999999 seconds", new CalendarInterval(0, 0, 999999))
109+
checkFromString(".999999999 seconds", new CalendarInterval(0, 0, 999999))
109110
checkFromInvalidString("0.123456789123 seconds", "Error parsing interval string")
110111
}
111112

0 commit comments

Comments
 (0)