diff --git a/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java b/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java index 7617e156ba..f665627838 100644 --- a/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java +++ b/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java @@ -13,7 +13,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -69,7 +68,7 @@ public LocalDateTime datetimeValue() { @Override public Instant timestampValue() { - return ZonedDateTime.of(date, timeValue(), ZoneId.systemDefault()).toInstant(); + return ZonedDateTime.of(date, timeValue(), ExprTimestampValue.ZONE).toInstant(); } @Override diff --git a/core/src/main/java/org/opensearch/sql/data/model/ExprDatetimeValue.java b/core/src/main/java/org/opensearch/sql/data/model/ExprDatetimeValue.java index f5f80f133f..628106b048 100644 --- a/core/src/main/java/org/opensearch/sql/data/model/ExprDatetimeValue.java +++ b/core/src/main/java/org/opensearch/sql/data/model/ExprDatetimeValue.java @@ -11,7 +11,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -71,7 +70,7 @@ public LocalTime timeValue() { @Override public Instant timestampValue() { - return ZonedDateTime.of(datetime, ZoneId.of("UTC")).toInstant(); + return ZonedDateTime.of(datetime, ExprTimestampValue.ZONE).toInstant(); } @Override diff --git a/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java b/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java index 6cc4021d2e..d77a2615d2 100644 --- a/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java +++ b/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java @@ -13,7 +13,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.util.Objects; @@ -21,6 +20,7 @@ import org.opensearch.sql.data.type.ExprCoreType; import org.opensearch.sql.data.type.ExprType; import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.FunctionProperties; /** * Expression Time Value. @@ -57,6 +57,19 @@ public LocalTime timeValue() { return time; } + public LocalDate dateValue(FunctionProperties functionProperties) { + return LocalDate.now(functionProperties.getQueryStartClock()); + } + + public LocalDateTime datetimeValue(FunctionProperties functionProperties) { + return LocalDateTime.of(dateValue(functionProperties), timeValue()); + } + + public Instant timestampValue(FunctionProperties functionProperties) { + return ZonedDateTime.of(dateValue(functionProperties), timeValue(), ExprTimestampValue.ZONE) + .toInstant(); + } + @Override public String toString() { return String.format("TIME '%s'", value()); diff --git a/core/src/main/java/org/opensearch/sql/data/model/ExprTimestampValue.java b/core/src/main/java/org/opensearch/sql/data/model/ExprTimestampValue.java index 219a4c2663..a7ae605a7f 100644 --- a/core/src/main/java/org/opensearch/sql/data/model/ExprTimestampValue.java +++ b/core/src/main/java/org/opensearch/sql/data/model/ExprTimestampValue.java @@ -30,7 +30,7 @@ public class ExprTimestampValue extends AbstractExprValue { /** * todo. only support UTC now. */ - private static final ZoneId ZONE = ZoneId.of("UTC"); + public static final ZoneId ZONE = ZoneId.of("UTC"); private final Instant timestamp; diff --git a/core/src/main/java/org/opensearch/sql/data/model/ExprValueUtils.java b/core/src/main/java/org/opensearch/sql/data/model/ExprValueUtils.java index 407b6df5b3..44e0350d31 100644 --- a/core/src/main/java/org/opensearch/sql/data/model/ExprValueUtils.java +++ b/core/src/main/java/org/opensearch/sql/data/model/ExprValueUtils.java @@ -6,6 +6,10 @@ package org.opensearch.sql.data.model; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.temporal.TemporalAmount; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -61,6 +65,22 @@ public static ExprValue intervalValue(TemporalAmount value) { return new ExprIntervalValue(value); } + public static ExprValue dateValue(LocalDate value) { + return new ExprDateValue(value); + } + + public static ExprValue datetimeValue(LocalDateTime value) { + return new ExprDatetimeValue(value); + } + + public static ExprValue timeValue(LocalTime value) { + return new ExprTimeValue(value); + } + + public static ExprValue timestampValue(Instant value) { + return new ExprTimestampValue(value); + } + /** * {@link ExprTupleValue} constructor. */ @@ -115,6 +135,14 @@ public static ExprValue fromObjectValue(Object o) { return stringValue((String) o); } else if (o instanceof Float) { return floatValue((Float) o); + } else if (o instanceof LocalDate) { + return dateValue((LocalDate) o); + } else if (o instanceof LocalDateTime) { + return datetimeValue((LocalDateTime) o); + } else if (o instanceof LocalTime) { + return timeValue((LocalTime) o); + } else if (o instanceof Instant) { + return timestampValue((Instant) o); } else { throw new ExpressionEvaluationException("unsupported object " + o.getClass()); } diff --git a/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java b/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java index be3966188f..6ee5a79172 100644 --- a/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java @@ -6,6 +6,7 @@ package org.opensearch.sql.expression.datetime; +import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static org.opensearch.sql.data.type.ExprCoreType.DATE; @@ -21,17 +22,20 @@ import static org.opensearch.sql.expression.function.FunctionDSL.impl; import static org.opensearch.sql.expression.function.FunctionDSL.implWithProperties; import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling; +import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandlingWithProperties; import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_LONG_YEAR; import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_SHORT_YEAR; import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_LONG_YEAR; import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_SHORT_YEAR; import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_STRICT_WITH_TZ; +import static org.opensearch.sql.utils.DateTimeUtils.extractDate; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DecimalFormat; import java.time.Clock; import java.time.DateTimeException; +import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -97,6 +101,7 @@ public void register(BuiltinFunctionRepository repository) { repository.register(current_time()); repository.register(current_timestamp()); repository.register(date()); + repository.register(datediff()); repository.register(datetime()); repository.register(date_add()); repository.register(date_sub()); @@ -128,6 +133,7 @@ public void register(BuiltinFunctionRepository repository) { repository.register(sysdate()); repository.register(time()); repository.register(time_to_sec()); + repository.register(timediff()); repository.register(timestamp()); repository.register(utc_date()); repository.register(utc_time()); @@ -267,6 +273,46 @@ private DefaultFunctionResolver date() { impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP)); } + /* + * Calculates the difference of date part of given values. + * (DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME) -> LONG + */ + private DefaultFunctionResolver datediff() { + return define(BuiltinFunctionName.DATEDIFF.getName(), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATE, DATE), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATETIME, DATE), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATE, DATETIME), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATETIME, DATETIME), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATE, TIME), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIME, DATE), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIME, TIME), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIMESTAMP, DATE), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATE, TIMESTAMP), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIMESTAMP, TIMESTAMP), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIMESTAMP, TIME), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIME, TIMESTAMP), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIMESTAMP, DATETIME), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATETIME, TIMESTAMP), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, TIME, DATETIME), + implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + LONG, DATETIME, TIME)); + } + /** * Specify a datetime with time zone field and a time zone to convert to. * Returns a local date time. @@ -538,6 +584,22 @@ private DefaultFunctionResolver time() { impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, TIMESTAMP)); } + /** + * Returns different between two times as a time. + * (TIME, TIME) -> TIME + * MySQL has these signatures too + * (DATE, DATE) -> TIME // result is > 24 hours + * (DATETIME, DATETIME) -> TIME // result is > 24 hours + * (TIMESTAMP, TIMESTAMP) -> TIME // result is > 24 hours + * (x, x) -> NULL // when args have different types + * (STRING, STRING) -> TIME // argument strings contain same types only + * (STRING, STRING) -> NULL // argument strings are different types + */ + private DefaultFunctionResolver timediff() { + return define(BuiltinFunctionName.TIMEDIFF.getName(), + impl(nullMissingHandling(DateTimeFunction::exprTimeDiff), TIME, TIME, TIME)); + } + /** * TIME_TO_SEC(STRING/TIME/DATETIME/TIMESTAMP). return the time argument, converted to seconds. */ @@ -737,6 +799,22 @@ private ExprValue exprDate(ExprValue exprValue) { } } + /** + * Calculate the value in days from one date to the other. + * Only the date parts of the values are used in the calculation. + * + * @param first The first value. + * @param second The second value. + * @return The diff. + */ + private ExprValue exprDateDiff(FunctionProperties functionProperties, + ExprValue first, ExprValue second) { + // java inverses the value, so we have to swap 1 and 2 + return new ExprLongValue(DAYS.between( + extractDate(second, functionProperties), + extractDate(first, functionProperties))); + } + /** * DateTime implementation for ExprValue. * @@ -1096,6 +1174,19 @@ private ExprValue exprTime(ExprValue exprValue) { } } + /** + * Calculate the time difference between two times. + * + * @param first The first value. + * @param second The second value. + * @return The diff. + */ + private ExprValue exprTimeDiff(ExprValue first, ExprValue second) { + // java inverses the value, so we have to swap 1 and 2 + return new ExprTimeValue(LocalTime.MIN.plus( + Duration.between(second.timeValue(), first.timeValue()))); + } + /** * Timestamp implementation for ExprValue. * diff --git a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java index e461419f04..50c8682c62 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java @@ -61,6 +61,7 @@ public enum BuiltinFunctionName { ADDDATE(FunctionName.of("adddate")), CONVERT_TZ(FunctionName.of("convert_tz")), DATE(FunctionName.of("date")), + DATEDIFF(FunctionName.of("datediff")), DATETIME(FunctionName.of("datetime")), DATE_ADD(FunctionName.of("date_add")), DATE_SUB(FunctionName.of("date_sub")), @@ -87,6 +88,7 @@ public enum BuiltinFunctionName { SECOND(FunctionName.of("second")), SUBDATE(FunctionName.of("subdate")), TIME(FunctionName.of("time")), + TIMEDIFF(FunctionName.of("timediff")), TIME_TO_SEC(FunctionName.of("time_to_sec")), TIMESTAMP(FunctionName.of("timestamp")), DATE_FORMAT(FunctionName.of("date_format")), diff --git a/core/src/main/java/org/opensearch/sql/expression/function/FunctionDSL.java b/core/src/main/java/org/opensearch/sql/expression/function/FunctionDSL.java index 5b182f76f4..1bf38f7722 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/FunctionDSL.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/FunctionDSL.java @@ -137,6 +137,52 @@ public String toString() { }; } + /** + * Implementation of a function that takes two arguments, returns a value, and + * requires FunctionProperties to complete. + * + * @param function {@link ExprValue} based unary function. + * @param returnType return type. + * @param args1Type first argument type. + * @param args2Type second argument type. + * @return Unary Function Implementation. + */ + public static SerializableFunction> + implWithProperties( + SerializableTriFunction function, + ExprType returnType, + ExprType args1Type, + ExprType args2Type) { + + return functionName -> { + FunctionSignature functionSignature = + new FunctionSignature(functionName, Arrays.asList(args1Type, args2Type)); + FunctionBuilder functionBuilder = + (functionProperties, arguments) -> new FunctionExpression(functionName, arguments) { + @Override + public ExprValue valueOf(Environment valueEnv) { + ExprValue arg1 = arguments.get(0).valueOf(valueEnv); + ExprValue arg2 = arguments.get(1).valueOf(valueEnv); + return function.apply(functionProperties, arg1, arg2); + } + + @Override + public ExprType type() { + return returnType; + } + + @Override + public String toString() { + return String.format("%s(%s)", functionName, + arguments.stream() + .map(Object::toString) + .collect(Collectors.joining(", "))); + } + }; + return Pair.of(functionSignature, functionBuilder); + }; + } + /** * No Arg Function Implementation. * @@ -181,31 +227,8 @@ public static SerializableFunction { - FunctionSignature functionSignature = - new FunctionSignature(functionName, Arrays.asList(args1Type, args2Type)); - FunctionBuilder functionBuilder = - (functionProperties, arguments) -> new FunctionExpression(functionName, arguments) { - @Override - public ExprValue valueOf(Environment valueEnv) { - ExprValue arg1 = arguments.get(0).valueOf(valueEnv); - ExprValue arg2 = arguments.get(1).valueOf(valueEnv); - return function.apply(arg1, arg2); - } - - @Override - public ExprType type() { - return returnType; - } - - @Override - public String toString() { - return String.format("%s(%s, %s)", functionName, arguments.get(0).toString(), - arguments.get(1).toString()); - } - }; - return Pair.of(functionSignature, functionBuilder); - }; + return implWithProperties((fp, arg1, arg2) -> + function.apply(arg1, arg2), returnType, args1Type, args2Type); } /** @@ -317,4 +340,22 @@ public SerializableTriFunction nullM } }; } + + /** + * Wrapper for the ExprValue function that takes 2 arguments and is aware of FunctionProperties, + * with default NULL and MISSING handling. + */ + public static SerializableTriFunction + nullMissingHandlingWithProperties( + SerializableTriFunction implementation) { + return (functionProperties, v1, v2) -> { + if (v1.isMissing() || v2.isMissing()) { + return ExprValueUtils.missingValue(); + } else if (v1.isNull() || v2.isNull()) { + return ExprValueUtils.nullValue(); + } else { + return implementation.apply(functionProperties, v1, v2); + } + }; + } } diff --git a/core/src/main/java/org/opensearch/sql/utils/DateTimeUtils.java b/core/src/main/java/org/opensearch/sql/utils/DateTimeUtils.java index 5a99af3f83..28bb4e6918 100644 --- a/core/src/main/java/org/opensearch/sql/utils/DateTimeUtils.java +++ b/core/src/main/java/org/opensearch/sql/utils/DateTimeUtils.java @@ -6,10 +6,14 @@ package org.opensearch.sql.utils; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import lombok.experimental.UtilityClass; +import org.opensearch.sql.data.model.ExprTimeValue; +import org.opensearch.sql.data.model.ExprValue; +import org.opensearch.sql.expression.function.FunctionProperties; @UtilityClass public class DateTimeUtils { @@ -125,4 +129,15 @@ public Boolean isValidMySqlTimeZoneId(ZoneId zone) { && (passedTzValidator.isAfter(minTzValidator) || passedTzValidator.isEqual(minTzValidator)); } + + /** + * Extracts LocalDate from a datetime ExprValue. + * Uses `FunctionProperties` for `ExprTimeValue`. + */ + public static LocalDate extractDate(ExprValue value, + FunctionProperties functionProperties) { + return value instanceof ExprTimeValue + ? ((ExprTimeValue) value).dateValue(functionProperties) + : value.dateValue(); + } } diff --git a/core/src/test/java/org/opensearch/sql/data/model/DateTimeValueTest.java b/core/src/test/java/org/opensearch/sql/data/model/DateTimeValueTest.java index 3a7df17d90..a335478e19 100644 --- a/core/src/test/java/org/opensearch/sql/data/model/DateTimeValueTest.java +++ b/core/src/test/java/org/opensearch/sql/data/model/DateTimeValueTest.java @@ -15,11 +15,11 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; import java.time.ZonedDateTime; import org.junit.jupiter.api.Test; import org.opensearch.sql.exception.ExpressionEvaluationException; import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.FunctionProperties; public class DateTimeValueTest { @@ -27,14 +27,33 @@ public class DateTimeValueTest { @Test public void timeValueInterfaceTest() { - ExprValue timeValue = new ExprTimeValue("01:01:01"); + ExprTimeValue timeValue = new ExprTimeValue("01:01:01"); assertEquals(TIME, timeValue.type()); + assertEquals(LocalTime.parse("01:01:01"), timeValue.timeValue()); + // It is prohibited to acquire values which include date part from `ExprTimeValue` + // without a FunctionProperties object + var exception = assertThrows(ExpressionEvaluationException.class, timeValue::dateValue); + assertEquals("invalid to get dateValue from value of type TIME", exception.getMessage()); + exception = assertThrows(ExpressionEvaluationException.class, timeValue::datetimeValue); + assertEquals("invalid to get datetimeValue from value of type TIME", exception.getMessage()); + exception = assertThrows(ExpressionEvaluationException.class, timeValue::timestampValue); + assertEquals("invalid to get timestampValue from value of type TIME", exception.getMessage()); + + var functionProperties = new FunctionProperties(); + var today = LocalDate.now(functionProperties.getQueryStartClock()); + + assertEquals(today, timeValue.dateValue(functionProperties)); + assertEquals(today.atTime(1, 1, 1), timeValue.datetimeValue(functionProperties)); + assertEquals(ZonedDateTime.of(LocalTime.parse("01:01:01").atDate(today), + ExprTimestampValue.ZONE).toInstant(), timeValue.timestampValue(functionProperties)); + assertEquals("01:01:01", timeValue.value()); assertEquals("TIME '01:01:01'", timeValue.toString()); - assertThrows(ExpressionEvaluationException.class, () -> integerValue(1).timeValue(), - "invalid to get timeValue from value of type INTEGER"); + exception = assertThrows(ExpressionEvaluationException.class, + () -> integerValue(1).timeValue()); + assertEquals("invalid to get timeValue from value of type INTEGER", exception.getMessage()); } @Test @@ -43,7 +62,7 @@ public void timestampValueInterfaceTest() { assertEquals(TIMESTAMP, timestampValue.type()); assertEquals(ZonedDateTime.of(LocalDateTime.parse("2020-07-07T01:01:01"), - ZoneId.of("UTC")).toInstant(), timestampValue.timestampValue()); + ExprTimestampValue.ZONE).toInstant(), timestampValue.timestampValue()); assertEquals("2020-07-07 01:01:01", timestampValue.value()); assertEquals("TIMESTAMP '2020-07-07 01:01:01'", timestampValue.toString()); assertEquals(LocalDate.parse("2020-07-07"), timestampValue.dateValue()); @@ -61,7 +80,7 @@ public void dateValueInterfaceTest() { assertEquals(LocalTime.parse("00:00:00"), dateValue.timeValue()); assertEquals(LocalDateTime.parse("2012-07-07T00:00:00"), dateValue.datetimeValue()); assertEquals(ZonedDateTime.of(LocalDateTime.parse("2012-07-07T00:00:00"), - ZoneId.systemDefault()).toInstant(), dateValue.timestampValue()); + ExprTimestampValue.ZONE).toInstant(), dateValue.timestampValue()); ExpressionEvaluationException exception = assertThrows(ExpressionEvaluationException.class, () -> integerValue(1).dateValue()); assertEquals("invalid to get dateValue from value of type INTEGER", @@ -76,7 +95,7 @@ public void datetimeValueInterfaceTest() { assertEquals(LocalDate.parse("2020-08-17"), datetimeValue.dateValue()); assertEquals(LocalTime.parse("19:44:00"), datetimeValue.timeValue()); assertEquals(ZonedDateTime.of(LocalDateTime.parse("2020-08-17T19:44:00"), - ZoneId.of("UTC")).toInstant(), datetimeValue.timestampValue()); + ExprTimestampValue.ZONE).toInstant(), datetimeValue.timestampValue()); assertEquals("DATETIME '2020-08-17 19:44:00'", datetimeValue.toString()); assertThrows(ExpressionEvaluationException.class, () -> integerValue(1).datetimeValue(), "invalid to get datetimeValue from value of type INTEGER"); diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/DateDiffTest.java b/core/src/test/java/org/opensearch/sql/expression/datetime/DateDiffTest.java new file mode 100644 index 0000000000..72c1ceba03 --- /dev/null +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/DateDiffTest.java @@ -0,0 +1,77 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.datetime; + +import static java.time.temporal.ChronoUnit.DAYS; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.temporal.Temporal; +import java.util.TimeZone; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class DateDiffTest extends DateTimeTestBase { + + private static final LocalTime timeSample1 = LocalTime.of(12, 42); + private static final LocalTime timeSample2 = LocalTime.of(7, 40); + private static final LocalDate dateSample1 = LocalDate.of(2022, 6, 6); + private static final LocalDate dateSample2 = LocalDate.of(1961, 4, 12); + private static final LocalDate dateSample3 = LocalDate.of(1993, 3, 4); + private static final LocalDate epochStart = LocalDate.of(1970, 1, 1); + private static final LocalDate dateNow = LocalDate.now(); + private static final LocalDateTime dateTimeSample1 = LocalDateTime.of(1961, 4, 12, 9, 7); + private static final LocalDateTime dateTimeSample2 = LocalDateTime.of(1993, 3, 4, 5, 6); + + // Function signature is: + // (DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME) -> LONG + private static Stream getTestData() { + // Arguments are: first argument for `DATE_DIFF` function, second argument and expected result. + return Stream.of( + Arguments.of(timeSample1, timeSample2, 0L), + Arguments.of(timeSample1, dateNow, 0L), + Arguments.of(timeSample1, LocalDateTime.now(), 0L), + Arguments.of(timeSample1, + Instant.now().plusMillis(TimeZone.getDefault().getRawOffset()), 0L), + Arguments.of(dateSample1, timeSample1, + -DAYS.between(dateSample1, dateNow)), + Arguments.of(dateSample1, dateSample3, + -DAYS.between(dateSample1, dateSample3)), + Arguments.of(dateSample1, dateTimeSample1, + -DAYS.between(dateSample1, dateSample2)), + Arguments.of(dateSample1, Instant.ofEpochSecond(42), + -DAYS.between(dateSample1, epochStart)), + Arguments.of(dateTimeSample1, LocalTime.now(), + -DAYS.between(dateSample2, dateNow)), + Arguments.of(dateTimeSample1, dateSample3, + -DAYS.between(dateSample2, dateSample3)), + Arguments.of(dateTimeSample1, dateTimeSample2, + -DAYS.between(dateSample2, dateSample3)), + Arguments.of(dateTimeSample1, Instant.ofEpochSecond(0), + -DAYS.between(dateSample2, epochStart)), + Arguments.of(Instant.ofEpochSecond(0), LocalTime.MAX, + -DAYS.between(epochStart, dateNow)), + Arguments.of(Instant.ofEpochSecond(0), dateSample3, + -DAYS.between(epochStart, dateSample3)), + Arguments.of(Instant.ofEpochSecond(0), dateTimeSample2, + -DAYS.between(epochStart, dateSample3)), + Arguments.of(Instant.ofEpochSecond(0), Instant.now(), + -DAYS.between(epochStart, LocalDateTime.now(ZoneId.of("UTC")))) + ); + } + + @ParameterizedTest + @MethodSource("getTestData") + public void try_different_data(Temporal arg1, Temporal arg2, Long expectedResult) { + assertEquals(expectedResult, datediff(arg1, arg2)); + } +} diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java b/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java index 555759f1b1..7517b27944 100644 --- a/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java @@ -5,10 +5,14 @@ package org.opensearch.sql.expression.datetime; +import static org.opensearch.sql.data.model.ExprValueUtils.fromObjectValue; +import static org.opensearch.sql.data.type.ExprCoreType.DOUBLE; + import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.temporal.Temporal; import java.util.List; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; @@ -18,6 +22,7 @@ import org.opensearch.sql.data.model.ExprDatetimeValue; import org.opensearch.sql.data.model.ExprMissingValue; import org.opensearch.sql.data.model.ExprNullValue; +import org.opensearch.sql.data.model.ExprTimeValue; import org.opensearch.sql.data.model.ExprTimestampValue; import org.opensearch.sql.data.model.ExprValue; import org.opensearch.sql.expression.DSL; @@ -38,14 +43,6 @@ public class DateTimeTestBase extends ExpressionTestBase { @Mock protected Environment env; - - protected static FunctionProperties functionProperties; - - @BeforeAll - public static void setup() { - functionProperties = new FunctionProperties(); - } - protected Expression nullRef = DSL.literal(ExprNullValue.of()); protected Expression missingRef = DSL.literal(ExprMissingValue.of()); @@ -54,6 +51,17 @@ protected ExprValue eval(Expression expression) { return expression.valueOf(env); } + protected FunctionExpression datediff(Expression first, Expression second) { + return (FunctionExpression) functionRepository.compile( + functionProperties, + BuiltinFunctionName.DATEDIFF.getName(), List.of(first, second)); + } + + protected Long datediff(Temporal first, Temporal second) { + return datediff(DSL.literal(fromObjectValue(first)), DSL.literal(fromObjectValue(second))) + .valueOf(null).longValue(); + } + protected LocalDateTime fromUnixTime(Double value) { return fromUnixTime(DSL.literal(value)).valueOf().datetimeValue(); } @@ -92,7 +100,6 @@ protected FunctionExpression maketime(Expression hour, Expression minute, Expres BuiltinFunctionName.MAKETIME.getName(), List.of(hour, minute, second)); } - protected LocalTime maketime(Double hour, Double minute, Double second) { return maketime(DSL.literal(hour), DSL.literal(minute), DSL.literal(second)) .valueOf().timeValue(); @@ -130,6 +137,17 @@ protected Integer period_diff(Integer first, Integer second) { .valueOf().integerValue(); } + protected FunctionExpression timediff(Expression first, Expression second) { + return (FunctionExpression) functionRepository.compile( + functionProperties, + BuiltinFunctionName.TIMEDIFF.getName(), List.of(first, second)); + } + + protected LocalTime timediff(LocalTime first, LocalTime second) { + return timediff(DSL.literal(new ExprTimeValue(first)), DSL.literal(new ExprTimeValue(second))) + .valueOf(null).timeValue(); + } + protected FunctionExpression unixTimeStampExpr() { return (FunctionExpression) functionRepository.compile( functionProperties, BuiltinFunctionName.UNIX_TIMESTAMP.getName(), List.of()); diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/TimeDiffTest.java b/core/src/test/java/org/opensearch/sql/expression/datetime/TimeDiffTest.java new file mode 100644 index 0000000000..8bfb09bd49 --- /dev/null +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/TimeDiffTest.java @@ -0,0 +1,42 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.datetime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.LocalTime; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.opensearch.sql.data.model.ExprTimeValue; +import org.opensearch.sql.expression.DSL; + +public class TimeDiffTest extends DateTimeTestBase { + + private static Stream getTestData() { + return Stream.of( + Arguments.of(LocalTime.of(12, 42), LocalTime.of(7, 40), LocalTime.of(5, 2)), + Arguments.of(LocalTime.of(7, 40), LocalTime.of(12, 42), LocalTime.of(18, 58)), + Arguments.of(LocalTime.of(7, 40), LocalTime.of(7, 40), LocalTime.of(0, 0)), + Arguments.of(LocalTime.MAX, LocalTime.MIN, LocalTime.MAX) + ); + } + + /** + * Test `TIME_DIFF` function with different data. + * @param arg1 First argument. + * @param arg2 Second argument. + * @param expectedResult Expected result. + */ + @ParameterizedTest + @MethodSource("getTestData") + public void try_different_data(LocalTime arg1, LocalTime arg2, LocalTime expectedResult) { + assertEquals(expectedResult, timediff(arg1, arg2)); + assertEquals(expectedResult, eval(timediff(DSL.literal(new ExprTimeValue(arg1)), + DSL.literal(new ExprTimeValue(arg2)))).timeValue()); + } +} diff --git a/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLTestBase.java b/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLTestBase.java index 193066e626..63c6ea3329 100644 --- a/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLTestBase.java +++ b/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLTestBase.java @@ -52,6 +52,8 @@ public int compareTo(ExprValue o) { static final SerializableFunction oneArg = v -> ANY; static final SerializableBiFunction oneArgWithProperties = (functionProperties, v) -> ANY; + static final SerializableTriFunction + twoArgWithProperties = (functionProperties, v1, v2) -> ANY; static final SerializableBiFunction twoArgs = (v1, v2) -> ANY; diff --git a/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLimplWithPropertiesTwoArgTest.java b/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLimplWithPropertiesTwoArgTest.java new file mode 100644 index 0000000000..18444a476e --- /dev/null +++ b/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLimplWithPropertiesTwoArgTest.java @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function; + +import java.util.List; +import org.apache.commons.lang3.tuple.Pair; +import org.opensearch.sql.data.model.ExprValue; +import org.opensearch.sql.expression.DSL; +import org.opensearch.sql.expression.Expression; + +class FunctionDSLimplWithPropertiesTwoArgTest extends FunctionDSLimplTestBase { + + @Override + SerializableFunction> + getImplementationGenerator() { + SerializableTriFunction functionBody + = (fp, arg1, arg2) -> ANY; + return FunctionDSL.implWithProperties(functionBody, ANY_TYPE, ANY_TYPE, ANY_TYPE); + } + + @Override + List getSampleArguments() { + return List.of(DSL.literal(ANY), DSL.literal(ANY)); + } + + @Override + String getExpected_toString() { + return "sample(ANY, ANY)"; + } +} diff --git a/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLnullMissingHandlingTest.java b/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLnullMissingHandlingTest.java index 64cac278f6..17f1de355f 100644 --- a/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLnullMissingHandlingTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/function/FunctionDSLnullMissingHandlingTest.java @@ -47,6 +47,41 @@ void nullMissingHandling_oneArg_FunctionProperties_apply() { nullMissingHandlingWithProperties(oneArgWithProperties).apply(functionProperties, ANY)); } + @Test + void nullMissingHandling_twoArgs_FunctionProperties_nullValue_firstArg() { + assertEquals(NULL, + nullMissingHandlingWithProperties(twoArgWithProperties) + .apply(functionProperties, NULL, ANY)); + } + + @Test + void nullMissingHandling_twoArgs_FunctionProperties_nullValue_secondArg() { + assertEquals(NULL, + nullMissingHandlingWithProperties(twoArgWithProperties) + .apply(functionProperties, ANY, NULL)); + } + + @Test + void nullMissingHandling_twoArgs_FunctionProperties_missingValue_firstArg() { + assertEquals(MISSING, + nullMissingHandlingWithProperties(twoArgWithProperties) + .apply(functionProperties, MISSING, ANY)); + } + + @Test + void nullMissingHandling_twoArgs_FunctionProperties_missingValue_secondArg() { + assertEquals(MISSING, + nullMissingHandlingWithProperties(twoArgWithProperties) + .apply(functionProperties, ANY, MISSING)); + } + + @Test + void nullMissingHandling_twoArgs_FunctionProperties_apply() { + assertEquals(ANY, + nullMissingHandlingWithProperties(twoArgWithProperties) + .apply(functionProperties, ANY, ANY)); + } + @Test void nullMissingHandling_twoArgs_firstArg_nullValue() { assertEquals(NULL, nullMissingHandling(twoArgs).apply(NULL, ANY)); diff --git a/docs/user/dql/functions.rst b/docs/user/dql/functions.rst index 6c6e77a24b..7f7284c95b 100644 --- a/docs/user/dql/functions.rst +++ b/docs/user/dql/functions.rst @@ -1353,6 +1353,26 @@ Example:: +-------------------------------------------------+-----------------------------------+-------------------------------------------------+ +DATEDIFF +-------- + +Usage: Calculates the difference of date parts of the given values. If the first argument is time, today's date is used. + +Argument type: DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME + +Return type: LONG + +Example:: + + os> SELECT DATEDIFF(TIMESTAMP('2000-01-02 00:00:00'), TIMESTAMP('2000-01-01 23:59:59')) AS `'2000-01-02' - '2000-01-01'`, DATEDIFF(DATE('2001-02-01'), TIMESTAMP('2004-01-01 00:00:00')) AS `'2001-02-01' - '2004-01-01'`, DATEDIFF(TIME('23:59:59'), TIME('00:00:00')) AS `today - today` + fetched rows / total rows = 1/1 + +-------------------------------+-------------------------------+-----------------+ + | '2000-01-02' - '2000-01-01' | '2001-02-01' - '2004-01-01' | today - today | + |-------------------------------+-------------------------------+-----------------| + | 1 | -1064 | 0 | + +-------------------------------+-------------------------------+-----------------+ + + DAY --- @@ -2061,6 +2081,29 @@ Example:: +--------------------------------+ +TIMEDIFF +-------- + +Description +>>>>>>>>>>> + +Usage: returns the difference between two time expressions as a time. + +Argument type: TIME, TIME + +Return type: TIME + +Example:: + + os> SELECT TIMEDIFF('23:59:59', '13:00:00') + fetched rows / total rows = 1/1 + +------------------------------------+ + | TIMEDIFF('23:59:59', '13:00:00') | + |------------------------------------| + | 10:59:59 | + +------------------------------------+ + + TIMESTAMP --------- diff --git a/docs/user/ppl/functions/datetime.rst b/docs/user/ppl/functions/datetime.rst index 99ccc8e360..60a8660c7a 100644 --- a/docs/user/ppl/functions/datetime.rst +++ b/docs/user/ppl/functions/datetime.rst @@ -537,6 +537,26 @@ Example:: +-------------------------------------------------+-----------------------------------+-------------------------------------------------+ +DATEDIFF +-------- + +Usage: Calculates the difference of date parts of given values. If the first argument is time, today's date is used. + +Argument type: DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME + +Return type: LONG + +Example:: + + os> source=people | eval `'2000-01-02' - '2000-01-01'` = DATEDIFF(TIMESTAMP('2000-01-02 00:00:00'), TIMESTAMP('2000-01-01 23:59:59')), `'2001-02-01' - '2004-01-01'` = DATEDIFF(DATE('2001-02-01'), TIMESTAMP('2004-01-01 00:00:00')), `today - today` = DATEDIFF(TIME('23:59:59'), TIME('00:00:00')) | fields `'2000-01-02' - '2000-01-01'`, `'2001-02-01' - '2004-01-01'`, `today - today` + fetched rows / total rows = 1/1 + +-------------------------------+-------------------------------+-----------------+ + | '2000-01-02' - '2000-01-01' | '2001-02-01' - '2004-01-01' | today - today | + |-------------------------------+-------------------------------+-----------------| + | 1 | -1064 | 0 | + +-------------------------------+-------------------------------+-----------------+ + + DAY --- @@ -1179,6 +1199,29 @@ Example:: +---------------------------------+ +TIMEDIFF +-------- + +Description +>>>>>>>>>>> + +Usage: returns the difference between two time expressions as a time. + +Argument type: TIME, TIME + +Return type: TIME + +Example:: + + os> source=people | eval `TIMEDIFF('23:59:59', '13:00:00')` = TIMEDIFF('23:59:59', '13:00:00') | fields `TIMEDIFF('23:59:59', '13:00:00')` + fetched rows / total rows = 1/1 + +------------------------------------+ + | TIMEDIFF('23:59:59', '13:00:00') | + |------------------------------------| + | 10:59:59 | + +------------------------------------+ + + TIMESTAMP --------- diff --git a/integ-test/src/test/java/org/opensearch/sql/ppl/DateTimeFunctionIT.java b/integ-test/src/test/java/org/opensearch/sql/ppl/DateTimeFunctionIT.java index 004d8a0648..9f59caefc6 100644 --- a/integ-test/src/test/java/org/opensearch/sql/ppl/DateTimeFunctionIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/ppl/DateTimeFunctionIT.java @@ -906,4 +906,27 @@ public void testPeriodDiff() throws IOException { verifySchema(result, schema("f1", null, "integer"), schema("f2", null, "integer")); verifySome(result.getJSONArray("datarows"), rows(11, -25)); } + + public void testDateDiff() throws IOException { + var result = executeQuery(String.format("source=%s | eval" + + " `'2000-01-02' - '2000-01-01'` = DATEDIFF(TIMESTAMP('2000-01-02 00:00:00'), TIMESTAMP('2000-01-01 23:59:59'))," + + " `'2001-02-01' - '2004-01-01'` = DATEDIFF(DATE('2001-02-01'), TIMESTAMP('2004-01-01 00:00:00'))," + + " `'2004-01-01' - '2002-02-01'` = DATEDIFF(TIMESTAMP('2004-01-01 00:00:00'), DATETIME('2002-02-01 14:25:30'))," + + " `today - today` = DATEDIFF(TIME('23:59:59'), TIME('00:00:00'))" + + " | fields `'2000-01-02' - '2000-01-01'`, `'2001-02-01' - '2004-01-01'`, `'2004-01-01' - '2002-02-01'`, `today - today`", TEST_INDEX_DATE)); + verifySchema(result, + schema("'2000-01-02' - '2000-01-01'", null, "long"), + schema("'2001-02-01' - '2004-01-01'", null, "long"), + schema("'2004-01-01' - '2002-02-01'", null, "long"), + schema("today - today", null, "long")); + verifySome(result.getJSONArray("datarows"), rows(1, -1064, 699, 0)); + } + + @Test + public void testTimeDiff() throws IOException { + var result = executeQuery(String.format( + "source=%s | eval f = TIMEDIFF('23:59:59', '13:00:00') | fields f", TEST_INDEX_DATE)); + verifySchema(result, schema("f", null, "time")); + verifySome(result.getJSONArray("datarows"), rows("10:59:59")); + } } diff --git a/integ-test/src/test/java/org/opensearch/sql/sql/DateTimeFunctionIT.java b/integ-test/src/test/java/org/opensearch/sql/sql/DateTimeFunctionIT.java index 957275852f..e135f15523 100644 --- a/integ-test/src/test/java/org/opensearch/sql/sql/DateTimeFunctionIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/sql/DateTimeFunctionIT.java @@ -904,6 +904,27 @@ public void testPeriodDiff() throws IOException { verifyDataRows(result, rows(11, -25)); } + public void testDateDiff() throws IOException { + var result = executeQuery("SELECT" + + " DATEDIFF(TIMESTAMP('2000-01-02 00:00:00'), TIMESTAMP('2000-01-01 23:59:59')) AS `'2000-01-02' - '2000-01-01'`," + + " DATEDIFF(DATE('2001-02-01'), TIMESTAMP('2004-01-01 00:00:00')) AS `'2001-02-01' - '2004-01-01'`," + + " DATEDIFF(TIMESTAMP('2004-01-01 00:00:00'), DATETIME('2002-02-01 14:25:30')) AS `'2004-01-01' - '2002-02-01'`," + + " DATEDIFF(TIME('23:59:59'), TIME('00:00:00')) AS `today - today`"); + verifySchema(result, + schema("DATEDIFF(TIMESTAMP('2000-01-02 00:00:00'), TIMESTAMP('2000-01-01 23:59:59'))", "'2000-01-02' - '2000-01-01'", "long"), + schema("DATEDIFF(DATE('2001-02-01'), TIMESTAMP('2004-01-01 00:00:00'))", "'2001-02-01' - '2004-01-01'", "long"), + schema("DATEDIFF(TIMESTAMP('2004-01-01 00:00:00'), DATETIME('2002-02-01 14:25:30'))", "'2004-01-01' - '2002-02-01'", "long"), + schema("DATEDIFF(TIME('23:59:59'), TIME('00:00:00'))", "today - today", "long")); + verifyDataRows(result, rows(1, -1064, 699, 0)); + } + + @Test + public void testTimeDiff() throws IOException { + var result = executeQuery("select TIMEDIFF('23:59:59', '13:00:00') as f"); + verifySchema(result, schema("TIMEDIFF('23:59:59', '13:00:00')", "f", "time")); + verifyDataRows(result, rows("10:59:59")); + } + protected JSONObject executeQuery(String query) throws IOException { Request request = new Request("POST", QUERY_API_ENDPOINT); request.setJsonEntity(String.format(Locale.ROOT, "{\n" + " \"query\": \"%s\"\n" + "}", query)); diff --git a/ppl/src/main/antlr/OpenSearchPPLLexer.g4 b/ppl/src/main/antlr/OpenSearchPPLLexer.g4 index 9282c42308..778d9fb3f9 100644 --- a/ppl/src/main/antlr/OpenSearchPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenSearchPPLLexer.g4 @@ -247,6 +247,7 @@ DATE: 'DATE'; DATE_ADD: 'DATE_ADD'; DATE_FORMAT: 'DATE_FORMAT'; DATE_SUB: 'DATE_SUB'; +DATEDIFF: 'DATEDIFF'; DAYNAME: 'DAYNAME'; DAYOFMONTH: 'DAYOFMONTH'; DAYOFWEEK: 'DAYOFWEEK'; @@ -265,6 +266,7 @@ SUBDATE: 'SUBDATE'; SYSDATE: 'SYSDATE'; TIME: 'TIME'; TIME_TO_SEC: 'TIME_TO_SEC'; +TIMEDIFF: 'TIMEDIFF'; TIMESTAMP: 'TIMESTAMP'; TO_DAYS: 'TO_DAYS'; UTC_DATE: 'UTC_DATE'; diff --git a/ppl/src/main/antlr/OpenSearchPPLParser.g4 b/ppl/src/main/antlr/OpenSearchPPLParser.g4 index d7491abb51..cb9bfae32f 100644 --- a/ppl/src/main/antlr/OpenSearchPPLParser.g4 +++ b/ppl/src/main/antlr/OpenSearchPPLParser.g4 @@ -441,6 +441,7 @@ dateAndTimeFunctionBase | DATE_ADD | DATE_FORMAT | DATE_SUB + | DATEDIFF | DATETIME | DAY | DAYNAME @@ -469,6 +470,7 @@ dateAndTimeFunctionBase | SYSDATE | TIME | TIME_TO_SEC + | TIMEDIFF | TIMESTAMP | TO_DAYS | UNIX_TIMESTAMP diff --git a/sql/src/main/antlr/OpenSearchSQLLexer.g4 b/sql/src/main/antlr/OpenSearchSQLLexer.g4 index 01016992e9..63fd51fc5d 100644 --- a/sql/src/main/antlr/OpenSearchSQLLexer.g4 +++ b/sql/src/main/antlr/OpenSearchSQLLexer.g4 @@ -200,6 +200,7 @@ DATE: 'DATE'; DATE_ADD: 'DATE_ADD'; DATE_FORMAT: 'DATE_FORMAT'; DATE_SUB: 'DATE_SUB'; +DATEDIFF: 'DATEDIFF'; DAYNAME: 'DAYNAME'; DAYOFMONTH: 'DAYOFMONTH'; DAYOFWEEK: 'DAYOFWEEK'; @@ -254,6 +255,7 @@ SUBTRACT: 'SUBTRACT'; SYSDATE: 'SYSDATE'; TAN: 'TAN'; TIME: 'TIME'; +TIMEDIFF: 'TIMEDIFF'; TIME_TO_SEC: 'TIME_TO_SEC'; TIMESTAMP: 'TIMESTAMP'; TRUNCATE: 'TRUNCATE'; diff --git a/sql/src/main/antlr/OpenSearchSQLParser.g4 b/sql/src/main/antlr/OpenSearchSQLParser.g4 index 859d96c505..954fc44c3f 100644 --- a/sql/src/main/antlr/OpenSearchSQLParser.g4 +++ b/sql/src/main/antlr/OpenSearchSQLParser.g4 @@ -420,6 +420,7 @@ dateTimeFunctionName | DATE_ADD | DATE_FORMAT | DATE_SUB + | DATEDIFF | DATETIME | DAY | DAYNAME @@ -445,6 +446,7 @@ dateTimeFunctionName | SYSDATE | TIME | TIME_TO_SEC + | TIMEDIFF | TIMESTAMP | TO_DAYS | UNIX_TIMESTAMP