-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-29387][SQL] Support * and / operators for intervals
#26132
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 30 commits
af2e7ab
4227915
379a20a
3e9ed0f
670a7c6
3ae94cf
3bce68e
754109c
166dbd8
b4dc59a
1e2a9a6
a6b6d81
6e569c0
69a3cc7
001d17b
014cde5
049f428
1ca7c89
9e6745a
b428070
8ad4001
91337e5
2bb916f
6ba53f0
719fe6c
d05ffa4
34f6605
00ede6c
690d9c1
5b25432
2265449
e559fb9
35ab9c0
dbc39e8
8244460
b70c0f8
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 |
|---|---|---|
|
|
@@ -59,6 +59,27 @@ public CalendarInterval subtract(CalendarInterval that) { | |
| return new CalendarInterval(months, days, microseconds); | ||
| } | ||
|
|
||
| private static CalendarInterval fromDoubles( | ||
| double monthsWithFraction, | ||
| double daysWithFraction, | ||
| double microsWithFraction) { | ||
| int truncatedMonths = Math.toIntExact((long)(monthsWithFraction)); | ||
| // Using 30 days per month as PostgreSQL does. | ||
| double days = daysWithFraction + 30 * (monthsWithFraction - truncatedMonths); | ||
|
||
| int truncatedDays = Math.toIntExact((long)days); | ||
| double micros = microsWithFraction + MICROS_PER_DAY * (days - truncatedDays); | ||
| long truncatedMicros = Math.round(micros); | ||
| return new CalendarInterval(truncatedMonths, truncatedDays, truncatedMicros); | ||
| } | ||
|
|
||
| public CalendarInterval multiply(double num) { | ||
|
||
| return fromDoubles(num * this.months, num * this.days, num * this.microseconds); | ||
| } | ||
|
|
||
| public CalendarInterval divide(double num) { | ||
| return fromDoubles(this.months / num, this.days / num, this.microseconds / num); | ||
| } | ||
|
|
||
| public CalendarInterval negate() { | ||
| return new CalendarInterval(-this.months, -this.days, -this.microseconds); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -831,6 +831,8 @@ object TypeCoercion { | |
| * 2. Turns Add/Subtract of TimestampType/DateType/IntegerType | ||
| * and TimestampType/IntegerType/DateType to DateAdd/DateSub/SubtractDates and | ||
| * to SubtractTimestamps. | ||
| * 3. Turns Multiply/Divide of CalendarIntervalType and NumericType | ||
| * to MultiplyInterval/DivideInterval | ||
| */ | ||
| object DateTimeOperations extends Rule[LogicalPlan] { | ||
|
|
||
|
|
@@ -846,6 +848,12 @@ object TypeCoercion { | |
| Cast(TimeAdd(l, r), l.dataType) | ||
| case Subtract(l, r @ CalendarIntervalType()) if acceptedTypes.contains(l.dataType) => | ||
| Cast(TimeSub(l, r), l.dataType) | ||
| case Multiply(l @ CalendarIntervalType(), r @ NumericType()) => | ||
| MultiplyInterval(l, r) | ||
| case Multiply(l @ NumericType(), r @ CalendarIntervalType()) => | ||
| MultiplyInterval(r, l) | ||
| case Divide(l @ CalendarIntervalType(), r @ NumericType()) => | ||
|
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. postgres=# select interval '1 year' / '365';
?column?
---------------
23:40:16.4064
(1 row)could this be supported?
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. Taking into account the discussion in #26165, I am not sure. @cloud-fan Should I support this?
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. We can, but this should only apply to literals, not string columns.
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. If we want to support it, please open another PR. |
||
| DivideInterval(l, r) | ||
|
|
||
| case Add(l @ DateType(), r @ IntegerType()) => DateAdd(l, r) | ||
| case Add(l @ IntegerType(), r @ DateType()) => DateAdd(r, l) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,45 +45,45 @@ abstract class ExtractIntervalPart( | |
| } | ||
|
|
||
| case class ExtractIntervalMillenniums(child: Expression) | ||
| extends ExtractIntervalPart(child, IntegerType, getMillenniums, "getMillenniums") | ||
| extends ExtractIntervalPart(child, IntegerType, getMillenniums, "getMillenniums") | ||
|
|
||
| case class ExtractIntervalCenturies(child: Expression) | ||
| extends ExtractIntervalPart(child, IntegerType, getCenturies, "getCenturies") | ||
| extends ExtractIntervalPart(child, IntegerType, getCenturies, "getCenturies") | ||
|
|
||
| case class ExtractIntervalDecades(child: Expression) | ||
| extends ExtractIntervalPart(child, IntegerType, getDecades, "getDecades") | ||
| extends ExtractIntervalPart(child, IntegerType, getDecades, "getDecades") | ||
|
|
||
| case class ExtractIntervalYears(child: Expression) | ||
| extends ExtractIntervalPart(child, IntegerType, getYears, "getYears") | ||
| extends ExtractIntervalPart(child, IntegerType, getYears, "getYears") | ||
|
|
||
| case class ExtractIntervalQuarters(child: Expression) | ||
| extends ExtractIntervalPart(child, ByteType, getQuarters, "getQuarters") | ||
| extends ExtractIntervalPart(child, ByteType, getQuarters, "getQuarters") | ||
|
|
||
| case class ExtractIntervalMonths(child: Expression) | ||
| extends ExtractIntervalPart(child, ByteType, getMonths, "getMonths") | ||
| extends ExtractIntervalPart(child, ByteType, getMonths, "getMonths") | ||
|
|
||
| case class ExtractIntervalDays(child: Expression) | ||
| extends ExtractIntervalPart(child, IntegerType, getDays, "getDays") | ||
| extends ExtractIntervalPart(child, IntegerType, getDays, "getDays") | ||
|
|
||
| case class ExtractIntervalHours(child: Expression) | ||
| extends ExtractIntervalPart(child, LongType, getHours, "getHours") | ||
| extends ExtractIntervalPart(child, LongType, getHours, "getHours") | ||
|
|
||
| case class ExtractIntervalMinutes(child: Expression) | ||
| extends ExtractIntervalPart(child, ByteType, getMinutes, "getMinutes") | ||
| extends ExtractIntervalPart(child, ByteType, getMinutes, "getMinutes") | ||
|
|
||
| case class ExtractIntervalSeconds(child: Expression) | ||
| extends ExtractIntervalPart(child, DecimalType(8, 6), getSeconds, "getSeconds") | ||
| extends ExtractIntervalPart(child, DecimalType(8, 6), getSeconds, "getSeconds") | ||
|
|
||
| case class ExtractIntervalMilliseconds(child: Expression) | ||
| extends ExtractIntervalPart(child, DecimalType(8, 3), getMilliseconds, "getMilliseconds") | ||
| extends ExtractIntervalPart(child, DecimalType(8, 3), getMilliseconds, "getMilliseconds") | ||
|
|
||
| case class ExtractIntervalMicroseconds(child: Expression) | ||
| extends ExtractIntervalPart(child, LongType, getMicroseconds, "getMicroseconds") | ||
| extends ExtractIntervalPart(child, LongType, getMicroseconds, "getMicroseconds") | ||
|
|
||
| // Number of seconds in 10000 years is 315576000001 (30 days per one month) | ||
| // which is 12 digits + 6 digits for the fractional part of seconds. | ||
| case class ExtractIntervalEpoch(child: Expression) | ||
| extends ExtractIntervalPart(child, DecimalType(18, 6), getEpoch, "getEpoch") | ||
| extends ExtractIntervalPart(child, DecimalType(18, 6), getEpoch, "getEpoch") | ||
|
|
||
| object ExtractIntervalPart { | ||
|
|
||
|
|
@@ -109,3 +109,54 @@ object ExtractIntervalPart { | |
| case _ => errorHandleFunc | ||
| } | ||
| } | ||
|
|
||
| abstract class IntervalNumOperation( | ||
| interval: Expression, | ||
| num: Expression, | ||
| operation: (CalendarInterval, Double) => CalendarInterval, | ||
| operationName: String) | ||
| extends BinaryExpression with ImplicitCastInputTypes with Serializable { | ||
| override def left: Expression = interval | ||
| override def right: Expression = num | ||
|
|
||
| override def inputTypes: Seq[AbstractDataType] = Seq(CalendarIntervalType, DoubleType) | ||
| override def dataType: DataType = CalendarIntervalType | ||
|
|
||
| override def nullable: Boolean = true | ||
|
|
||
| override def nullSafeEval(interval: Any, num: Any): Any = { | ||
| try { | ||
| operation(interval.asInstanceOf[CalendarInterval], num.asInstanceOf[Double]) | ||
| } catch { | ||
| case _: java.lang.ArithmeticException => null | ||
| } | ||
| } | ||
|
|
||
| override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = { | ||
| nullSafeCodeGen(ctx, ev, (interval, num) => { | ||
| s""" | ||
| try { | ||
| ${ev.value} = $interval.$operationName($num); | ||
| } catch (java.lang.ArithmeticException e) { | ||
| ${ev.isNull} = true; | ||
| } | ||
| """ | ||
| }) | ||
| } | ||
|
|
||
| override def prettyName: String = operationName + "_interval" | ||
| } | ||
|
|
||
| case class MultiplyInterval(interval: Expression, num: Expression) | ||
|
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. https://github.com/apache/spark/pull/26345/files#diff-b83497f7bc11578a0b63a814a2a30f48R2198-R2207
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. This should be added for only expressions registered as functions. |
||
| extends IntervalNumOperation( | ||
| interval, | ||
| num, | ||
| (i: CalendarInterval, n: Double) => i.multiply(n), | ||
| "multiply") | ||
|
|
||
| case class DivideInterval(interval: Expression, num: Expression) | ||
|
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. |
||
| extends IntervalNumOperation( | ||
| interval, | ||
| num, | ||
| (i: CalendarInterval, n: Double) => i.divide(n), | ||
| "divide") | ||
Uh oh!
There was an error while loading. Please reload this page.