Skip to content
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

Update DATE_ADD/ADDDATE and DATE_SUB/SUBDATE functions. (#122) #1182

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public static ExprValue fromObjectValue(Object o) {
return timeValue((LocalTime) o);
} else if (o instanceof Instant) {
return timestampValue((Instant) o);
} else if (o instanceof TemporalAmount) {
return intervalValue((TemporalAmount) o);
} else {
throw new ExpressionEvaluationException("unsupported object " + o.getClass());
}
Expand Down
16 changes: 0 additions & 16 deletions core/src/main/java/org/opensearch/sql/expression/DSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,6 @@ public static FunctionExpression multiply(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.MULTIPLY, expressions);
}

public static FunctionExpression adddate(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.ADDDATE, expressions);
}

public static FunctionExpression convert_tz(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.CONVERT_TZ, expressions);
}
Expand All @@ -306,14 +302,6 @@ public static FunctionExpression datetime(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.DATETIME, expressions);
}

public static FunctionExpression date_add(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.DATE_ADD, expressions);
}

public static FunctionExpression date_sub(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.DATE_SUB, expressions);
}

public static FunctionExpression day(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.DAY, expressions);
}
Expand Down Expand Up @@ -402,10 +390,6 @@ public static FunctionExpression second_of_minute(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.SECOND_OF_MINUTE, expressions);
}

public static FunctionExpression subdate(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.SUBDATE, expressions);
}

public static FunctionExpression time(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.TIME, expressions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,14 @@
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.TextStyle;
import java.time.temporal.TemporalAmount;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.tuple.Pair;
import org.opensearch.sql.data.model.ExprDateValue;
import org.opensearch.sql.data.model.ExprDatetimeValue;
import org.opensearch.sql.data.model.ExprDoubleValue;
Expand All @@ -67,10 +71,14 @@
import org.opensearch.sql.expression.function.BuiltinFunctionName;
import org.opensearch.sql.expression.function.BuiltinFunctionRepository;
import org.opensearch.sql.expression.function.DefaultFunctionResolver;
import org.opensearch.sql.expression.function.FunctionBuilder;
import org.opensearch.sql.expression.function.FunctionDSL;
import org.opensearch.sql.expression.function.FunctionName;
import org.opensearch.sql.expression.function.FunctionProperties;
import org.opensearch.sql.expression.function.FunctionResolver;
import org.opensearch.sql.expression.function.FunctionSignature;
import org.opensearch.sql.expression.function.SerializableFunction;
import org.opensearch.sql.expression.function.SerializableTriFunction;
import org.opensearch.sql.utils.DateTimeUtils;

/**
Expand Down Expand Up @@ -229,30 +237,51 @@ private FunctionResolver current_date() {
}

/**
* Specify a start date and add a temporal amount to the date.
* A common signature for `date_add` and `date_sub`.
* Specify a start date and add/subtract a temporal amount to/from the date.
* The return type depends on the date type and the interval unit. Detailed supported signatures:
* (STRING/DATE/DATETIME/TIMESTAMP, INTERVAL) -> DATETIME
* (DATE, LONG) -> DATE
* (STRING/DATETIME/TIMESTAMP, LONG) -> DATETIME
*/
private DefaultFunctionResolver add_date(FunctionName functionName) {
return define(functionName,
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval),
DATETIME, STRING, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval), DATETIME, DATE, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval),
* (DATE/DATETIME/TIMESTAMP/TIME, INTERVAL) -> DATETIME
* MySQL has these signatures too
* (DATE, INTERVAL) -> DATE // when interval has no time part
* (TIME, INTERVAL) -> TIME // when interval has no date part
* (STRING, INTERVAL) -> STRING // when argument has date or datetime string,
* // result has date or datetime depending on interval type
*/
private Stream<SerializableFunction<?, ?>> get_date_add_date_sub_signatures(
SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue> function) {
return Stream.of(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Datetime required? I found you post same concern in #1176.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, yes, unless we have 4 types for date/time.

implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, DATE, INTERVAL),
implWithProperties(nullMissingHandlingWithProperties(function),
DATETIME, DATETIME, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval),
implWithProperties(nullMissingHandlingWithProperties(function),
DATETIME, TIMESTAMP, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATE, DATE, LONG),
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATETIME, DATETIME, LONG),
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATETIME, TIMESTAMP, LONG),
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATETIME, STRING, LONG)
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, TIME, INTERVAL)
);
}

/**
* A common signature for `adddate` and `subdate`.
* Adds/subtracts an integer number of days to/from the first argument.
* (DATE, LONG) -> DATE
* (TIME/DATETIME/TIMESTAMP, LONG) -> DATETIME
*/
private Stream<SerializableFunction<?, ?>> get_adddate_subdate_signatures(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue> function) {
return Stream.of(
implWithProperties(nullMissingHandlingWithProperties(function), DATE, DATE, LONG),
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, DATETIME, LONG),
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, TIMESTAMP, LONG),
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, TIME, LONG)
);
}

private DefaultFunctionResolver adddate() {
return add_date(BuiltinFunctionName.ADDDATE.getName());
return define(BuiltinFunctionName.ADDDATE.getName(),
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
(Stream.concat(
get_date_add_date_sub_signatures(DateTimeFunction::exprAddDateInterval),
get_adddate_subdate_signatures(DateTimeFunction::exprAddDateDays))
.toArray(SerializableFunction<?, ?>[]::new)));
}

/**
Expand Down Expand Up @@ -385,34 +414,17 @@ private FunctionResolver datetime() {
}

private DefaultFunctionResolver date_add() {
return add_date(BuiltinFunctionName.DATE_ADD.getName());
}

/**
* Specify a start date and subtract a temporal amount to the date.
* The return type depends on the date type and the interval unit. Detailed supported signatures:
* (STRING/DATE/DATETIME/TIMESTAMP, INTERVAL) -> DATETIME
* (DATE, LONG) -> DATE
* (STRING/DATETIME/TIMESTAMP, LONG) -> DATETIME
*/
private DefaultFunctionResolver sub_date(FunctionName functionName) {
return define(functionName,
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval),
DATETIME, STRING, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval), DATETIME, DATE, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval),
DATETIME, DATETIME, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval),
DATETIME, TIMESTAMP, INTERVAL),
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATE, DATE, LONG),
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATETIME, DATETIME, LONG),
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATETIME, TIMESTAMP, LONG),
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATETIME, STRING, LONG)
);
return define(BuiltinFunctionName.DATE_ADD.getName(),
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
get_date_add_date_sub_signatures(DateTimeFunction::exprAddDateInterval)
.toArray(SerializableFunction<?, ?>[]::new));
}

private DefaultFunctionResolver date_sub() {
return sub_date(BuiltinFunctionName.DATE_SUB.getName());
return define(BuiltinFunctionName.DATE_SUB.getName(),
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
get_date_add_date_sub_signatures(DateTimeFunction::exprSubDateInterval)
.toArray(SerializableFunction<?, ?>[]::new));
}

/**
Expand Down Expand Up @@ -637,7 +649,12 @@ private DefaultFunctionResolver second(BuiltinFunctionName name) {
}

private DefaultFunctionResolver subdate() {
return sub_date(BuiltinFunctionName.SUBDATE.getName());
return define(BuiltinFunctionName.SUBDATE.getName(),
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
(Stream.concat(
get_date_add_date_sub_signatures(DateTimeFunction::exprSubDateInterval),
get_adddate_subdate_signatures(DateTimeFunction::exprSubDateDays))
.toArray(SerializableFunction<?, ?>[]::new)));
}

/**
Expand Down Expand Up @@ -872,29 +889,65 @@ private ExprValue dayOfWeekToday(Clock clock) {
}

/**
* ADDDATE function implementation for ExprValue.
* DATE_ADD function implementation for ExprValue.
*
* @param functionProperties An FunctionProperties object.
* @param datetime ExprValue of Date/Time/Datetime/Timestamp type.
* @param interval ExprValue of Interval type, the temporal amount to add.
* @return Datetime resulted from `interval` added to `datetime`.
*/
private ExprValue exprAddDateInterval(FunctionProperties functionProperties,
ExprValue datetime, ExprValue interval) {
return exprDateApplyInterval(functionProperties, datetime, interval.intervalValue(), true);
}

/**
* Adds or subtracts `interval` to/from `datetime`.
*
* @param date ExprValue of String/Date/Datetime/Timestamp type.
* @param expr ExprValue of Interval type, the temporal amount to add.
* @return Datetime resulted from expr added to date.
* @param functionProperties An FunctionProperties object.
* @param datetime A Date/Time/Datetime/Timestamp value to change.
* @param interval An Interval to isAdd or subtract.
* @param isAdd A flag: true to isAdd, false to subtract.
* @return Datetime calculated.
*/
private ExprValue exprAddDateInterval(ExprValue date, ExprValue expr) {
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().plus(expr.intervalValue()));
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
: exprValue);
private ExprValue exprDateApplyInterval(FunctionProperties functionProperties,
ExprValue datetime,
TemporalAmount interval,
Boolean isAdd) {
var dt = extractDateTime(datetime, functionProperties);
return new ExprDatetimeValue(isAdd ? dt.plus(interval) : dt.minus(interval));
}

/**
* ADDDATE function implementation for ExprValue.
*
* @param date ExprValue of String/Date/Datetime/Timestamp type.
* @param functionProperties An FunctionProperties object.
* @param datetime ExprValue of Time/Date/Datetime/Timestamp type.
* @param days ExprValue of Long type, representing the number of days to add.
* @return Date/Datetime resulted from days added to date.
* @return Date/Datetime resulted from days added to `datetime`.
*/
private ExprValue exprAddDateDays(ExprValue date, ExprValue days) {
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().plusDays(days.longValue()));
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
: exprValue);
private ExprValue exprAddDateDays(FunctionProperties functionProperties,
ExprValue datetime, ExprValue days) {
return exprDateApplyDays(functionProperties, datetime, days.longValue(), true);
}

/**
* Adds or subtracts `days` to/from `datetime`.
*
* @param functionProperties An FunctionProperties object.
* @param datetime A Date/Time/Datetime/Timestamp value to change.
* @param days A days amount to add or subtract.
* @param isAdd A flag: true to add, false to subtract.
* @return Datetime calculated.
*/
private ExprValue exprDateApplyDays(FunctionProperties functionProperties,
ExprValue datetime, Long days, Boolean isAdd) {
if (datetime.type() == DATE) {
return new ExprDateValue(isAdd ? datetime.dateValue().plusDays(days)
: datetime.dateValue().minusDays(days));
}
var dt = extractDateTime(datetime, functionProperties);
return new ExprDatetimeValue(isAdd ? dt.plusDays(days) : dt.minusDays(days));
}

/**
Expand Down Expand Up @@ -1320,27 +1373,27 @@ private ExprValue exprSecond(ExprValue time) {
/**
* SUBDATE function implementation for ExprValue.
*
* @param date ExprValue of String/Date/Datetime/Timestamp type.
* @param functionProperties An FunctionProperties object.
* @param date ExprValue of Time/Date/Datetime/Timestamp type.
* @param days ExprValue of Long type, representing the number of days to subtract.
* @return Date/Datetime resulted from days subtracted to date.
*/
private ExprValue exprSubDateDays(ExprValue date, ExprValue days) {
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().minusDays(days.longValue()));
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
: exprValue);
private ExprValue exprSubDateDays(FunctionProperties functionProperties,
ExprValue date, ExprValue days) {
return exprDateApplyDays(functionProperties, date, days.longValue(), false);
}

/**
* SUBDATE function implementation for ExprValue.
* DATE_SUB function implementation for ExprValue.
*
* @param date ExprValue of String/Date/Datetime/Timestamp type.
* @param functionProperties An FunctionProperties object.
* @param datetime ExprValue of Time/Date/Datetime/Timestamp type.
* @param expr ExprValue of Interval type, the temporal amount to subtract.
* @return Datetime resulted from expr subtracted to date.
* @return Datetime resulted from expr subtracted to `datetime`.
*/
private ExprValue exprSubDateInterval(ExprValue date, ExprValue expr) {
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().minus(expr.intervalValue()));
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
: exprValue);
private ExprValue exprSubDateInterval(FunctionProperties functionProperties,
ExprValue datetime, ExprValue expr) {
return exprDateApplyInterval(functionProperties, datetime, expr.intervalValue(), false);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ private ExprValue hour(ExprValue value) {
}

private ExprValue day(ExprValue value) {
return new ExprIntervalValue(Duration.ofDays(getIntegerValue(value)));
return new ExprIntervalValue(Period.ofDays(getIntegerValue(value)));
}

private ExprValue week(ExprValue value) {
Expand Down
Loading