Skip to content

Commit

Permalink
Rework to use FunctionProperties.
Browse files Browse the repository at this point in the history
Signed-off-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
Yury-Fridlyand committed Dec 10, 2022
1 parent 110c291 commit 5468caa
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,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.
Expand Down Expand Up @@ -56,19 +57,17 @@ public LocalTime timeValue() {
return time;
}

@Override
public LocalDate dateValue() {
return LocalDate.now();
public LocalDate dateValue(FunctionProperties functionProperties) {
return LocalDate.now(functionProperties.getQueryStartClock());
}

@Override
public LocalDateTime datetimeValue() {
return LocalDateTime.of(dateValue(), timeValue());
public LocalDateTime datetimeValue(FunctionProperties functionProperties) {
return LocalDateTime.of(dateValue(functionProperties), timeValue());
}

@Override
public Instant timestampValue() {
return ZonedDateTime.of(dateValue(), timeValue(), ExprTimestampValue.ZONE).toInstant();
public Instant timestampValue(FunctionProperties functionProperties) {
return ZonedDateTime.of(dateValue(functionProperties), timeValue(), ExprTimestampValue.ZONE)
.toInstant();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
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;
Expand Down Expand Up @@ -65,6 +67,7 @@
import org.opensearch.sql.expression.function.DefaultFunctionResolver;
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.utils.DateTimeUtils;

Expand Down Expand Up @@ -271,22 +274,38 @@ private DefaultFunctionResolver date() {
*/
private DefaultFunctionResolver datediff() {
return define(BuiltinFunctionName.DATEDIFF.getName(),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATE, DATE),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATETIME, DATE),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATE, DATETIME),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATETIME, DATETIME),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATE, TIME),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIME, DATE),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIME, TIME),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIMESTAMP, DATE),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATE, TIMESTAMP),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIMESTAMP, TIMESTAMP),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIMESTAMP, TIME),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIME, TIMESTAMP),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIMESTAMP, DATETIME),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATETIME, TIMESTAMP),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, TIME, DATETIME),
impl(nullMissingHandling(DateTimeFunction::exprDateDiff), LONG, DATETIME, TIME));
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));
}

/**
Expand Down Expand Up @@ -743,9 +762,12 @@ private ExprValue exprDate(ExprValue exprValue) {
* @param second The second value.
* @return The diff.
*/
private ExprValue exprDateDiff(ExprValue first, ExprValue second) {
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(second.dateValue(), first.dateValue()));
return new ExprLongValue(DAYS.between(
extractDate(second, functionProperties),
extractDate(first, functionProperties)));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<FunctionName, Pair<FunctionSignature, FunctionBuilder>>
implWithProperties(
SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue> 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<Expression, ExprValue> 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.
*
Expand Down Expand Up @@ -317,4 +363,22 @@ public SerializableTriFunction<ExprValue, ExprValue, ExprValue, ExprValue> nullM
}
};
}

/**
* Wrapper for the ExprValue function that takes 2 arguments and is aware of FunctionProperties,
* with default NULL and MISSING handling.
*/
public static SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue>
nullMissingHandlingWithProperties(
SerializableTriFunction<FunctionProperties, ExprValue, ExprValue,ExprValue> 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);
}
};
}
}
15 changes: 15 additions & 0 deletions core/src/main/java/org/opensearch/sql/utils/DateTimeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,41 @@
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 {

private static final int NANOS_PRECISION_MAX = 9;

@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());
assertEquals(LocalDate.now(), timeValue.dateValue());
assertEquals(LocalDate.now().atTime(1, 1, 1), timeValue.datetimeValue());
assertEquals(ZonedDateTime.of(LocalTime.parse("01:01:01").atDate(LocalDate.now()),
ExprTimestampValue.ZONE).toInstant(), timeValue.timestampValue());
// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,6 @@ public class DateTimeTestBase extends ExpressionTestBase {
@Mock
protected Environment<Expression, ExprValue> 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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public int compareTo(ExprValue o) {
static final SerializableFunction<ExprValue, ExprValue> oneArg = v -> ANY;
static final SerializableBiFunction<FunctionProperties, ExprValue, ExprValue>
oneArgWithProperties = (functionProperties, v) -> ANY;
static final SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue>
twoArgWithProperties = (functionProperties, v1, v2) -> ANY;

static final SerializableBiFunction<ExprValue, ExprValue, ExprValue>
twoArgs = (v1, v2) -> ANY;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<FunctionName, Pair<FunctionSignature, FunctionBuilder>>
getImplementationGenerator() {
SerializableTriFunction<FunctionProperties, ExprValue, ExprValue, ExprValue> functionBody
= (fp, arg1, arg2) -> ANY;
return FunctionDSL.implWithProperties(functionBody, ANY_TYPE, ANY_TYPE, ANY_TYPE);
}

@Override
List<Expression> getSampleArguments() {
return List.of(DSL.literal(ANY), DSL.literal(ANY));
}

@Override
String getExpected_toString() {
return "sample(ANY, ANY)";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down

0 comments on commit 5468caa

Please sign in to comment.