Skip to content
Open
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
28 changes: 28 additions & 0 deletions core/src/main/java/io/substrait/dsl/SubstraitBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,34 @@ public Expression.StrLiteral str(String s) {
return Expression.StrLiteral.builder().value(s).build();
}

/**
* Creates a {@code CURRENT_TIMESTAMP} execution context variable expression.
*
* @param precision the fractional-second precision of the timestamp
* @return a new {@link Expression.CurrentTimestamp}
*/
public Expression.CurrentTimestamp currentTimestamp(int precision) {
return Expression.CurrentTimestamp.builder().precision(precision).build();
}

/**
* Creates a {@code CURRENT_TIMEZONE} execution context variable expression.
*
* @return a new {@link Expression.CurrentTimezone}
*/
public Expression.CurrentTimezone currentTimezone() {
return Expression.CurrentTimezone.builder().build();
}

/**
* Creates a {@code CURRENT_DATE} execution context variable expression.
*
* @return a new {@link Expression.CurrentDate}
*/
public Expression.CurrentDate currentDate() {
return Expression.CurrentDate.builder().build();
}

/**
* Creates a cast expression that converts an expression to a different type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,4 +616,43 @@ public O visit(Expression.InPredicate expr, C context) throws E {
public O visit(Expression.DynamicParameter expr, C context) throws E {
return visitFallback(expr, context);
}

/**
* Visits a current timestamp execution context variable expression.
*
* @param expr the current timestamp expression
* @param context the visitation context
* @return the visit result
* @throws E if visitation fails
*/
@Override
public O visit(Expression.CurrentTimestamp expr, C context) throws E {
return visitFallback(expr, context);
}

/**
* Visits a current timezone execution context variable expression.
*
* @param expr the current timezone expression
* @param context the visitation context
* @return the visit result
* @throws E if visitation fails
*/
@Override
public O visit(Expression.CurrentTimezone expr, C context) throws E {
return visitFallback(expr, context);
}

/**
* Visits a current date execution context variable expression.
*
* @param expr the current date expression
* @param context the visitation context
* @return the visit result
* @throws E if visitation fails
*/
@Override
public O visit(Expression.CurrentDate expr, C context) throws E {
return visitFallback(expr, context);
}
}
106 changes: 106 additions & 0 deletions core/src/main/java/io/substrait/expression/Expression.java
Original file line number Diff line number Diff line change
Expand Up @@ -2113,6 +2113,112 @@ public <R, C extends VisitationContext, E extends Throwable> R accept(
}
}

/**
* Marker interface for execution-context-dependent variables such as {@code CURRENT_TIMESTAMP},
* {@code CURRENT_TIMEZONE}, and {@code CURRENT_DATE}.
*
* <p>The Substrait spec models these as expressions rather than functions because they take no
* input arguments, depend on the execution context rather than input data, and require evaluation
* mode control (see {@link io.substrait.plan.Plan.ExecutionBehavior}). The result type of an
* execution context variable always has {@code NULLABILITY_REQUIRED} nullability.
*/
interface ExecutionContextVariable extends Expression {}

/**
* Execution context variable holding the current timestamp in the current session timezone.
*
* <p>Its result type is a {@code precision_timestamp_tz} with the configured {@link #precision()}
* and required nullability.
*/
@Value.Immutable
abstract class CurrentTimestamp implements ExecutionContextVariable {
/**
* Returns the fractional-second precision of the current timestamp, expressed as the number of
* digits after the decimal point (e.g. {@code 6} for microseconds).
*
* @return the timestamp precision
*/
public abstract int precision();

@Override
public Type getType() {
return TypeCreator.REQUIRED.precisionTimestampTZ(precision());
}

/**
* Creates a new builder for constructing a CurrentTimestamp.
*
* @return a new builder instance
*/
public static ImmutableExpression.CurrentTimestamp.Builder builder() {
return ImmutableExpression.CurrentTimestamp.builder();
}

@Override
public <R, C extends VisitationContext, E extends Throwable> R accept(
ExpressionVisitor<R, C, E> visitor, C context) throws E {
return visitor.visit(this, context);
}
}

/**
* Execution context variable holding the current session timezone as a string defined by the IANA
* timezone database (<a
* href="https://www.iana.org/time-zones">https://www.iana.org/time-zones</a>).
*
* <p>Its result type is a {@code string} with required nullability.
*/
@Value.Immutable
abstract class CurrentTimezone implements ExecutionContextVariable {
@Override
public Type getType() {
return TypeCreator.REQUIRED.STRING;
}

/**
* Creates a new builder for constructing a CurrentTimezone.
*
* @return a new builder instance
*/
public static ImmutableExpression.CurrentTimezone.Builder builder() {
return ImmutableExpression.CurrentTimezone.builder();
}

@Override
public <R, C extends VisitationContext, E extends Throwable> R accept(
ExpressionVisitor<R, C, E> visitor, C context) throws E {
return visitor.visit(this, context);
}
}

/**
* Execution context variable holding the current date.
*
* <p>Its result type is a {@code date} with required nullability.
*/
@Value.Immutable
abstract class CurrentDate implements ExecutionContextVariable {
@Override
public Type getType() {
return TypeCreator.REQUIRED.DATE;
}

/**
* Creates a new builder for constructing a CurrentDate.
*
* @return a new builder instance
*/
public static ImmutableExpression.CurrentDate.Builder builder() {
return ImmutableExpression.CurrentDate.builder();
}

@Override
public <R, C extends VisitationContext, E extends Throwable> R accept(
ExpressionVisitor<R, C, E> visitor, C context) throws E {
return visitor.visit(this, context);
}
}

/** Defines the operation type for set predicates (EXISTS, UNIQUE). */
enum PredicateOp {
/** Unspecified predicate operation. */
Expand Down
28 changes: 28 additions & 0 deletions core/src/main/java/io/substrait/expression/ExpressionCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -597,4 +597,32 @@ public static Expression cast(
.failureBehavior(failureBehavior)
.build();
}

/**
* Creates a {@code CURRENT_TIMESTAMP} execution context variable.
*
* @param precision the fractional-second precision of the timestamp
* @return the current timestamp expression
*/
public static Expression.CurrentTimestamp currentTimestamp(int precision) {
return Expression.CurrentTimestamp.builder().precision(precision).build();
}

/**
* Creates a {@code CURRENT_TIMEZONE} execution context variable.
*
* @return the current timezone expression
*/
public static Expression.CurrentTimezone currentTimezone() {
return Expression.CurrentTimezone.builder().build();
}

/**
* Creates a {@code CURRENT_DATE} execution context variable.
*
* @return the current date expression
*/
public static Expression.CurrentDate currentDate() {
return Expression.CurrentDate.builder().build();
}
}
30 changes: 30 additions & 0 deletions core/src/main/java/io/substrait/expression/ExpressionVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -480,4 +480,34 @@ public interface ExpressionVisitor<R, C extends VisitationContext, E extends Thr
* @throws E on visit failure
*/
R visit(Expression.DynamicParameter expr, C context) throws E;

/**
* Visit a current timestamp execution context variable expression.
*
* @param expr the current timestamp expression
* @param context visitation context
* @return visit result
* @throws E on visit failure
*/
R visit(Expression.CurrentTimestamp expr, C context) throws E;

/**
* Visit a current timezone execution context variable expression.
*
* @param expr the current timezone expression
* @param context visitation context
* @return visit result
* @throws E on visit failure
*/
R visit(Expression.CurrentTimezone expr, C context) throws E;

/**
* Visit a current date execution context variable expression.
*
* @param expr the current date expression
* @param context visitation context
* @return visit result
* @throws E on visit failure
*/
R visit(Expression.CurrentDate expr, C context) throws E;
}
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,39 @@ public Expression visit(
.build();
}

@Override
public Expression visit(
io.substrait.expression.Expression.CurrentTimestamp expr, EmptyVisitationContext context)
throws RuntimeException {
return Expression.newBuilder()
.setExecutionContextVariable(
Expression.ExecutionContextVariable.newBuilder()
.setCurrentTimestamp(toProto(expr.getType()).getPrecisionTimestampTz()))
.build();
}

@Override
public Expression visit(
io.substrait.expression.Expression.CurrentTimezone expr, EmptyVisitationContext context)
throws RuntimeException {
return Expression.newBuilder()
.setExecutionContextVariable(
Expression.ExecutionContextVariable.newBuilder()
.setCurrentTimezone(toProto(expr.getType()).getString()))
.build();
}

@Override
public Expression visit(
io.substrait.expression.Expression.CurrentDate expr, EmptyVisitationContext context)
throws RuntimeException {
return Expression.newBuilder()
.setExecutionContextVariable(
Expression.ExecutionContextVariable.newBuilder()
.setCurrentDate(toProto(expr.getType()).getDate()))
.build();
}

public static class BoundConverter
implements WindowBound.WindowBoundVisitor<Expression.WindowFunction.Bound, RuntimeException> {
private static final BoundConverter TO_BOUND_VISITOR = new BoundConverter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,25 @@ public Type visit(Type.Struct type) throws RuntimeException {
.parameterReference(dp.getParameterReference())
.build();
}
case EXECUTION_CONTEXT_VARIABLE:
{
io.substrait.proto.Expression.ExecutionContextVariable ecv =
expr.getExecutionContextVariable();
switch (ecv.getExecutionContextVariableTypeCase()) {
case CURRENT_TIMESTAMP:
return Expression.CurrentTimestamp.builder()
.precision(ecv.getCurrentTimestamp().getPrecision())
.build();
case CURRENT_TIMEZONE:
return Expression.CurrentTimezone.builder().build();
case CURRENT_DATE:
return Expression.CurrentDate.builder().build();
default:
throw new IllegalArgumentException(
"Unknown execution context variable type: "
+ ecv.getExecutionContextVariableTypeCase());
}
}
// TODO enum.
case ENUM:
throw new UnsupportedOperationException("Unsupported type: " + expr.getRexTypeCase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,24 @@ public Optional<Expression> visit(
return Optional.empty();
}

@Override
public Optional<Expression> visit(
Expression.CurrentTimestamp expr, EmptyVisitationContext context) throws E {
return Optional.empty();
}

@Override
public Optional<Expression> visit(Expression.CurrentTimezone expr, EmptyVisitationContext context)
throws E {
return Optional.empty();
}

@Override
public Optional<Expression> visit(Expression.CurrentDate expr, EmptyVisitationContext context)
throws E {
return Optional.empty();
}

// utilities

protected Optional<List<Expression>> visitExprList(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.substrait.type.proto;

import static org.junit.jupiter.api.Assertions.assertEquals;

import io.substrait.TestBase;
import io.substrait.expression.Expression;
import io.substrait.type.TypeCreator;
import org.junit.jupiter.api.Test;

class ExecutionContextVariableRoundtripTest extends TestBase {

@Test
void currentTimestampMicros() {
Expression.CurrentTimestamp expr = Expression.CurrentTimestamp.builder().precision(6).build();

assertEquals(TypeCreator.REQUIRED.precisionTimestampTZ(6), expr.getType());
verifyRoundTrip(expr);
}

@Test
void currentTimestampSeconds() {
Expression.CurrentTimestamp expr = Expression.CurrentTimestamp.builder().precision(0).build();

assertEquals(TypeCreator.REQUIRED.precisionTimestampTZ(0), expr.getType());
verifyRoundTrip(expr);
}

@Test
void currentTimezone() {
Expression.CurrentTimezone expr = Expression.CurrentTimezone.builder().build();

assertEquals(TypeCreator.REQUIRED.STRING, expr.getType());
verifyRoundTrip(expr);
}

@Test
void currentDate() {
Expression.CurrentDate expr = Expression.CurrentDate.builder().build();

assertEquals(TypeCreator.REQUIRED.DATE, expr.getType());
verifyRoundTrip(expr);
}
}
Loading
Loading