@@ -24,23 +24,34 @@ import org.apache.spark.sql.catalyst.util.DateTimeUtils
2424import org .apache .spark .sql .types ._
2525import org .apache .spark .unsafe .types .UTF8String
2626
27- object UnboundYearsFunction extends UnboundFunction {
27+ abstract class UnboundYearsFunctionBase extends UnboundFunction {
28+ protected def isValidType (dt : DataType ): Boolean = dt match {
29+ case DateType | TimestampType => true
30+ case _ => false
31+ }
32+
33+ override def description (): String = name()
34+ override def name (): String = " years"
35+ }
36+
37+ object UnboundYearsFunction extends UnboundYearsFunctionBase {
2838 override def bind (inputType : StructType ): BoundFunction = {
2939 if (inputType.size == 1 && isValidType(inputType.head.dataType)) YearsFunction
3040 else throw new UnsupportedOperationException (
3141 " 'years' only take date or timestamp as input type" )
3242 }
43+ }
3344
34- private def isValidType (dt : DataType ): Boolean = dt match {
35- case DateType | TimestampType => true
36- case _ => false
45+ object UnboundYearsFunctionWithToYearsReducerWithLongResult extends UnboundYearsFunctionBase {
46+ override def bind (inputType : StructType ): BoundFunction = {
47+ if (inputType.size == 1 && isValidType(inputType.head.dataType)) {
48+ YearsFunctionWithToYearsReducerWithLongResult
49+ } else throw new UnsupportedOperationException (
50+ " 'years' only take date or timestamp as input type" )
3751 }
38-
39- override def description (): String = name()
40- override def name (): String = " years"
4152}
4253
43- object YearsFunction extends ScalarFunction [Int ] with ReducibleFunction [Int , Int ] {
54+ abstract class YearsFunctionBase [ O ] extends ScalarFunction [Int ] with ReducibleFunction [Int , O ] {
4455 override def inputTypes (): Array [DataType ] = Array (TimestampType )
4556 override def resultType (): DataType = IntegerType
4657 override def name (): String = " years"
@@ -53,10 +64,31 @@ object YearsFunction extends ScalarFunction[Int] with ReducibleFunction[Int, Int
5364 val localDate = DateTimeUtils .microsToInstant(ts).atZone(UTC ).toLocalDate
5465 ChronoUnit .YEARS .between(EPOCH_LOCAL_DATE , localDate).toInt
5566 }
67+ }
5668
69+ // This `years` function reduces `IntegerType` partition keys to `IntegerType` partition keys when
70+ // partitions are reduced to partitions of a `days` function, which produces `DateType` keys.
71+ object YearsFunction extends YearsFunctionBase [Int ] {
5772 override def reducer (otherFunction : ReducibleFunction [_, _]): Reducer [Int , Int ] = null
5873}
5974
75+ // This `years` function reduces `IntegerType` partition keys to `LongType` partition keys when
76+ // partitions are reduced to partitions of a `days` function, which produces `DateType` keys.
77+ object YearsFunctionWithToYearsReducerWithLongResult extends YearsFunctionBase [Long ] {
78+ override def reducer (otherFunction : ReducibleFunction [_, _]): Reducer [Int , Long ] = {
79+ if (otherFunction == DaysFunctionWithToYearsReducerWithLongResult ) {
80+ YearsToYearsReducerWithLogResult ()
81+ } else {
82+ null
83+ }
84+ }
85+ }
86+
87+ case class YearsToYearsReducerWithLogResult () extends Reducer [Int , Long ] {
88+ override def resultType (): DataType = LongType
89+ override def reduce (days : Int ): Long = days.toLong
90+ }
91+
6092abstract class UnboundDaysFunctionBase extends UnboundFunction {
6193 protected def isValidType (dt : DataType ): Boolean = dt match {
6294 case DateType | TimestampType => true
@@ -75,16 +107,25 @@ object UnboundDaysFunction extends UnboundDaysFunctionBase {
75107 }
76108}
77109
78- object UnboundDaysFunctionWithIncompatibleResultTypeReducer extends UnboundDaysFunctionBase {
110+ object UnboundDaysFunctionWithToYearsReducerWithDateResult extends UnboundDaysFunctionBase {
111+ override def bind (inputType : StructType ): BoundFunction = {
112+ if (inputType.size == 1 && isValidType(inputType.head.dataType)) {
113+ DaysFunctionWithToYearsReducerWithDateResult
114+ } else throw new UnsupportedOperationException (
115+ " 'days' only take date or timestamp as input type" )
116+ }
117+ }
118+
119+ object UnboundDaysFunctionWithToYearsReducerWithLongResult extends UnboundDaysFunctionBase {
79120 override def bind (inputType : StructType ): BoundFunction = {
80121 if (inputType.size == 1 && isValidType(inputType.head.dataType)) {
81- DaysFunctionWithIncompatibleResultTypeReducer
122+ DaysFunctionWithToYearsReducerWithLongResult
82123 } else throw new UnsupportedOperationException (
83124 " 'days' only take date or timestamp as input type" )
84125 }
85126}
86127
87- abstract class DaysFunctionBase extends ScalarFunction [Int ] with ReducibleFunction [Int , Int ] {
128+ abstract class DaysFunctionBase [ O ] extends ScalarFunction [Int ] with ReducibleFunction [Int , O ] {
88129 override def inputTypes (): Array [DataType ] = Array (TimestampType )
89130 override def resultType (): DataType = DateType
90131 override def name (): String = " days"
@@ -93,7 +134,7 @@ abstract class DaysFunctionBase extends ScalarFunction[Int] with ReducibleFuncti
93134
94135// This `days` function reduces `DateType` partition keys to `IntegerType` partition keys when
95136// partitions are reduced to partitions of a `years` function, which produces `IntegerType` keys.
96- object DaysFunction extends DaysFunctionBase {
137+ object DaysFunction extends DaysFunctionBase [ Int ] {
97138 override def reducer (otherFunc : ReducibleFunction [_, _]): Reducer [Int , Int ] = {
98139 if (otherFunc == YearsFunction ) {
99140 DaysToYearsReducer ()
@@ -105,32 +146,51 @@ object DaysFunction extends DaysFunctionBase {
105146
106147// This `days` function reduces `DateType` partition keys to `DateType` partition keys when
107148// partitions are reduced to partitions of a `years` function, which produces `IntegerType` keys.
108- object DaysFunctionWithIncompatibleResultTypeReducer extends DaysFunctionBase {
149+ object DaysFunctionWithToYearsReducerWithDateResult extends DaysFunctionBase [ Int ] {
109150 override def reducer (otherFunc : ReducibleFunction [_, _]): Reducer [Int , Int ] = {
110151 if (otherFunc == YearsFunction ) {
111- DaysToYearsReducerWithIncompatibleResultType ()
152+ DaysToYearsReducerWithDateResult ()
112153 } else {
113154 null
114155 }
115156 }
116157}
117158
118- abstract class DaysToYearsReducerBase extends Reducer [Int , Int ] {
159+ // This `days` function reduces `DateType` partition keys to `LongType` partition keys when
160+ // partitions are reduced to partitions of a `years` function, which produces `IntegerType` keys.
161+ object DaysFunctionWithToYearsReducerWithLongResult extends DaysFunctionBase [Long ] {
162+ override def reducer (otherFunc : ReducibleFunction [_, _]): Reducer [Int , Long ] = {
163+ if (otherFunc == YearsFunctionWithToYearsReducerWithLongResult ) {
164+ DaysToYearsReducerWithLongResult ()
165+ } else {
166+ null
167+ }
168+ }
169+ }
170+
171+ abstract class DaysToYearsReducerBase [O ] extends Reducer [Int , O ] {
119172 val UTC : ZoneId = ZoneId .of(" UTC" )
120173 val EPOCH_LOCAL_DATE : LocalDate = Instant .EPOCH .atZone(UTC ).toLocalDate
121174
122- override def reduce (days : Int ): Int = {
175+ protected def doReduce (days : Int ): Long = {
123176 val localDate = EPOCH_LOCAL_DATE .plusDays(days)
124- ChronoUnit .YEARS .between(EPOCH_LOCAL_DATE , localDate).toInt
177+ ChronoUnit .YEARS .between(EPOCH_LOCAL_DATE , localDate)
125178 }
126179}
127180
128- case class DaysToYearsReducer () extends DaysToYearsReducerBase {
181+ case class DaysToYearsReducer () extends DaysToYearsReducerBase [ Int ] {
129182 override def resultType (): DataType = IntegerType
183+ override def reduce (days : Int ): Int = doReduce(days).toInt
130184}
131185
132- case class DaysToYearsReducerWithIncompatibleResultType () extends DaysToYearsReducerBase {
186+ case class DaysToYearsReducerWithDateResult () extends DaysToYearsReducerBase [ Int ] {
133187 override def resultType (): DataType = DateType
188+ override def reduce (days : Int ): Int = doReduce(days).toInt
189+ }
190+
191+ case class DaysToYearsReducerWithLongResult () extends DaysToYearsReducerBase [Long ] {
192+ override def resultType (): DataType = LongType
193+ override def reduce (days : Int ): Long = doReduce(days)
134194}
135195
136196object UnboundBucketFunction extends UnboundFunction {
0 commit comments