Skip to content

Commit 25d069f

Browse files
committed
Merge remote-tracking branch 'origin/main' into rel-coerc
2 parents 93a56ab + 30aba65 commit 25d069f

22 files changed

Lines changed: 525 additions & 475 deletions

File tree

core/src/main/java/org/opensearch/sql/calcite/ExtendedRexBuilder.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55

66
package org.opensearch.sql.calcite;
77

8+
import static org.opensearch.sql.calcite.utils.OpenSearchTypeFactory.ExprUDT.EXPR_DATE;
9+
import static org.opensearch.sql.calcite.utils.OpenSearchTypeFactory.ExprUDT.EXPR_TIME;
10+
import static org.opensearch.sql.calcite.utils.OpenSearchTypeFactory.ExprUDT.EXPR_TIMESTAMP;
11+
812
import com.google.common.collect.ImmutableList;
913
import java.util.Arrays;
1014
import java.util.List;
15+
import java.util.Set;
1116
import org.apache.calcite.avatica.util.TimeUnit;
1217
import org.apache.calcite.rel.type.RelDataType;
1318
import org.apache.calcite.rex.RexBuilder;
@@ -19,6 +24,8 @@
1924
import org.apache.calcite.sql.type.SqlTypeName;
2025
import org.apache.calcite.sql.type.SqlTypeUtil;
2126
import org.opensearch.sql.ast.expression.SpanUnit;
27+
import org.opensearch.sql.calcite.type.ExprSqlType;
28+
import org.opensearch.sql.expression.function.PPLBuiltinOperators;
2229

2330
public class ExtendedRexBuilder extends RexBuilder {
2431

@@ -117,6 +124,16 @@ public RexNode makeCast(
117124
// SqlStdOperatorTable.NOT_EQUALS,
118125
// ImmutableList.of(exp, makeZeroLiteral(exp.getType())));
119126
}
127+
} else if (type instanceof ExprSqlType exprSqlType
128+
&& Set.of(EXPR_DATE, EXPR_TIME, EXPR_TIMESTAMP).contains(exprSqlType.getUdt())) {
129+
switch (exprSqlType.getUdt()) {
130+
case EXPR_DATE:
131+
return makeCall(type, PPLBuiltinOperators.DATE, List.of(exp));
132+
case EXPR_TIME:
133+
return makeCall(type, PPLBuiltinOperators.TIME, List.of(exp));
134+
case EXPR_TIMESTAMP:
135+
return makeCall(type, PPLBuiltinOperators.TIMESTAMP, List.of(exp));
136+
}
120137
}
121138
return super.makeCast(pos, type, exp, matchNullability, safe, format);
122139
}

core/src/main/java/org/opensearch/sql/calcite/utils/datetime/DateTimeConversionUtils.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import org.apache.calcite.avatica.util.TimeUnit;
1212
import org.opensearch.sql.data.model.*;
1313
import org.opensearch.sql.data.type.ExprCoreType;
14-
import org.opensearch.sql.exception.SemanticCheckException;
14+
import org.opensearch.sql.exception.ExpressionEvaluationException;
1515
import org.opensearch.sql.expression.function.FunctionProperties;
1616

1717
public final class DateTimeConversionUtils {
@@ -36,7 +36,7 @@ public static ExprTimestampValue forceConvertToTimestampValue(
3636
ExprValueUtils.timestampValue(timeValue.timestampValue(properties));
3737
case ExprStringValue stringValue -> new ExprTimestampValue(
3838
DateTimeParser.parse(stringValue.stringValue()));
39-
default -> throw new SemanticCheckException(
39+
default -> throw new ExpressionEvaluationException(
4040
String.format(
4141
"Cannot convert %s to timestamp, only STRING, DATE, TIME and TIMESTAMP are supported",
4242
value.type()));
@@ -63,8 +63,8 @@ public static ExprTimestampValue convertToTimestampValue(
6363
} else {
6464
try {
6565
return new ExprTimestampValue(value.timestampValue());
66-
} catch (SemanticCheckException e) {
67-
throw new SemanticCheckException(
66+
} catch (ExpressionEvaluationException e) {
67+
throw new ExpressionEvaluationException(
6868
String.format(
6969
"Cannot convert %s to timestamp, only STRING, DATE, TIME and TIMESTAMP are"
7070
+ " supported",
@@ -98,7 +98,7 @@ public static ExprDateValue convertToDateValue(ExprValue value, FunctionProperti
9898
return new ExprDateValue(value.stringValue());
9999
}
100100
default -> {
101-
throw new SemanticCheckException(
101+
throw new ExpressionEvaluationException(
102102
String.format(
103103
"Cannot convert %s to date, only STRING, DATE, TIME and TIMESTAMP are supported",
104104
value.type()));

core/src/main/java/org/opensearch/sql/calcite/utils/datetime/DateTimeParser.java

Lines changed: 13 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -5,127 +5,38 @@
55

66
package org.opensearch.sql.calcite.utils.datetime;
77

8-
import com.google.common.collect.ImmutableList;
98
import java.time.LocalDate;
109
import java.time.LocalDateTime;
1110
import java.time.LocalTime;
1211
import java.time.ZoneId;
13-
import java.time.format.DateTimeFormatter;
14-
import java.util.List;
15-
import org.opensearch.sql.exception.SemanticCheckException;
12+
import java.time.format.DateTimeParseException;
13+
import java.util.Locale;
1614
import org.opensearch.sql.utils.DateTimeFormatters;
1715

1816
public interface DateTimeParser {
1917
/**
20-
* Parse a string into a LocalDateTime If only date is found, time is set to 00:00:00. If only
21-
* time is found, date is set to today.
18+
* Parse a string into a LocalDateTime. If only date is found, time is set to 00:00:00. If only
19+
* time is found, date is set to today at UTC.
2220
*
2321
* @param input A date/time/timestamp string
2422
* @return A LocalDateTime
2523
* @throws IllegalArgumentException if parsing fails
2624
*/
2725
static LocalDateTime parse(String input) {
28-
29-
if (input == null || input.trim().isEmpty()) {
30-
throw new SemanticCheckException("Cannot parse a null/empty date-time string.");
31-
}
32-
33-
if (input.contains(":")) {
34-
try {
35-
return parseTimestamp(input);
36-
} catch (Exception ignored) {
37-
}
38-
39-
try {
40-
LocalTime t = parseTime(input);
41-
return LocalDateTime.of(LocalDate.now(ZoneId.of("UTC")), t);
42-
} catch (Exception ignored) {
43-
}
44-
} else {
45-
try {
46-
LocalDate d = parseDate(input);
47-
return d.atStartOfDay();
48-
} catch (Exception ignored) {
49-
}
50-
}
51-
throw new SemanticCheckException(String.format("Unable to parse %s as datetime", input));
52-
}
53-
54-
static LocalDateTime parseTimeOrTimestamp(String input) {
55-
if (input == null || input.trim().isEmpty()) {
56-
throw new SemanticCheckException("Cannot parse a null/empty date-time string.");
57-
}
58-
5926
try {
60-
return parseTime(input).atDate(LocalDate.now(ZoneId.of("UTC")));
61-
} catch (Exception ignored) {
27+
return LocalDateTime.parse(input, DateTimeFormatters.DATE_TIMESTAMP_FORMATTER);
28+
} catch (DateTimeParseException ignored) {
6229
}
6330

6431
try {
65-
return parseTimestamp(input);
32+
LocalTime t = LocalTime.parse(input, DateTimeFormatters.TIME_TIMESTAMP_FORMATTER);
33+
return LocalDateTime.of(LocalDate.now(ZoneId.of("UTC")), t);
6634
} catch (Exception ignored) {
35+
throw new IllegalArgumentException(
36+
String.format(
37+
Locale.ROOT,
38+
"timestamp:%s in unsupported format, please use 'yyyy-MM-dd HH:mm:ss[.SSSSSSSSS]'",
39+
input));
6740
}
68-
69-
throw new SemanticCheckException(
70-
String.format("time:%s in unsupported format, please use 'HH:mm:ss[.SSSSSSSSS]'", input));
71-
}
72-
73-
static LocalDateTime parseDateOrTimestamp(String input) {
74-
if (input == null || input.trim().isEmpty()) {
75-
throw new SemanticCheckException("Cannot parse a null/empty date-time string.");
76-
}
77-
78-
try {
79-
return parseDate(input).atStartOfDay();
80-
} catch (Exception ignored) {
81-
}
82-
83-
try {
84-
return parseTimestamp(input);
85-
} catch (Exception ignored) {
86-
}
87-
88-
throw new SemanticCheckException(
89-
String.format("date:%s in unsupported format, please use 'yyyy-MM-dd'", input));
90-
}
91-
92-
static LocalDateTime parseTimestamp(String input) {
93-
List<DateTimeFormatter> dateTimeFormatters =
94-
ImmutableList.of(DateTimeFormatters.DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL);
95-
96-
for (DateTimeFormatter fmt : dateTimeFormatters) {
97-
try {
98-
return LocalDateTime.parse(input, fmt);
99-
} catch (Exception ignored) {
100-
}
101-
}
102-
throw new SemanticCheckException(
103-
String.format(
104-
"timestamp:%s in unsupported format, please use 'yyyy-MM-dd HH:mm:ss[.SSSSSSSSS]'",
105-
input));
106-
}
107-
108-
static LocalTime parseTime(String input) {
109-
List<DateTimeFormatter> timeFormatters = ImmutableList.of(DateTimeFormatter.ISO_TIME);
110-
for (DateTimeFormatter fmt : timeFormatters) {
111-
try {
112-
return LocalTime.parse(input, fmt);
113-
} catch (Exception ignored) {
114-
}
115-
}
116-
throw new SemanticCheckException(
117-
String.format("time:%s in unsupported format, please use 'HH:mm:ss[.SSSSSSSSS]'", input));
118-
}
119-
120-
static LocalDate parseDate(String input) {
121-
List<DateTimeFormatter> dateFormatters = ImmutableList.of(DateTimeFormatter.ISO_DATE);
122-
for (DateTimeFormatter fmt : dateFormatters) {
123-
try {
124-
return LocalDate.parse(input, fmt);
125-
} catch (Exception ignored) {
126-
}
127-
}
128-
throw new SemanticCheckException(
129-
String.format("date:%s in unsupported format, please use 'yyyy-MM-dd'", input));
13041
}
13142
}

core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
package org.opensearch.sql.data.model;
77

8-
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL;
9-
108
import com.google.common.base.Objects;
119
import java.time.Instant;
1210
import java.time.LocalDate;
@@ -18,20 +16,25 @@
1816
import lombok.RequiredArgsConstructor;
1917
import org.opensearch.sql.data.type.ExprCoreType;
2018
import org.opensearch.sql.data.type.ExprType;
21-
import org.opensearch.sql.exception.SemanticCheckException;
19+
import org.opensearch.sql.exception.ExpressionEvaluationException;
20+
import org.opensearch.sql.utils.DateTimeFormatters;
2221

2322
/** Expression Date Value. */
2423
@RequiredArgsConstructor
2524
public class ExprDateValue extends AbstractExprValue {
2625

2726
private final LocalDate date;
2827

29-
/** Constructor of ExprDateValue. */
28+
/**
29+
* Constructor with date string.
30+
*
31+
* @param date a date or timestamp string (does not accept time string)
32+
*/
3033
public ExprDateValue(String date) {
3134
try {
32-
this.date = LocalDate.parse(date, DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL);
35+
this.date = LocalDate.parse(date, DateTimeFormatters.DATE_TIMESTAMP_FORMATTER);
3336
} catch (DateTimeParseException e) {
34-
throw new SemanticCheckException(
37+
throw new ExpressionEvaluationException(
3538
String.format("date:%s in unsupported format, please use 'yyyy-MM-dd'", date));
3639
}
3740
}

core/src/main/java/org/opensearch/sql/data/model/ExprStringValue.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import lombok.RequiredArgsConstructor;
1414
import org.opensearch.sql.data.type.ExprCoreType;
1515
import org.opensearch.sql.data.type.ExprType;
16-
import org.opensearch.sql.exception.SemanticCheckException;
16+
import org.opensearch.sql.exception.ExpressionEvaluationException;
1717

1818
/** Expression String Value. */
1919
@RequiredArgsConstructor
@@ -39,7 +39,7 @@ public String stringValue() {
3939
public Instant timestampValue() {
4040
try {
4141
return new ExprTimestampValue(value).timestampValue();
42-
} catch (SemanticCheckException e) {
42+
} catch (ExpressionEvaluationException e) {
4343
return new ExprTimestampValue(
4444
LocalDateTime.of(new ExprDateValue(value).dateValue(), LocalTime.of(0, 0, 0)))
4545
.timestampValue();
@@ -50,7 +50,7 @@ public Instant timestampValue() {
5050
public LocalDate dateValue() {
5151
try {
5252
return new ExprTimestampValue(value).dateValue();
53-
} catch (SemanticCheckException e) {
53+
} catch (ExpressionEvaluationException e) {
5454
return new ExprDateValue(value).dateValue();
5555
}
5656
}
@@ -59,7 +59,7 @@ public LocalDate dateValue() {
5959
public LocalTime timeValue() {
6060
try {
6161
return new ExprTimestampValue(value).timeValue();
62-
} catch (SemanticCheckException e) {
62+
} catch (ExpressionEvaluationException e) {
6363
return new ExprTimeValue(value).timeValue();
6464
}
6565
}

core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package org.opensearch.sql.data.model;
77

88
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
9-
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL;
109

1110
import java.time.Instant;
1211
import java.time.LocalDate;
@@ -18,21 +17,26 @@
1817
import lombok.RequiredArgsConstructor;
1918
import org.opensearch.sql.data.type.ExprCoreType;
2019
import org.opensearch.sql.data.type.ExprType;
21-
import org.opensearch.sql.exception.SemanticCheckException;
20+
import org.opensearch.sql.exception.ExpressionEvaluationException;
2221
import org.opensearch.sql.expression.function.FunctionProperties;
22+
import org.opensearch.sql.utils.DateTimeFormatters;
2323

2424
/** Expression Time Value. */
2525
@RequiredArgsConstructor
2626
public class ExprTimeValue extends AbstractExprValue {
2727

2828
private final LocalTime time;
2929

30-
/** Constructor of ExprTimeValue. */
30+
/**
31+
* Constructor with time string.
32+
*
33+
* @param time a time or timestamp string (does not accept date string)
34+
*/
3135
public ExprTimeValue(String time) {
3236
try {
33-
this.time = LocalTime.parse(time, DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL);
37+
this.time = LocalTime.parse(time, DateTimeFormatters.TIME_TIMESTAMP_FORMATTER);
3438
} catch (DateTimeParseException e) {
35-
throw new SemanticCheckException(
39+
throw new ExpressionEvaluationException(
3640
String.format("time:%s in unsupported format, please use 'HH:mm:ss[.SSSSSSSSS]'", time));
3741
}
3842
}

core/src/main/java/org/opensearch/sql/data/model/ExprTimestampValue.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,27 @@
1919
import lombok.RequiredArgsConstructor;
2020
import org.opensearch.sql.data.type.ExprCoreType;
2121
import org.opensearch.sql.data.type.ExprType;
22-
import org.opensearch.sql.exception.SemanticCheckException;
22+
import org.opensearch.sql.exception.ExpressionEvaluationException;
23+
import org.opensearch.sql.utils.DateTimeFormatters;
2324

2425
/** Expression Timestamp Value. */
2526
@RequiredArgsConstructor
2627
public class ExprTimestampValue extends AbstractExprValue {
2728

2829
private final Instant timestamp;
2930

30-
/** Constructor. */
31+
/**
32+
* Constructor with timestamp string.
33+
*
34+
* @param timestamp a date or timestamp string (does not accept time string)
35+
*/
3136
public ExprTimestampValue(String timestamp) {
3237
try {
3338
this.timestamp =
34-
LocalDateTime.parse(timestamp, DATE_TIME_FORMATTER_VARIABLE_NANOS)
35-
.atZone(ZoneOffset.UTC)
36-
.toInstant();
39+
LocalDateTime.parse(timestamp, DateTimeFormatters.DATE_TIMESTAMP_FORMATTER)
40+
.toInstant(ZoneOffset.UTC);
3741
} catch (DateTimeParseException e) {
38-
throw new SemanticCheckException(
42+
throw new ExpressionEvaluationException(
3943
String.format(
4044
"timestamp:%s in unsupported format, please use 'yyyy-MM-dd HH:mm:ss[.SSSSSSSSS]'",
4145
timestamp));

core/src/main/java/org/opensearch/sql/utils/DateTimeFormatters.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ public class DateTimeFormatters {
129129
.toFormatter(Locale.ROOT)
130130
.withResolverStyle(ResolverStyle.STRICT);
131131

132+
public static final DateTimeFormatter DATE_TIMESTAMP_FORMATTER =
133+
new DateTimeFormatterBuilder()
134+
.appendPattern("[uuuu-MM-dd HH:mm:ss][uuuu-MM-dd HH:mm][uuuu-MM-dd]")
135+
.appendFraction(
136+
ChronoField.NANO_OF_SECOND, MIN_FRACTION_SECONDS, MAX_FRACTION_SECONDS, true)
137+
.parseDefaulting(HOUR_OF_DAY, 0)
138+
.parseDefaulting(MINUTE_OF_HOUR, 0)
139+
.parseDefaulting(SECOND_OF_MINUTE, 0)
140+
.toFormatter(Locale.ROOT)
141+
.withResolverStyle(ResolverStyle.STRICT);
142+
143+
public static final DateTimeFormatter TIME_TIMESTAMP_FORMATTER =
144+
new DateTimeFormatterBuilder()
145+
.appendPattern("[uuuu-MM-dd HH:mm:ss][uuuu-MM-dd HH:mm][HH:mm:ss][HH:mm]")
146+
.appendFraction(
147+
ChronoField.NANO_OF_SECOND, MIN_FRACTION_SECONDS, MAX_FRACTION_SECONDS, true)
148+
.parseDefaulting(SECOND_OF_MINUTE, 0)
149+
.toFormatter(Locale.ROOT)
150+
.withResolverStyle(ResolverStyle.STRICT);
151+
132152
// MDD
133153
public static final DateTimeFormatter DATE_FORMATTER_SINGLE_DIGIT_MONTH =
134154
new DateTimeFormatterBuilder()

0 commit comments

Comments
 (0)