Skip to content

Commit

Permalink
Add implementation of now, sysdate, localtime and similar funct…
Browse files Browse the repository at this point in the history
…ions (#92)

Signed-off-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
Yury-Fridlyand committed Aug 17, 2022
1 parent ce15448 commit d5e8a21
Show file tree
Hide file tree
Showing 22 changed files with 1,488 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@

package org.opensearch.sql.common.utils;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.apache.logging.log4j.ThreadContext;

/**
* Utility class for recording and accessing context for the query being executed.
* Implementation Details: context variables is being persisted statically in the thread context
* @see: @ThreadContext
*/
public class QueryContext {

Expand All @@ -21,6 +24,11 @@ public class QueryContext {
*/
private static final String REQUEST_ID_KEY = "request_id";

/**
* Timestamp when SQL plugin started to process current request.
*/
private static final String REQUEST_PROCESSING_STARTED = "request_processing_started";

/**
* Generates a random UUID and adds to the {@link ThreadContext} as the request id.
* <p>
Expand Down Expand Up @@ -48,6 +56,22 @@ public static String getRequestId() {
return id;
}

public static void recordProcessingStarted() {
ThreadContext.put(REQUEST_PROCESSING_STARTED, LocalDateTime.now().toString());
}

/**
* Get recorded previously time indicating when processing started for the current query.
* @return A LocalDateTime object
*/
public static LocalDateTime getProcessingStartedTime() {
if (ThreadContext.containsKey(REQUEST_PROCESSING_STARTED)) {
return LocalDateTime.parse(ThreadContext.get(REQUEST_PROCESSING_STARTED));
}
// This shouldn't happen outside of unit tests
return LocalDateTime.now();
}

/**
* Wraps a given instance of {@link Runnable} into a new one which gets all the
* entries from current ThreadContext map.
Expand Down
36 changes: 36 additions & 0 deletions core/src/main/java/org/opensearch/sql/expression/DSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,42 @@ public FunctionExpression match_bool_prefix(Expression... args) {
return compile(BuiltinFunctionName.MATCH_BOOL_PREFIX, args);
}

public FunctionExpression now(Expression... args) {
return compile(BuiltinFunctionName.NOW, args);
}

public FunctionExpression current_timestamp(Expression... args) {
return compile(BuiltinFunctionName.CURRENT_TIMESTAMP, args);
}

public FunctionExpression localtimestamp(Expression... args) {
return compile(BuiltinFunctionName.LOCALTIMESTAMP, args);
}

public FunctionExpression localtime(Expression... args) {
return compile(BuiltinFunctionName.LOCALTIME, args);
}

public FunctionExpression sysdate(Expression... args) {
return compile(BuiltinFunctionName.SYSDATE, args);
}

public FunctionExpression curtime(Expression... args) {
return compile(BuiltinFunctionName.CURTIME, args);
}

public FunctionExpression current_time(Expression... args) {
return compile(BuiltinFunctionName.CURRENT_TIME, args);
}

public FunctionExpression curdate(Expression... args) {
return compile(BuiltinFunctionName.CURDATE, args);
}

public FunctionExpression current_date(Expression... args) {
return compile(BuiltinFunctionName.CURRENT_DATE, args);
}

private FunctionExpression compile(BuiltinFunctionName bfn, Expression... args) {
return (FunctionExpression) repository.compile(bfn.getName(), Arrays.asList(args.clone()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,19 @@
import static org.opensearch.sql.expression.function.FunctionDSL.impl;
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import lombok.experimental.UtilityClass;
import org.opensearch.sql.common.utils.QueryContext;
import org.opensearch.sql.data.model.ExprDateValue;
import org.opensearch.sql.data.model.ExprDatetimeValue;
import org.opensearch.sql.data.model.ExprIntegerValue;
Expand Down Expand Up @@ -84,6 +90,89 @@ public void register(BuiltinFunctionRepository repository) {
repository.register(to_days());
repository.register(week());
repository.register(year());

repository.register(now());
repository.register(current_timestamp());
repository.register(localtimestamp());
repository.register(localtime());
repository.register(sysdate());
repository.register(curtime());
repository.register(current_time());
repository.register(curdate());
repository.register(current_date());
}

/**
* NOW() returns a constant time that indicates the time at which the statement began to execute.
*/
private LocalDateTime now(@Nullable Integer fsp) {
return formatLocalDateTime(QueryContext::getProcessingStartedTime, fsp);
}

private FunctionResolver now(FunctionName functionName) {
return define(functionName,
impl(() -> new ExprDatetimeValue(now((Integer)null)), DATETIME),
impl((v) -> new ExprDatetimeValue(now(v.integerValue())), DATETIME, INTEGER)
);
}

private FunctionResolver now() {
return now(BuiltinFunctionName.NOW.getName());
}

private FunctionResolver current_timestamp() {
return now(BuiltinFunctionName.CURRENT_TIMESTAMP.getName());
}

private FunctionResolver localtimestamp() {
return now(BuiltinFunctionName.LOCALTIMESTAMP.getName());
}

private FunctionResolver localtime() {
return now(BuiltinFunctionName.LOCALTIME.getName());
}

/**
* SYSDATE() returns the time at which it executes.
*/
private LocalDateTime sysDate(@Nullable Integer fsp) {
return formatLocalDateTime(LocalDateTime::now, fsp);
}

private FunctionResolver sysdate() {
return define(BuiltinFunctionName.SYSDATE.getName(),
impl(() -> new ExprDatetimeValue(sysDate(null)), DATETIME),
impl((v) -> new ExprDatetimeValue(sysDate(v.integerValue())), DATETIME, INTEGER)
);
}

private FunctionResolver curtime(FunctionName functionName) {
return define(functionName,
impl(() -> new ExprTimeValue(sysDate(null).toLocalTime()), TIME),
impl((v) -> new ExprTimeValue(sysDate(v.integerValue()).toLocalTime()), TIME, INTEGER)
);
}

private FunctionResolver curtime() {
return curtime(BuiltinFunctionName.CURTIME.getName());
}

private FunctionResolver current_time() {
return curtime(BuiltinFunctionName.CURRENT_TIME.getName());
}

private FunctionResolver curdate(FunctionName functionName) {
return define(functionName,
impl(() -> new ExprDateValue(sysDate(null).toLocalDate()), DATE)
);
}

private FunctionResolver curdate() {
return curdate(BuiltinFunctionName.CURDATE.getName());
}

private FunctionResolver current_date() {
return curdate(BuiltinFunctionName.CURRENT_DATE.getName());
}

/**
Expand Down Expand Up @@ -114,6 +203,10 @@ private FunctionResolver adddate() {
return add_date(BuiltinFunctionName.ADDDATE.getName());
}

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

/**
* Extracts the date part of a date and time value.
* Also to construct a date type. The supported signatures:
Expand All @@ -127,10 +220,6 @@ private FunctionResolver date() {
impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP));
}

private FunctionResolver 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:
Expand Down Expand Up @@ -739,4 +828,26 @@ private ExprValue exprYear(ExprValue date) {
return new ExprIntegerValue(date.dateValue().getYear());
}

/**
* Prepare LocalDateTime value.
* @param supplier A function which returns LocalDateTime to format.
* @param fsp argument is given to specify a fractional seconds precision from 0 to 6,
* the return value includes a fractional seconds part of that many digits.
* @return LocalDateTime object.
*/
private LocalDateTime formatLocalDateTime(Supplier<LocalDateTime> supplier,
@Nullable Integer fsp) {
var res = supplier.get();
if (fsp == null) {
return res;
}
var defaultPrecision = 9; // There are 10^9 nanoseconds in one second
if (fsp < 0 || fsp > 6) { // Check that the argument is in the allowed range [0, 6]
throw new IllegalArgumentException(
String.format("Invalid `fsp` value: %d, allowed 0 to 6", fsp));
}
var nano = new BigDecimal(res.getNano())
.setScale(fsp - defaultPrecision, RoundingMode.DOWN).intValue();
return res.withNano(nano);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,16 @@ public enum BuiltinFunctionName {
TO_DAYS(FunctionName.of("to_days")),
WEEK(FunctionName.of("week")),
YEAR(FunctionName.of("year")),

// `now`-like functions
NOW(FunctionName.of("now")),
CURDATE(FunctionName.of("curdate")),
CURRENT_DATE(FunctionName.of("current_date")),
CURTIME(FunctionName.of("curtime")),
CURRENT_TIME(FunctionName.of("current_time")),
LOCALTIME(FunctionName.of("localtime")),
CURRENT_TIMESTAMP(FunctionName.of("current_timestamp")),
LOCALTIMESTAMP(FunctionName.of("localtimestamp")),
SYSDATE(FunctionName.of("sysdate")),
/**
* Text Functions.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.opensearch.sql.analysis.symbol.Namespace;
import org.opensearch.sql.analysis.symbol.Symbol;
import org.opensearch.sql.ast.dsl.AstDSL;
Expand All @@ -45,6 +50,7 @@
import org.opensearch.sql.exception.SemanticCheckException;
import org.opensearch.sql.expression.DSL;
import org.opensearch.sql.expression.Expression;
import org.opensearch.sql.expression.FunctionExpression;
import org.opensearch.sql.expression.HighlightExpression;
import org.opensearch.sql.expression.config.ExpressionConfig;
import org.opensearch.sql.expression.window.aggregation.AggregateWindowFunction;
Expand Down Expand Up @@ -537,6 +543,49 @@ public void match_phrase_prefix_all_params() {
);
}

private static Stream<Arguments> functionNames() {
var dsl = new DSL(new ExpressionConfig().functionRepository());
return Stream.of(
Arguments.of((Function<Expression[], FunctionExpression>)dsl::now,
"now", true),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::current_timestamp,
"current_timestamp", true),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::localtimestamp,
"localtimestamp", true),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::localtime,
"localtime", true),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::sysdate,
"sysdate", true),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::curtime,
"curtime", true),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::current_time,
"current_time", true),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::curdate,
"curdate", false),
Arguments.of((Function<Expression[], FunctionExpression>)dsl::current_date,
"current_date", false));
}

@ParameterizedTest(name = "{1}")
@MethodSource("functionNames")
public void now_like_functions(Function<Expression[], FunctionExpression> function,
String name,
Boolean hasFsp) {
assertAnalyzeEqual(
function.apply(new Expression[]{}),
AstDSL.function(name));

if (hasFsp) {
assertAnalyzeEqual(
function.apply(new Expression[]{DSL.ref("integer_value", INTEGER)}),
AstDSL.function(name, field("integer_value")));

assertAnalyzeEqual(
function.apply(new Expression[]{DSL.literal(3)}),
AstDSL.function(name, intLiteral(3)));
}
}

@Test
void highlight() {
assertAnalyzeEqual(new HighlightExpression(DSL.literal("fieldA")),
Expand Down
Loading

0 comments on commit d5e8a21

Please sign in to comment.