-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-29532][SQL] Simplify interval string parsing #26190
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 1 commit
c8d81e3
87417d9
a5f355f
10aa751
5c5e251
03e90e4
836e868
33ceedc
f759fd5
77f69a1
48b7ef4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -159,65 +159,60 @@ public static CalendarInterval fromDayTimeString(String s, String from, String t | |
| return result; | ||
| } | ||
|
|
||
| public static CalendarInterval fromSingleUnitString(String unit, String s) | ||
| public static CalendarInterval fromUnitString(String[] units, String[] values) | ||
| throws IllegalArgumentException { | ||
| assert units.length == values.length; | ||
| int months = 0; | ||
| long microseconds = 0; | ||
|
|
||
| CalendarInterval result = null; | ||
| if (s == null) { | ||
| throw new IllegalArgumentException(String.format("Interval %s string was null", unit)); | ||
| } | ||
| s = s.trim(); | ||
| try { | ||
| switch (unit) { | ||
| case "year": | ||
| int year = (int) toLongWithRange("year", s, | ||
| Integer.MIN_VALUE / 12, Integer.MAX_VALUE / 12); | ||
| result = new CalendarInterval(year * 12, 0L); | ||
| break; | ||
| case "month": | ||
| int month = (int) toLongWithRange("month", s, | ||
| Integer.MIN_VALUE, Integer.MAX_VALUE); | ||
| result = new CalendarInterval(month, 0L); | ||
| break; | ||
| case "week": | ||
| long week = toLongWithRange("week", s, | ||
| Long.MIN_VALUE / MICROS_PER_WEEK, Long.MAX_VALUE / MICROS_PER_WEEK); | ||
| result = new CalendarInterval(0, week * MICROS_PER_WEEK); | ||
| break; | ||
| case "day": | ||
| long day = toLongWithRange("day", s, | ||
| Long.MIN_VALUE / MICROS_PER_DAY, Long.MAX_VALUE / MICROS_PER_DAY); | ||
| result = new CalendarInterval(0, day * MICROS_PER_DAY); | ||
| break; | ||
| case "hour": | ||
| long hour = toLongWithRange("hour", s, | ||
| Long.MIN_VALUE / MICROS_PER_HOUR, Long.MAX_VALUE / MICROS_PER_HOUR); | ||
| result = new CalendarInterval(0, hour * MICROS_PER_HOUR); | ||
| break; | ||
| case "minute": | ||
| long minute = toLongWithRange("minute", s, | ||
| Long.MIN_VALUE / MICROS_PER_MINUTE, Long.MAX_VALUE / MICROS_PER_MINUTE); | ||
| result = new CalendarInterval(0, minute * MICROS_PER_MINUTE); | ||
| break; | ||
| case "second": { | ||
| long micros = parseSecondNano(s); | ||
| result = new CalendarInterval(0, micros); | ||
| break; | ||
| for (int i = 0; i < units.length; i++) { | ||
| try { | ||
| String value = values[i].trim(); | ||
|
||
| switch (units[i]) { | ||
| case "year": | ||
| months = Math.addExact(months, Math.multiplyExact(Integer.parseInt(value), 12)); | ||
| break; | ||
| case "month": | ||
| months = Math.addExact(months, Integer.parseInt(value)); | ||
srowen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| break; | ||
| case "week": | ||
| microseconds = Math.addExact( | ||
| microseconds, | ||
| Math.multiplyExact(Long.parseLong(value), MICROS_PER_WEEK)); | ||
| break; | ||
| case "day": | ||
| microseconds = Math.addExact( | ||
| microseconds, | ||
| Math.multiplyExact(Long.parseLong(value), MICROS_PER_DAY)); | ||
| break; | ||
| case "hour": | ||
| microseconds = Math.addExact( | ||
| microseconds, | ||
| Math.multiplyExact(Long.parseLong(value), MICROS_PER_HOUR)); | ||
| break; | ||
| case "minute": | ||
| microseconds = Math.addExact( | ||
| microseconds, | ||
| Math.multiplyExact(Long.parseLong(value), MICROS_PER_MINUTE)); | ||
| break; | ||
| case "second": { | ||
| microseconds = Math.addExact(microseconds, parseSecondNano(value)); | ||
| break; | ||
| } | ||
| case "millisecond": | ||
| microseconds = Math.addExact( | ||
| microseconds, | ||
| Math.multiplyExact(Long.parseLong(value), MICROS_PER_MILLI)); | ||
| break; | ||
| case "microsecond": | ||
| microseconds = Math.addExact(microseconds, Long.parseLong(value)); | ||
| break; | ||
| } | ||
| case "millisecond": | ||
| long millisecond = toLongWithRange("millisecond", s, | ||
| Long.MIN_VALUE / MICROS_PER_MILLI, Long.MAX_VALUE / MICROS_PER_MILLI); | ||
| result = new CalendarInterval(0, millisecond * MICROS_PER_MILLI); | ||
| break; | ||
| case "microsecond": | ||
| long micros = Long.parseLong(s); | ||
| result = new CalendarInterval(0, micros); | ||
| break; | ||
| } catch (Exception e) { | ||
| throw new IllegalArgumentException("Error parsing interval string: " + e.getMessage(), e); | ||
| } | ||
| } catch (Exception e) { | ||
| throw new IllegalArgumentException("Error parsing interval string: " + e.getMessage(), e); | ||
| } | ||
| return result; | ||
| return new CalendarInterval(months, microseconds); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,10 +103,11 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging | |
|
|
||
| override def visitSingleInterval(ctx: SingleIntervalContext): CalendarInterval = { | ||
| withOrigin(ctx) { | ||
| val intervals = ctx.intervalField.asScala.map(visitIntervalField) | ||
| validate(intervals.nonEmpty, | ||
| "at least one time unit should be given for interval literal", ctx) | ||
| intervals.reduce(_.add(_)) | ||
| val units = ctx.intervalUnit().asScala.map { | ||
| u => normalizeInternalUnit(u.getText.toLowerCase(Locale.ROOT)) | ||
| }.toArray | ||
| val values = ctx.intervalValue().asScala.map(getIntervalValue).toArray | ||
| CalendarInterval.fromUnitString(units, values) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1940,18 +1941,12 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging | |
| */ | ||
| override def visitIntervalField(ctx: IntervalFieldContext): CalendarInterval = withOrigin(ctx) { | ||
| import ctx._ | ||
| val s = if (value.STRING() != null) { | ||
| string(value.STRING()) | ||
| } else { | ||
| value.getText | ||
| } | ||
| val s = getIntervalValue(value) | ||
| try { | ||
| val unitText = unit.getText.toLowerCase(Locale.ROOT) | ||
| val interval = (unitText, Option(to).map(_.getText.toLowerCase(Locale.ROOT))) match { | ||
| case (u, None) => | ||
| // Handle plural forms, e.g: yearS/monthS/weekS/dayS/hourS/minuteS/hourS/... | ||
| val unit = if (u.endsWith("s")) u.substring(0, u.length - 1) else u | ||
| CalendarInterval.fromSingleUnitString(unit, s) | ||
| CalendarInterval.fromUnitString(Array(normalizeInternalUnit(u)), Array(s)) | ||
|
||
| case ("year", Some("month")) => | ||
| CalendarInterval.fromYearMonthString(s) | ||
| case ("day", Some("hour")) => | ||
|
|
@@ -1980,6 +1975,19 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging | |
| } | ||
| } | ||
|
|
||
| private def getIntervalValue(value: IntervalValueContext): String = { | ||
| if (value.STRING() != null) { | ||
| string(value.STRING()) | ||
| } else { | ||
| value.getText | ||
| } | ||
| } | ||
|
|
||
| // Handle plural forms, e.g: yearS/monthS/weekS/dayS/hourS/minuteS/hourS/... | ||
| private def normalizeInternalUnit(s: String): String = { | ||
| if (s.endsWith("s")) s.substring(0, s.length - 1) else s | ||
| } | ||
|
|
||
| /* ******************************************************************************************** | ||
| * DataType parsing | ||
| * ******************************************************************************************** */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,6 @@ trait ParserInterface { | |
| /** | ||
| * Parse a string to a [[CalendarInterval]]. | ||
| */ | ||
| @throws[ParseException]("Text cannot be parsed to a DataType") | ||
| @throws[ParseException]("Text cannot be parsed to an interval") | ||
|
||
| def parseInterval(sqlText: String): CalendarInterval | ||
|
||
| } | ||
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.
nit:
fromUnitStrings?