Skip to content

Commit af188a3

Browse files
Update DATE_ADD/ADDDATE and DATE_SUB/SUBDATE functions. (#122) (#1182)
* Update `DATE_ADD`/`ADDDATE` and `DATE_SUB`/`SUBDATE` functions. (#122) Signed-off-by: Yury-Fridlyand <[email protected]>
1 parent 79c7922 commit af188a3

File tree

13 files changed

+981
-588
lines changed

13 files changed

+981
-588
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ public static ExprValue fromObjectValue(Object o) {
142142
return timeValue((LocalTime) o);
143143
} else if (o instanceof Instant) {
144144
return timestampValue((Instant) o);
145+
} else if (o instanceof TemporalAmount) {
146+
return intervalValue((TemporalAmount) o);
145147
} else {
146148
throw new ExpressionEvaluationException("unsupported object " + o.getClass());
147149
}

core/src/main/java/org/opensearch/sql/expression/DSL.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,6 @@ public static FunctionExpression multiply(Expression... expressions) {
290290
return compile(FunctionProperties.None, BuiltinFunctionName.MULTIPLY, expressions);
291291
}
292292

293-
public static FunctionExpression adddate(Expression... expressions) {
294-
return compile(FunctionProperties.None, BuiltinFunctionName.ADDDATE, expressions);
295-
}
296-
297293
public static FunctionExpression convert_tz(Expression... expressions) {
298294
return compile(FunctionProperties.None, BuiltinFunctionName.CONVERT_TZ, expressions);
299295
}
@@ -306,14 +302,6 @@ public static FunctionExpression datetime(Expression... expressions) {
306302
return compile(FunctionProperties.None, BuiltinFunctionName.DATETIME, expressions);
307303
}
308304

309-
public static FunctionExpression date_add(Expression... expressions) {
310-
return compile(FunctionProperties.None, BuiltinFunctionName.DATE_ADD, expressions);
311-
}
312-
313-
public static FunctionExpression date_sub(Expression... expressions) {
314-
return compile(FunctionProperties.None, BuiltinFunctionName.DATE_SUB, expressions);
315-
}
316-
317305
public static FunctionExpression day(Expression... expressions) {
318306
return compile(FunctionProperties.None, BuiltinFunctionName.DAY, expressions);
319307
}
@@ -406,10 +394,6 @@ public static FunctionExpression second_of_minute(Expression... expressions) {
406394
return compile(FunctionProperties.None, BuiltinFunctionName.SECOND_OF_MINUTE, expressions);
407395
}
408396

409-
public static FunctionExpression subdate(Expression... expressions) {
410-
return compile(FunctionProperties.None, BuiltinFunctionName.SUBDATE, expressions);
411-
}
412-
413397
public static FunctionExpression time(Expression... expressions) {
414398
return compile(FunctionProperties.None, BuiltinFunctionName.TIME, expressions);
415399
}

core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java

Lines changed: 122 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,14 @@
5050
import java.time.format.DateTimeFormatter;
5151
import java.time.format.DateTimeParseException;
5252
import java.time.format.TextStyle;
53+
import java.time.temporal.TemporalAmount;
54+
import java.util.Arrays;
5355
import java.util.Locale;
5456
import java.util.TimeZone;
5557
import java.util.concurrent.TimeUnit;
58+
import java.util.stream.Stream;
5659
import lombok.experimental.UtilityClass;
60+
import org.apache.commons.lang3.tuple.Pair;
5761
import org.opensearch.sql.data.model.ExprDateValue;
5862
import org.opensearch.sql.data.model.ExprDatetimeValue;
5963
import org.opensearch.sql.data.model.ExprDoubleValue;
@@ -69,10 +73,14 @@
6973
import org.opensearch.sql.expression.function.BuiltinFunctionName;
7074
import org.opensearch.sql.expression.function.BuiltinFunctionRepository;
7175
import org.opensearch.sql.expression.function.DefaultFunctionResolver;
76+
import org.opensearch.sql.expression.function.FunctionBuilder;
7277
import org.opensearch.sql.expression.function.FunctionDSL;
7378
import org.opensearch.sql.expression.function.FunctionName;
7479
import org.opensearch.sql.expression.function.FunctionProperties;
7580
import org.opensearch.sql.expression.function.FunctionResolver;
81+
import org.opensearch.sql.expression.function.FunctionSignature;
82+
import org.opensearch.sql.expression.function.SerializableFunction;
83+
import org.opensearch.sql.expression.function.SerializableTriFunction;
7684
import org.opensearch.sql.utils.DateTimeUtils;
7785

7886
/**
@@ -232,30 +240,51 @@ private FunctionResolver current_date() {
232240
}
233241

234242
/**
235-
* Specify a start date and add a temporal amount to the date.
243+
* A common signature for `date_add` and `date_sub`.
244+
* Specify a start date and add/subtract a temporal amount to/from the date.
236245
* The return type depends on the date type and the interval unit. Detailed supported signatures:
237-
* (STRING/DATE/DATETIME/TIMESTAMP, INTERVAL) -> DATETIME
238-
* (DATE, LONG) -> DATE
239-
* (STRING/DATETIME/TIMESTAMP, LONG) -> DATETIME
240-
*/
241-
private DefaultFunctionResolver add_date(FunctionName functionName) {
242-
return define(functionName,
243-
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval),
244-
DATETIME, STRING, INTERVAL),
245-
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval), DATETIME, DATE, INTERVAL),
246-
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval),
246+
* (DATE/DATETIME/TIMESTAMP/TIME, INTERVAL) -> DATETIME
247+
* MySQL has these signatures too
248+
* (DATE, INTERVAL) -> DATE // when interval has no time part
249+
* (TIME, INTERVAL) -> TIME // when interval has no date part
250+
* (STRING, INTERVAL) -> STRING // when argument has date or datetime string,
251+
* // result has date or datetime depending on interval type
252+
*/
253+
private Stream<SerializableFunction<?, ?>> get_date_add_date_sub_signatures(
254+
SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue> function) {
255+
return Stream.of(
256+
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, DATE, INTERVAL),
257+
implWithProperties(nullMissingHandlingWithProperties(function),
247258
DATETIME, DATETIME, INTERVAL),
248-
impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval),
259+
implWithProperties(nullMissingHandlingWithProperties(function),
249260
DATETIME, TIMESTAMP, INTERVAL),
250-
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATE, DATE, LONG),
251-
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATETIME, DATETIME, LONG),
252-
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATETIME, TIMESTAMP, LONG),
253-
impl(nullMissingHandling(DateTimeFunction::exprAddDateDays), DATETIME, STRING, LONG)
261+
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, TIME, INTERVAL)
262+
);
263+
}
264+
265+
/**
266+
* A common signature for `adddate` and `subdate`.
267+
* Adds/subtracts an integer number of days to/from the first argument.
268+
* (DATE, LONG) -> DATE
269+
* (TIME/DATETIME/TIMESTAMP, LONG) -> DATETIME
270+
*/
271+
private Stream<SerializableFunction<?, ?>> get_adddate_subdate_signatures(
272+
SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue> function) {
273+
return Stream.of(
274+
implWithProperties(nullMissingHandlingWithProperties(function), DATE, DATE, LONG),
275+
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, DATETIME, LONG),
276+
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, TIMESTAMP, LONG),
277+
implWithProperties(nullMissingHandlingWithProperties(function), DATETIME, TIME, LONG)
254278
);
255279
}
256280

257281
private DefaultFunctionResolver adddate() {
258-
return add_date(BuiltinFunctionName.ADDDATE.getName());
282+
return define(BuiltinFunctionName.ADDDATE.getName(),
283+
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
284+
(Stream.concat(
285+
get_date_add_date_sub_signatures(DateTimeFunction::exprAddDateInterval),
286+
get_adddate_subdate_signatures(DateTimeFunction::exprAddDateDays))
287+
.toArray(SerializableFunction<?, ?>[]::new)));
259288
}
260289

261290
/**
@@ -388,34 +417,17 @@ private FunctionResolver datetime() {
388417
}
389418

390419
private DefaultFunctionResolver date_add() {
391-
return add_date(BuiltinFunctionName.DATE_ADD.getName());
392-
}
393-
394-
/**
395-
* Specify a start date and subtract a temporal amount to the date.
396-
* The return type depends on the date type and the interval unit. Detailed supported signatures:
397-
* (STRING/DATE/DATETIME/TIMESTAMP, INTERVAL) -> DATETIME
398-
* (DATE, LONG) -> DATE
399-
* (STRING/DATETIME/TIMESTAMP, LONG) -> DATETIME
400-
*/
401-
private DefaultFunctionResolver sub_date(FunctionName functionName) {
402-
return define(functionName,
403-
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval),
404-
DATETIME, STRING, INTERVAL),
405-
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval), DATETIME, DATE, INTERVAL),
406-
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval),
407-
DATETIME, DATETIME, INTERVAL),
408-
impl(nullMissingHandling(DateTimeFunction::exprSubDateInterval),
409-
DATETIME, TIMESTAMP, INTERVAL),
410-
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATE, DATE, LONG),
411-
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATETIME, DATETIME, LONG),
412-
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATETIME, TIMESTAMP, LONG),
413-
impl(nullMissingHandling(DateTimeFunction::exprSubDateDays), DATETIME, STRING, LONG)
414-
);
420+
return define(BuiltinFunctionName.DATE_ADD.getName(),
421+
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
422+
get_date_add_date_sub_signatures(DateTimeFunction::exprAddDateInterval)
423+
.toArray(SerializableFunction<?, ?>[]::new));
415424
}
416425

417426
private DefaultFunctionResolver date_sub() {
418-
return sub_date(BuiltinFunctionName.DATE_SUB.getName());
427+
return define(BuiltinFunctionName.DATE_SUB.getName(),
428+
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
429+
get_date_add_date_sub_signatures(DateTimeFunction::exprSubDateInterval)
430+
.toArray(SerializableFunction<?, ?>[]::new));
419431
}
420432

421433
/**
@@ -641,7 +653,12 @@ private DefaultFunctionResolver second(BuiltinFunctionName name) {
641653
}
642654

643655
private DefaultFunctionResolver subdate() {
644-
return sub_date(BuiltinFunctionName.SUBDATE.getName());
656+
return define(BuiltinFunctionName.SUBDATE.getName(),
657+
(SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>[])
658+
(Stream.concat(
659+
get_date_add_date_sub_signatures(DateTimeFunction::exprSubDateInterval),
660+
get_adddate_subdate_signatures(DateTimeFunction::exprSubDateDays))
661+
.toArray(SerializableFunction<?, ?>[]::new)));
645662
}
646663

647664
/**
@@ -876,29 +893,65 @@ private ExprValue dayOfWeekToday(Clock clock) {
876893
}
877894

878895
/**
879-
* ADDDATE function implementation for ExprValue.
896+
* DATE_ADD function implementation for ExprValue.
897+
*
898+
* @param functionProperties An FunctionProperties object.
899+
* @param datetime ExprValue of Date/Time/Datetime/Timestamp type.
900+
* @param interval ExprValue of Interval type, the temporal amount to add.
901+
* @return Datetime resulted from `interval` added to `datetime`.
902+
*/
903+
private ExprValue exprAddDateInterval(FunctionProperties functionProperties,
904+
ExprValue datetime, ExprValue interval) {
905+
return exprDateApplyInterval(functionProperties, datetime, interval.intervalValue(), true);
906+
}
907+
908+
/**
909+
* Adds or subtracts `interval` to/from `datetime`.
880910
*
881-
* @param date ExprValue of String/Date/Datetime/Timestamp type.
882-
* @param expr ExprValue of Interval type, the temporal amount to add.
883-
* @return Datetime resulted from expr added to date.
911+
* @param functionProperties An FunctionProperties object.
912+
* @param datetime A Date/Time/Datetime/Timestamp value to change.
913+
* @param interval An Interval to isAdd or subtract.
914+
* @param isAdd A flag: true to isAdd, false to subtract.
915+
* @return Datetime calculated.
884916
*/
885-
private ExprValue exprAddDateInterval(ExprValue date, ExprValue expr) {
886-
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().plus(expr.intervalValue()));
887-
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
888-
: exprValue);
917+
private ExprValue exprDateApplyInterval(FunctionProperties functionProperties,
918+
ExprValue datetime,
919+
TemporalAmount interval,
920+
Boolean isAdd) {
921+
var dt = extractDateTime(datetime, functionProperties);
922+
return new ExprDatetimeValue(isAdd ? dt.plus(interval) : dt.minus(interval));
889923
}
890924

891925
/**
892926
* ADDDATE function implementation for ExprValue.
893927
*
894-
* @param date ExprValue of String/Date/Datetime/Timestamp type.
928+
* @param functionProperties An FunctionProperties object.
929+
* @param datetime ExprValue of Time/Date/Datetime/Timestamp type.
895930
* @param days ExprValue of Long type, representing the number of days to add.
896-
* @return Date/Datetime resulted from days added to date.
931+
* @return Date/Datetime resulted from days added to `datetime`.
897932
*/
898-
private ExprValue exprAddDateDays(ExprValue date, ExprValue days) {
899-
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().plusDays(days.longValue()));
900-
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
901-
: exprValue);
933+
private ExprValue exprAddDateDays(FunctionProperties functionProperties,
934+
ExprValue datetime, ExprValue days) {
935+
return exprDateApplyDays(functionProperties, datetime, days.longValue(), true);
936+
}
937+
938+
/**
939+
* Adds or subtracts `days` to/from `datetime`.
940+
*
941+
* @param functionProperties An FunctionProperties object.
942+
* @param datetime A Date/Time/Datetime/Timestamp value to change.
943+
* @param days A days amount to add or subtract.
944+
* @param isAdd A flag: true to add, false to subtract.
945+
* @return Datetime calculated.
946+
*/
947+
private ExprValue exprDateApplyDays(FunctionProperties functionProperties,
948+
ExprValue datetime, Long days, Boolean isAdd) {
949+
if (datetime.type() == DATE) {
950+
return new ExprDateValue(isAdd ? datetime.dateValue().plusDays(days)
951+
: datetime.dateValue().minusDays(days));
952+
}
953+
var dt = extractDateTime(datetime, functionProperties);
954+
return new ExprDatetimeValue(isAdd ? dt.plusDays(days) : dt.minusDays(days));
902955
}
903956

904957
/**
@@ -1325,27 +1378,27 @@ private ExprValue exprSecond(ExprValue time) {
13251378
/**
13261379
* SUBDATE function implementation for ExprValue.
13271380
*
1328-
* @param date ExprValue of String/Date/Datetime/Timestamp type.
1381+
* @param functionProperties An FunctionProperties object.
1382+
* @param date ExprValue of Time/Date/Datetime/Timestamp type.
13291383
* @param days ExprValue of Long type, representing the number of days to subtract.
13301384
* @return Date/Datetime resulted from days subtracted to date.
13311385
*/
1332-
private ExprValue exprSubDateDays(ExprValue date, ExprValue days) {
1333-
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().minusDays(days.longValue()));
1334-
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
1335-
: exprValue);
1386+
private ExprValue exprSubDateDays(FunctionProperties functionProperties,
1387+
ExprValue date, ExprValue days) {
1388+
return exprDateApplyDays(functionProperties, date, days.longValue(), false);
13361389
}
13371390

13381391
/**
1339-
* SUBDATE function implementation for ExprValue.
1392+
* DATE_SUB function implementation for ExprValue.
13401393
*
1341-
* @param date ExprValue of String/Date/Datetime/Timestamp type.
1394+
* @param functionProperties An FunctionProperties object.
1395+
* @param datetime ExprValue of Time/Date/Datetime/Timestamp type.
13421396
* @param expr ExprValue of Interval type, the temporal amount to subtract.
1343-
* @return Datetime resulted from expr subtracted to date.
1397+
* @return Datetime resulted from expr subtracted to `datetime`.
13441398
*/
1345-
private ExprValue exprSubDateInterval(ExprValue date, ExprValue expr) {
1346-
ExprValue exprValue = new ExprDatetimeValue(date.datetimeValue().minus(expr.intervalValue()));
1347-
return (exprValue.timeValue().toSecondOfDay() == 0 ? new ExprDateValue(exprValue.dateValue())
1348-
: exprValue);
1399+
private ExprValue exprSubDateInterval(FunctionProperties functionProperties,
1400+
ExprValue datetime, ExprValue expr) {
1401+
return exprDateApplyInterval(functionProperties, datetime, expr.intervalValue(), false);
13491402
}
13501403

13511404
/**

core/src/main/java/org/opensearch/sql/expression/datetime/IntervalClause.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ private ExprValue hour(ExprValue value) {
9393
}
9494

9595
private ExprValue day(ExprValue value) {
96-
return new ExprIntervalValue(Duration.ofDays(getIntegerValue(value)));
96+
return new ExprIntervalValue(Period.ofDays(getIntegerValue(value)));
9797
}
9898

9999
private ExprValue week(ExprValue value) {

0 commit comments

Comments
 (0)