-
Notifications
You must be signed in to change notification settings - Fork 24.7k
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
SQL: Replace joda with java time #38437
Changes from 5 commits
af51ec6
bc77167
88ef084
459c8db
904c0c8
8e06556
cf9dc6b
e37ddc5
153fa09
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 |
---|---|---|
|
@@ -10,17 +10,18 @@ | |
import org.elasticsearch.common.xcontent.XContentHelper; | ||
import org.elasticsearch.common.xcontent.json.JsonXContent; | ||
import org.elasticsearch.test.ESTestCase; | ||
import org.joda.time.DateTime; | ||
import org.joda.time.ReadableDateTime; | ||
|
||
import java.sql.Timestamp; | ||
import java.time.Clock; | ||
import java.time.Duration; | ||
import java.time.ZoneId; | ||
import java.time.ZonedDateTime; | ||
|
||
import static org.hamcrest.Matchers.instanceOf; | ||
|
||
|
||
public class TypeConverterTests extends ESTestCase { | ||
|
||
|
||
public void testFloatAsNative() throws Exception { | ||
assertThat(convertAsNative(42.0f, EsType.FLOAT), instanceOf(Float.class)); | ||
assertThat(convertAsNative(42.0, EsType.FLOAT), instanceOf(Float.class)); | ||
|
@@ -40,21 +41,17 @@ public void testDoubleAsNative() throws Exception { | |
} | ||
|
||
public void testTimestampAsNative() throws Exception { | ||
DateTime now = DateTime.now(); | ||
ZonedDateTime now = ZonedDateTime.now(Clock.tick(Clock.system(ZoneId.of("Z")), Duration.ofMillis(1))); | ||
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. The same approach is used here maybe you can re-use that method. 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. Also, 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. jdbc module doesn't have access to |
||
assertThat(convertAsNative(now, EsType.DATETIME), instanceOf(Timestamp.class)); | ||
assertEquals(now.getMillis(), ((Timestamp) convertAsNative(now, EsType.DATETIME)).getTime()); | ||
assertEquals(now.toInstant().toEpochMilli(), ((Timestamp) convertAsNative(now, EsType.DATETIME)).getTime()); | ||
} | ||
|
||
private Object convertAsNative(Object value, EsType type) throws Exception { | ||
// Simulate sending over XContent | ||
XContentBuilder builder = JsonXContent.contentBuilder(); | ||
builder.startObject(); | ||
builder.field("value"); | ||
if (value instanceof ReadableDateTime) { | ||
builder.value(((ReadableDateTime) value).getMillis()); | ||
} else { | ||
builder.value(value); | ||
} | ||
builder.value(value); | ||
builder.endObject(); | ||
builder.close(); | ||
Object copy = XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2().get("value"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,6 @@ | |
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; | ||
import org.elasticsearch.xpack.sql.type.DataType; | ||
import org.elasticsearch.xpack.sql.util.DateUtils; | ||
import org.joda.time.DateTime; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayDeque; | ||
|
@@ -132,10 +131,6 @@ private Object unwrapMultiValue(Object values) { | |
if (values instanceof String) { | ||
return DateUtils.asDateTime(Long.parseLong(values.toString())); | ||
} | ||
// returned by nested types... | ||
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. @costin I'm not sure about this, but doesn't seem to break something. Should we check more in depth and add tests? 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. It can be removed now with Joda out. Likely this will eliminate |
||
if (values instanceof DateTime) { | ||
return DateUtils.asDateTime((DateTime) values); | ||
} | ||
} | ||
if (values instanceof Long || values instanceof Double || values instanceof String || values instanceof Boolean) { | ||
return values; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,25 +111,25 @@ | |
import org.elasticsearch.xpack.sql.type.DataType; | ||
import org.elasticsearch.xpack.sql.type.DataTypeConversion; | ||
import org.elasticsearch.xpack.sql.type.DataTypes; | ||
import org.elasticsearch.xpack.sql.util.DateUtils; | ||
import org.elasticsearch.xpack.sql.util.StringUtils; | ||
import org.joda.time.DateTime; | ||
import org.joda.time.format.DateTimeFormatter; | ||
import org.joda.time.format.DateTimeFormatterBuilder; | ||
import org.joda.time.format.ISODateTimeFormat; | ||
|
||
import java.time.Duration; | ||
import java.time.LocalTime; | ||
import java.time.Period; | ||
import java.time.format.DateTimeParseException; | ||
import java.time.temporal.TemporalAmount; | ||
import java.util.EnumSet; | ||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
import java.util.StringJoiner; | ||
|
||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME; | ||
import static java.util.Collections.emptyList; | ||
import static java.util.Collections.singletonList; | ||
import static org.elasticsearch.xpack.sql.type.DataTypeConversion.conversionFor; | ||
import static org.elasticsearch.xpack.sql.util.DateUtils.asDateOnly; | ||
import static org.elasticsearch.xpack.sql.util.DateUtils.ofEscapedLiteral; | ||
|
||
abstract class ExpressionBuilder extends IdentifierBuilder { | ||
|
||
|
@@ -791,13 +791,11 @@ public Literal visitDateEscapedLiteral(DateEscapedLiteralContext ctx) { | |
String string = string(ctx.string()); | ||
Source source = source(ctx); | ||
// parse yyyy-MM-dd | ||
DateTime dt = null; | ||
try { | ||
dt = ISODateTimeFormat.date().parseDateTime(string); | ||
} catch(IllegalArgumentException ex) { | ||
return new Literal(source, asDateOnly(string), DataType.DATE); | ||
} catch(DateTimeParseException ex) { | ||
throw new ParsingException(source, "Invalid date received; {}", ex.getMessage()); | ||
} | ||
return new Literal(source, DateUtils.asDateOnly(dt), DataType.DATE); | ||
} | ||
|
||
@Override | ||
|
@@ -806,10 +804,10 @@ public Literal visitTimeEscapedLiteral(TimeEscapedLiteralContext ctx) { | |
Source source = source(ctx); | ||
|
||
// parse HH:mm:ss | ||
DateTime dt = null; | ||
LocalTime lt = null; | ||
try { | ||
dt = ISODateTimeFormat.hourMinuteSecond().parseDateTime(string); | ||
} catch (IllegalArgumentException ex) { | ||
lt = LocalTime.parse(string, ISO_LOCAL_TIME); | ||
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. I see ISO_LOCAL_TIME deals, also, with format as 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. Will dive into that when we implement the |
||
} catch (DateTimeParseException ex) { | ||
throw new ParsingException(source, "Invalid time received; {}", ex.getMessage()); | ||
} | ||
|
||
|
@@ -822,18 +820,11 @@ public Literal visitTimestampEscapedLiteral(TimestampEscapedLiteralContext ctx) | |
|
||
Source source = source(ctx); | ||
// parse yyyy-mm-dd hh:mm:ss(.f...) | ||
DateTime dt = null; | ||
try { | ||
DateTimeFormatter formatter = new DateTimeFormatterBuilder() | ||
.append(ISODateTimeFormat.date()) | ||
.appendLiteral(" ") | ||
.append(ISODateTimeFormat.hourMinuteSecondFraction()) | ||
.toFormatter(); | ||
dt = formatter.parseDateTime(string); | ||
} catch (IllegalArgumentException ex) { | ||
return new Literal(source, ofEscapedLiteral(string), DataType.DATETIME); | ||
} catch (DateTimeParseException ex) { | ||
throw new ParsingException(source, "Invalid timestamp received; {}", ex.getMessage()); | ||
} | ||
return new Literal(source, DateUtils.asDateTime(dt), DataType.DATETIME); | ||
} | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,29 +6,35 @@ | |
|
||
package org.elasticsearch.xpack.sql.util; | ||
|
||
import org.elasticsearch.common.time.DateFormatter; | ||
import org.elasticsearch.common.time.DateFormatters; | ||
import org.elasticsearch.xpack.sql.proto.StringUtils; | ||
import org.joda.time.DateTime; | ||
import org.joda.time.format.DateTimeFormatter; | ||
import org.joda.time.format.ISODateTimeFormat; | ||
|
||
import java.time.Instant; | ||
import java.time.LocalDateTime; | ||
import java.time.LocalDate; | ||
import java.time.ZoneId; | ||
import java.time.ZoneOffset; | ||
import java.time.ZonedDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
import java.time.format.DateTimeFormatterBuilder; | ||
|
||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; | ||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME; | ||
|
||
public final class DateUtils { | ||
|
||
private static final long DAY_IN_MILLIS = 60 * 60 * 24 * 1000; | ||
|
||
// TODO: do we have a java.time based parser we can use instead? | ||
private static final DateTimeFormatter UTC_DATE_FORMATTER = ISODateTimeFormat.dateOptionalTimeParser().withZoneUTC(); | ||
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. would it be the same if we use DateFormatters.forPattern("date_optional_time") ? 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. It might work (see the TODO above). 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 I use the
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. So I need the same formatter but with:
|
||
|
||
public static final ZoneId UTC = ZoneId.of("Z"); | ||
public static final String DATE_PARSE_FORMAT = "epoch_millis"; | ||
|
||
private static final DateTimeFormatter DATE_TIME_ESCAPED_LITERAL_FORMATTER = new DateTimeFormatterBuilder() | ||
.append(ISO_LOCAL_DATE) | ||
.appendLiteral(" ") | ||
.append(ISO_LOCAL_TIME) | ||
.toFormatter().withZone(UTC); | ||
|
||
private static final DateFormatter UTC_DATE_TIME_FORMATTER = DateFormatter.forPattern("date_optional_time").withZone(UTC); | ||
|
||
private static final long DAY_IN_MILLIS = 60 * 60 * 24 * 1000L; | ||
|
||
private DateUtils() {} | ||
|
||
/** | ||
|
@@ -56,22 +62,7 @@ public static ZonedDateTime asDateTime(long millis, ZoneId id) { | |
* Parses the given string into a Date (SQL DATE type) using UTC as a default timezone. | ||
*/ | ||
public static ZonedDateTime asDateOnly(String dateFormat) { | ||
return asDateOnly(UTC_DATE_FORMATTER.parseDateTime(dateFormat)); | ||
} | ||
|
||
public static ZonedDateTime asDateOnly(DateTime dateTime) { | ||
LocalDateTime ldt = LocalDateTime.of( | ||
dateTime.getYear(), | ||
dateTime.getMonthOfYear(), | ||
dateTime.getDayOfMonth(), | ||
0, | ||
0, | ||
0, | ||
0); | ||
|
||
return ZonedDateTime.ofStrict(ldt, | ||
ZoneOffset.ofTotalSeconds(dateTime.getZone().getOffset(dateTime) / 1000), | ||
org.elasticsearch.common.time.DateUtils.dateTimeZoneToZoneId(dateTime.getZone())); | ||
return LocalDate.parse(dateFormat, ISO_LOCAL_DATE).atStartOfDay(UTC); | ||
} | ||
|
||
public static ZonedDateTime asDateOnly(ZonedDateTime zdt) { | ||
|
@@ -82,25 +73,13 @@ public static ZonedDateTime asDateOnly(ZonedDateTime zdt) { | |
* Parses the given string into a DateTime using UTC as a default timezone. | ||
*/ | ||
public static ZonedDateTime asDateTime(String dateFormat) { | ||
return asDateTime(UTC_DATE_FORMATTER.parseDateTime(dateFormat)); | ||
return DateFormatters.from(UTC_DATE_TIME_FORMATTER.parse(dateFormat)).withZoneSameInstant(UTC); | ||
} | ||
|
||
public static ZonedDateTime asDateTime(DateTime dateTime) { | ||
LocalDateTime ldt = LocalDateTime.of( | ||
dateTime.getYear(), | ||
dateTime.getMonthOfYear(), | ||
dateTime.getDayOfMonth(), | ||
dateTime.getHourOfDay(), | ||
dateTime.getMinuteOfHour(), | ||
dateTime.getSecondOfMinute(), | ||
dateTime.getMillisOfSecond() * 1_000_000); | ||
|
||
return ZonedDateTime.ofStrict(ldt, | ||
ZoneOffset.ofTotalSeconds(dateTime.getZone().getOffset(dateTime) / 1000), | ||
org.elasticsearch.common.time.DateUtils.dateTimeZoneToZoneId(dateTime.getZone())); | ||
public static ZonedDateTime ofEscapedLiteral(String dateFormat) { | ||
return ZonedDateTime.parse(dateFormat, DATE_TIME_ESCAPED_LITERAL_FORMATTER.withZone(UTC)); | ||
} | ||
|
||
|
||
public static String toString(ZonedDateTime dateTime) { | ||
return StringUtils.toString(dateTime); | ||
} | ||
|
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.
why using Clock here?
maybe we could just use
ZonedDateTime.now(ZoneOffset.UTC)
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.
If I recall correctly, it's because the clock approach had unpredictable behavior between jdk 8 and 11.
@astefan do you remember?
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.
Right - you guys made an excellent fix here #38437 (comment)
I had similar problems this morning with watcher.
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.
@costin yes. We had this issue in the past for the
NOW()
function - #36877. @matriv's approach is spot on.