@@ -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"
@@ -49,14 +60,37 @@ object YearsFunction extends ScalarFunction[Int] with ReducibleFunction[Int, Int
4960 val UTC : ZoneId = ZoneId .of(" UTC" )
5061 val EPOCH_LOCAL_DATE : LocalDate = Instant .EPOCH .atZone(UTC ).toLocalDate
5162
52- def invoke (ts : Long ): Int = {
63+ protected def doInvoke (ts : Long ): Long = {
5364 val localDate = DateTimeUtils .microsToInstant(ts).atZone(UTC ).toLocalDate
54- ChronoUnit .YEARS .between(EPOCH_LOCAL_DATE , localDate).toInt
65+ ChronoUnit .YEARS .between(EPOCH_LOCAL_DATE , localDate)
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 ] {
72+ def invoke (ts : Long ): Int = doInvoke(ts).toInt
5773 override def reducer (otherFunction : ReducibleFunction [_, _]): Reducer [Int , Int ] = null
5874}
5975
76+ // This `years` function reduces `IntegerType` partition keys to `LongType` partition keys when
77+ // partitions are reduced to partitions of a `days` function, which produces `DateType` keys.
78+ object YearsFunctionWithToYearsReducerWithLongResult extends YearsFunctionBase [Long ] {
79+ def invoke (ts : Long ): Int = doInvoke(ts).toInt
80+ override def reducer (otherFunction : ReducibleFunction [_, _]): Reducer [Int , Long ] = {
81+ if (otherFunction == DaysFunctionWithToYearsReducerWithLongResult ) {
82+ YearsToYearsReducerWithLongResult ()
83+ } else {
84+ null
85+ }
86+ }
87+ }
88+
89+ case class YearsToYearsReducerWithLongResult () extends Reducer [Int , Long ] {
90+ override def resultType (): DataType = LongType
91+ override def reduce (days : Int ): Long = days.toLong
92+ }
93+
6094abstract class UnboundDaysFunctionBase extends UnboundFunction {
6195 protected def isValidType (dt : DataType ): Boolean = dt match {
6296 case DateType | TimestampType => true
@@ -75,16 +109,25 @@ object UnboundDaysFunction extends UnboundDaysFunctionBase {
75109 }
76110}
77111
78- object UnboundDaysFunctionWithIncompatibleResultTypeReducer extends UnboundDaysFunctionBase {
112+ object UnboundDaysFunctionWithToYearsReducerWithDateResult extends UnboundDaysFunctionBase {
113+ override def bind (inputType : StructType ): BoundFunction = {
114+ if (inputType.size == 1 && isValidType(inputType.head.dataType)) {
115+ DaysFunctionWithToYearsReducerWithDateResult
116+ } else throw new UnsupportedOperationException (
117+ " 'days' only take date or timestamp as input type" )
118+ }
119+ }
120+
121+ object UnboundDaysFunctionWithToYearsReducerWithLongResult extends UnboundDaysFunctionBase {
79122 override def bind (inputType : StructType ): BoundFunction = {
80123 if (inputType.size == 1 && isValidType(inputType.head.dataType)) {
81- DaysFunctionWithIncompatibleResultTypeReducer
124+ DaysFunctionWithToYearsReducerWithLongResult
82125 } else throw new UnsupportedOperationException (
83126 " 'days' only take date or timestamp as input type" )
84127 }
85128}
86129
87- abstract class DaysFunctionBase extends ScalarFunction [Int ] with ReducibleFunction [Int , Int ] {
130+ abstract class DaysFunctionBase [ O ] extends ScalarFunction [Int ] with ReducibleFunction [Int , O ] {
88131 override def inputTypes (): Array [DataType ] = Array (TimestampType )
89132 override def resultType (): DataType = DateType
90133 override def name (): String = " days"
@@ -93,7 +136,7 @@ abstract class DaysFunctionBase extends ScalarFunction[Int] with ReducibleFuncti
93136
94137// This `days` function reduces `DateType` partition keys to `IntegerType` partition keys when
95138// partitions are reduced to partitions of a `years` function, which produces `IntegerType` keys.
96- object DaysFunction extends DaysFunctionBase {
139+ object DaysFunction extends DaysFunctionBase [ Int ] {
97140 override def reducer (otherFunc : ReducibleFunction [_, _]): Reducer [Int , Int ] = {
98141 if (otherFunc == YearsFunction ) {
99142 DaysToYearsReducer ()
@@ -105,32 +148,51 @@ object DaysFunction extends DaysFunctionBase {
105148
106149// This `days` function reduces `DateType` partition keys to `DateType` partition keys when
107150// partitions are reduced to partitions of a `years` function, which produces `IntegerType` keys.
108- object DaysFunctionWithIncompatibleResultTypeReducer extends DaysFunctionBase {
151+ object DaysFunctionWithToYearsReducerWithDateResult extends DaysFunctionBase [ Int ] {
109152 override def reducer (otherFunc : ReducibleFunction [_, _]): Reducer [Int , Int ] = {
110153 if (otherFunc == YearsFunction ) {
111- DaysToYearsReducerWithIncompatibleResultType ()
154+ DaysToYearsReducerWithDateResult ()
112155 } else {
113156 null
114157 }
115158 }
116159}
117160
118- abstract class DaysToYearsReducerBase extends Reducer [Int , Int ] {
161+ // This `days` function reduces `DateType` partition keys to `LongType` partition keys when
162+ // partitions are reduced to partitions of a `years` function, which produces `IntegerType` keys.
163+ object DaysFunctionWithToYearsReducerWithLongResult extends DaysFunctionBase [Long ] {
164+ override def reducer (otherFunc : ReducibleFunction [_, _]): Reducer [Int , Long ] = {
165+ if (otherFunc == YearsFunctionWithToYearsReducerWithLongResult ) {
166+ DaysToYearsReducerWithLongResult ()
167+ } else {
168+ null
169+ }
170+ }
171+ }
172+
173+ abstract class DaysToYearsReducerBase [O ] extends Reducer [Int , O ] {
119174 val UTC : ZoneId = ZoneId .of(" UTC" )
120175 val EPOCH_LOCAL_DATE : LocalDate = Instant .EPOCH .atZone(UTC ).toLocalDate
121176
122- override def reduce (days : Int ): Int = {
177+ protected def doReduce (days : Int ): Long = {
123178 val localDate = EPOCH_LOCAL_DATE .plusDays(days)
124- ChronoUnit .YEARS .between(EPOCH_LOCAL_DATE , localDate).toInt
179+ ChronoUnit .YEARS .between(EPOCH_LOCAL_DATE , localDate)
125180 }
126181}
127182
128- case class DaysToYearsReducer () extends DaysToYearsReducerBase {
183+ case class DaysToYearsReducer () extends DaysToYearsReducerBase [ Int ] {
129184 override def resultType (): DataType = IntegerType
185+ override def reduce (days : Int ): Int = doReduce(days).toInt
130186}
131187
132- case class DaysToYearsReducerWithIncompatibleResultType () extends DaysToYearsReducerBase {
188+ case class DaysToYearsReducerWithDateResult () extends DaysToYearsReducerBase [ Int ] {
133189 override def resultType (): DataType = DateType
190+ override def reduce (days : Int ): Int = doReduce(days).toInt
191+ }
192+
193+ case class DaysToYearsReducerWithLongResult () extends DaysToYearsReducerBase [Long ] {
194+ override def resultType (): DataType = LongType
195+ override def reduce (days : Int ): Long = doReduce(days)
134196}
135197
136198object UnboundBucketFunction extends UnboundFunction {
0 commit comments