diff --git a/core/src/main/java/io/substrait/expression/AbstractExpressionVisitor.java b/core/src/main/java/io/substrait/expression/AbstractExpressionVisitor.java index e6ebb5782..340aa5b04 100644 --- a/core/src/main/java/io/substrait/expression/AbstractExpressionVisitor.java +++ b/core/src/main/java/io/substrait/expression/AbstractExpressionVisitor.java @@ -165,6 +165,19 @@ public O visit(Expression.TimeLiteral expr, C context) throws E { return visitFallback(expr, context); } + /** + * Visits a precision time literal. + * + * @param expr the precision time literal + * @param context the visitation context + * @return the visit result + * @throws E if visitation fails + */ + @Override + public O visit(Expression.PrecisionTimeLiteral expr, C context) throws E { + return visitFallback(expr, context); + } + /** * Visits a date literal. * diff --git a/core/src/main/java/io/substrait/expression/Expression.java b/core/src/main/java/io/substrait/expression/Expression.java index f8a5c1d3a..2197b0bd0 100644 --- a/core/src/main/java/io/substrait/expression/Expression.java +++ b/core/src/main/java/io/substrait/expression/Expression.java @@ -271,7 +271,12 @@ public R accept( } } + /** + * @deprecated {@link TimestampLiteral} is deprecated in favor of {@link + * PrecisionTimestampLiteral} + */ @Value.Immutable + @Deprecated abstract class TimestampLiteral implements Literal { public abstract long value(); @@ -291,7 +296,11 @@ public R accept( } } + /** + * @deprecated {@link TimeLiteral} is deprecated in favor of {@link PrecisionTimeLiteral} + */ @Value.Immutable + @Deprecated abstract class TimeLiteral implements Literal { public abstract long value(); @@ -311,6 +320,28 @@ public R accept( } } + @Value.Immutable + abstract class PrecisionTimeLiteral implements Literal { + public abstract long value(); + + public abstract int precision(); + + @Override + public Type getType() { + return Type.withNullability(nullable()).precisionTimestamp(precision()); + } + + public static ImmutableExpression.PrecisionTimeLiteral.Builder builder() { + return ImmutableExpression.PrecisionTimeLiteral.builder(); + } + + @Override + public R accept( + ExpressionVisitor visitor, C context) throws E { + return visitor.visit(this, context); + } + } + @Value.Immutable abstract class DateLiteral implements Literal { public abstract int value(); @@ -331,7 +362,12 @@ public R accept( } } + /** + * @deprecated {@link TimestampTZLiteral} is deprecated in favor of {@link + * PrecisionTimestampTZLiteral} + */ @Value.Immutable + @Deprecated abstract class TimestampTZLiteral implements Literal { public abstract long value(); diff --git a/core/src/main/java/io/substrait/expression/ExpressionCreator.java b/core/src/main/java/io/substrait/expression/ExpressionCreator.java index f10143551..8f276d142 100644 --- a/core/src/main/java/io/substrait/expression/ExpressionCreator.java +++ b/core/src/main/java/io/substrait/expression/ExpressionCreator.java @@ -72,10 +72,23 @@ public static Expression.DateLiteral date(boolean nullable, int value) { return Expression.DateLiteral.builder().nullable(nullable).value(value).build(); } + /** + * @deprecated Time is deprecated in favor of PrecisionTime + */ + @Deprecated public static Expression.TimeLiteral time(boolean nullable, long value) { return Expression.TimeLiteral.builder().nullable(nullable).value(value).build(); } + public static Expression.PrecisionTimeLiteral precisionTime( + boolean nullable, long value, int precision) { + return Expression.PrecisionTimeLiteral.builder() + .nullable(nullable) + .value(value) + .precision(precision) + .build(); + } + /** * @deprecated Timestamp is deprecated in favor of PrecisionTimestamp */ diff --git a/core/src/main/java/io/substrait/expression/ExpressionVisitor.java b/core/src/main/java/io/substrait/expression/ExpressionVisitor.java index 7f094b688..05540a924 100644 --- a/core/src/main/java/io/substrait/expression/ExpressionVisitor.java +++ b/core/src/main/java/io/substrait/expression/ExpressionVisitor.java @@ -121,6 +121,16 @@ public interface ExpressionVisitor bldr.setNullable(expr.nullable()).setTime(expr.value())); } + @Override + public Expression visit( + io.substrait.expression.Expression.PrecisionTimeLiteral expr, + EmptyVisitationContext context) { + return lit( + bldr -> + bldr.setNullable(expr.nullable()) + .setPrecisionTime( + Expression.Literal.PrecisionTime.newBuilder() + .setValue(expr.value()) + .setPrecision(expr.precision()) + .build())); + } + @Override public Expression visit( io.substrait.expression.Expression.DateLiteral expr, EmptyVisitationContext context) { diff --git a/core/src/main/java/io/substrait/expression/proto/ProtoExpressionConverter.java b/core/src/main/java/io/substrait/expression/proto/ProtoExpressionConverter.java index 01e25c907..e4a9fffea 100644 --- a/core/src/main/java/io/substrait/expression/proto/ProtoExpressionConverter.java +++ b/core/src/main/java/io/substrait/expression/proto/ProtoExpressionConverter.java @@ -413,6 +413,11 @@ public Expression.Literal from(io.substrait.proto.Expression.Literal literal) { return ExpressionCreator.date(literal.getNullable(), literal.getDate()); case TIME: return ExpressionCreator.time(literal.getNullable(), literal.getTime()); + case PRECISION_TIME: + return ExpressionCreator.precisionTime( + literal.getNullable(), + literal.getPrecisionTime().getValue(), + literal.getPrecisionTime().getPrecision()); case INTERVAL_YEAR_TO_MONTH: return ExpressionCreator.intervalYear( literal.getNullable(), diff --git a/core/src/main/java/io/substrait/relation/ExpressionCopyOnWriteVisitor.java b/core/src/main/java/io/substrait/relation/ExpressionCopyOnWriteVisitor.java index cdb72aea8..1e9254716 100644 --- a/core/src/main/java/io/substrait/relation/ExpressionCopyOnWriteVisitor.java +++ b/core/src/main/java/io/substrait/relation/ExpressionCopyOnWriteVisitor.java @@ -4,6 +4,7 @@ import static io.substrait.relation.CopyOnWriteUtils.transformList; import io.substrait.expression.Expression; +import io.substrait.expression.Expression.PrecisionTimeLiteral; import io.substrait.expression.ExpressionVisitor; import io.substrait.expression.FieldReference; import io.substrait.expression.FunctionArg; @@ -95,6 +96,12 @@ public Optional visit(Expression.TimeLiteral expr, EmptyVisitationCo return visitLiteral(expr); } + @Override + public Optional visit(PrecisionTimeLiteral expr, EmptyVisitationContext context) + throws E { + return visitLiteral(expr); + } + @Override public Optional visit(Expression.DateLiteral expr, EmptyVisitationContext context) throws E { diff --git a/core/src/main/java/io/substrait/type/Type.java b/core/src/main/java/io/substrait/type/Type.java index 5423b360a..86eaa733c 100644 --- a/core/src/main/java/io/substrait/type/Type.java +++ b/core/src/main/java/io/substrait/type/Type.java @@ -164,6 +164,7 @@ public R accept(final TypeVisitor typeVisitor) th } @Value.Immutable + @Deprecated abstract class Time implements Type { public static ImmutableType.Time.Builder builder() { return ImmutableType.Time.builder(); diff --git a/core/src/main/java/io/substrait/type/TypeVisitor.java b/core/src/main/java/io/substrait/type/TypeVisitor.java index ce6a08910..9cf772232 100644 --- a/core/src/main/java/io/substrait/type/TypeVisitor.java +++ b/core/src/main/java/io/substrait/type/TypeVisitor.java @@ -21,6 +21,7 @@ public interface TypeVisitor { R visit(Type.Date type) throws E; + @Deprecated R visit(Type.Time type) throws E; @Deprecated diff --git a/examples/substrait-spark/src/main/java/io/substrait/examples/util/ExpressionStringify.java b/examples/substrait-spark/src/main/java/io/substrait/examples/util/ExpressionStringify.java index a26ec963e..a22756cc9 100644 --- a/examples/substrait-spark/src/main/java/io/substrait/examples/util/ExpressionStringify.java +++ b/examples/substrait-spark/src/main/java/io/substrait/examples/util/ExpressionStringify.java @@ -25,6 +25,7 @@ import io.substrait.expression.Expression.MapLiteral; import io.substrait.expression.Expression.MultiOrList; import io.substrait.expression.Expression.NullLiteral; +import io.substrait.expression.Expression.PrecisionTimeLiteral; import io.substrait.expression.Expression.PrecisionTimestampLiteral; import io.substrait.expression.Expression.PrecisionTimestampTZLiteral; import io.substrait.expression.Expression.ScalarFunctionInvocation; @@ -112,6 +113,12 @@ public String visit(TimeLiteral expr, EmptyVisitationContext context) throws Run return ""; } + @Override + public String visit(PrecisionTimeLiteral expr, EmptyVisitationContext context) + throws RuntimeException { + return ""; + } + @Override public String visit(DateLiteral expr, EmptyVisitationContext context) throws RuntimeException { return ""; diff --git a/isthmus/src/main/java/io/substrait/isthmus/expression/ExpressionRexConverter.java b/isthmus/src/main/java/io/substrait/isthmus/expression/ExpressionRexConverter.java index 3deff1f81..5fd9efdcb 100644 --- a/isthmus/src/main/java/io/substrait/isthmus/expression/ExpressionRexConverter.java +++ b/isthmus/src/main/java/io/substrait/isthmus/expression/ExpressionRexConverter.java @@ -7,6 +7,7 @@ import io.substrait.expression.EnumArg; import io.substrait.expression.Expression; import io.substrait.expression.Expression.FailureBehavior; +import io.substrait.expression.Expression.PrecisionTimeLiteral; import io.substrait.expression.Expression.PrecisionTimestampLiteral; import io.substrait.expression.Expression.PrecisionTimestampTZLiteral; import io.substrait.expression.Expression.ScalarSubquery; @@ -205,19 +206,48 @@ public RexNode visit(Expression.BinaryLiteral expr, Context context) throws Runt @Override public RexNode visit(Expression.TimeLiteral expr, Context context) throws RuntimeException { - // Expression.TimeLiteral is Microseconds - // Construct a TimeString : - // 1. Truncate microseconds to seconds - // 2. Get the fraction seconds in precision of nanoseconds. - // 3. Construct TimeString : seconds + fraction_seconds part. - long microSec = expr.value(); - long seconds = TimeUnit.MICROSECONDS.toSeconds(microSec); - int fracSecondsInNano = - (int) (TimeUnit.MICROSECONDS.toNanos(microSec) - TimeUnit.SECONDS.toNanos(seconds)); - TimeString timeString = - TimeString.fromMillisOfDay((int) TimeUnit.SECONDS.toMillis(seconds)) - .withNanos(fracSecondsInNano); - return rexBuilder.makeLiteral(timeString, typeConverter.toCalcite(typeFactory, expr.getType())); + return rexBuilder.makeLiteral( + createTimeString(expr.value(), 6), typeConverter.toCalcite(typeFactory, expr.getType())); + } + + @Override + public RexNode visit(PrecisionTimeLiteral expr, Context context) throws RuntimeException { + return rexBuilder.makeLiteral( + createTimeString(expr.value(), expr.precision()), + typeConverter.toCalcite(typeFactory, expr.getType())); + } + + protected TimeString createTimeString(long value, int precision) { + switch (precision) { + case 0: + return TimeString.fromMillisOfDay((int) TimeUnit.SECONDS.toMillis(value)); + case 3: + { + long seconds = TimeUnit.MILLISECONDS.toSeconds(value); + int fracSecondsInNano = + (int) (TimeUnit.MILLISECONDS.toNanos(value) - TimeUnit.SECONDS.toNanos(seconds)); + return TimeString.fromMillisOfDay((int) TimeUnit.SECONDS.toMillis(seconds)) + .withNanos(fracSecondsInNano); + } + case 6: + { + long seconds = TimeUnit.MICROSECONDS.toSeconds(value); + int fracSecondsInNano = + (int) (TimeUnit.MICROSECONDS.toNanos(value) - TimeUnit.SECONDS.toNanos(seconds)); + return TimeString.fromMillisOfDay((int) TimeUnit.SECONDS.toMillis(seconds)) + .withNanos(fracSecondsInNano); + } + case 9: + { + long seconds = TimeUnit.NANOSECONDS.toSeconds(value); + int fracSecondsInNano = (int) (value - TimeUnit.SECONDS.toNanos(seconds)); + return TimeString.fromMillisOfDay((int) TimeUnit.SECONDS.toMillis(seconds)) + .withNanos(fracSecondsInNano); + } + default: + throw new UnsupportedOperationException( + String.format("Cannot handle PrecisionTime with precision %d.", precision)); + } } @Override diff --git a/spark/src/main/scala/io/substrait/spark/expression/IgnoreNullableAndParameters.scala b/spark/src/main/scala/io/substrait/spark/expression/IgnoreNullableAndParameters.scala index 9cb38b8d0..c280e1fb1 100644 --- a/spark/src/main/scala/io/substrait/spark/expression/IgnoreNullableAndParameters.scala +++ b/spark/src/main/scala/io/substrait/spark/expression/IgnoreNullableAndParameters.scala @@ -44,6 +44,7 @@ class IgnoreNullableAndParameters(val typeToMatch: ParameterizedType) override def visit(`type`: Type.Date): Boolean = typeToMatch.isInstanceOf[Type.Date] + @nowarn override def visit(`type`: Type.Time): Boolean = typeToMatch.isInstanceOf[Type.Time] @nowarn