|
50 | 50 | import java.time.format.DateTimeFormatter;
|
51 | 51 | import java.time.format.DateTimeParseException;
|
52 | 52 | import java.time.format.TextStyle;
|
| 53 | +import java.time.temporal.TemporalAmount; |
| 54 | +import java.util.Arrays; |
53 | 55 | import java.util.Locale;
|
54 | 56 | import java.util.TimeZone;
|
55 | 57 | import java.util.concurrent.TimeUnit;
|
| 58 | +import java.util.stream.Stream; |
56 | 59 | import lombok.experimental.UtilityClass;
|
| 60 | +import org.apache.commons.lang3.tuple.Pair; |
57 | 61 | import org.opensearch.sql.data.model.ExprDateValue;
|
58 | 62 | import org.opensearch.sql.data.model.ExprDatetimeValue;
|
59 | 63 | import org.opensearch.sql.data.model.ExprDoubleValue;
|
|
69 | 73 | import org.opensearch.sql.expression.function.BuiltinFunctionName;
|
70 | 74 | import org.opensearch.sql.expression.function.BuiltinFunctionRepository;
|
71 | 75 | import org.opensearch.sql.expression.function.DefaultFunctionResolver;
|
| 76 | +import org.opensearch.sql.expression.function.FunctionBuilder; |
72 | 77 | import org.opensearch.sql.expression.function.FunctionDSL;
|
73 | 78 | import org.opensearch.sql.expression.function.FunctionName;
|
74 | 79 | import org.opensearch.sql.expression.function.FunctionProperties;
|
75 | 80 | 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; |
76 | 84 | import org.opensearch.sql.utils.DateTimeUtils;
|
77 | 85 |
|
78 | 86 | /**
|
@@ -232,30 +240,51 @@ private FunctionResolver current_date() {
|
232 | 240 | }
|
233 | 241 |
|
234 | 242 | /**
|
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. |
236 | 245 | * 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), |
247 | 258 | DATETIME, DATETIME, INTERVAL),
|
248 |
| - impl(nullMissingHandling(DateTimeFunction::exprAddDateInterval), |
| 259 | + implWithProperties(nullMissingHandlingWithProperties(function), |
249 | 260 | 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) |
254 | 278 | );
|
255 | 279 | }
|
256 | 280 |
|
257 | 281 | 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))); |
259 | 288 | }
|
260 | 289 |
|
261 | 290 | /**
|
@@ -388,34 +417,17 @@ private FunctionResolver datetime() {
|
388 | 417 | }
|
389 | 418 |
|
390 | 419 | 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)); |
415 | 424 | }
|
416 | 425 |
|
417 | 426 | 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)); |
419 | 431 | }
|
420 | 432 |
|
421 | 433 | /**
|
@@ -641,7 +653,12 @@ private DefaultFunctionResolver second(BuiltinFunctionName name) {
|
641 | 653 | }
|
642 | 654 |
|
643 | 655 | 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))); |
645 | 662 | }
|
646 | 663 |
|
647 | 664 | /**
|
@@ -876,29 +893,65 @@ private ExprValue dayOfWeekToday(Clock clock) {
|
876 | 893 | }
|
877 | 894 |
|
878 | 895 | /**
|
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`. |
880 | 910 | *
|
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. |
884 | 916 | */
|
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)); |
889 | 923 | }
|
890 | 924 |
|
891 | 925 | /**
|
892 | 926 | * ADDDATE function implementation for ExprValue.
|
893 | 927 | *
|
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. |
895 | 930 | * @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`. |
897 | 932 | */
|
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)); |
902 | 955 | }
|
903 | 956 |
|
904 | 957 | /**
|
@@ -1325,27 +1378,27 @@ private ExprValue exprSecond(ExprValue time) {
|
1325 | 1378 | /**
|
1326 | 1379 | * SUBDATE function implementation for ExprValue.
|
1327 | 1380 | *
|
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. |
1329 | 1383 | * @param days ExprValue of Long type, representing the number of days to subtract.
|
1330 | 1384 | * @return Date/Datetime resulted from days subtracted to date.
|
1331 | 1385 | */
|
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); |
1336 | 1389 | }
|
1337 | 1390 |
|
1338 | 1391 | /**
|
1339 |
| - * SUBDATE function implementation for ExprValue. |
| 1392 | + * DATE_SUB function implementation for ExprValue. |
1340 | 1393 | *
|
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. |
1342 | 1396 | * @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`. |
1344 | 1398 | */
|
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); |
1349 | 1402 | }
|
1350 | 1403 |
|
1351 | 1404 | /**
|
|
0 commit comments