Skip to content

Commit e24ce6f

Browse files
authored
feat: support new IntervalCompound and updated IntervalDay types (#288)
BREAKING CHANGE: IntervalDay now has "subsecond" and "precision" fields instead of "microseconds". Old protobufs should be still read correctly.
1 parent c8c31ec commit e24ce6f

40 files changed

+492
-42
lines changed

core/src/main/antlr/SubstraitType.g4

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Date : D A T E;
4949
Time : T I M E;
5050
IntervalYear: I N T E R V A L '_' Y E A R;
5151
IntervalDay: I N T E R V A L '_' D A Y;
52+
IntervalCompound: I N T E R V A L '_' C O M P O U N D;
5253
UUID : U U I D;
5354
Decimal : D E C I M A L;
5455
PrecisionTimestamp: P R E C I S I O N '_' T I M E S T A M P;
@@ -158,7 +159,6 @@ scalarType
158159
| TimestampTZ #timestampTz
159160
| Date #date
160161
| Time #time
161-
| IntervalDay #intervalDay
162162
| IntervalYear #intervalYear
163163
| UUID #uuid
164164
| UserDefined Identifier #userDefined
@@ -169,6 +169,8 @@ parameterizedType
169169
| VarChar isnull='?'? Lt len=numericParameter Gt #varChar
170170
| FixedBinary isnull='?'? Lt len=numericParameter Gt #fixedBinary
171171
| Decimal isnull='?'? Lt precision=numericParameter Comma scale=numericParameter Gt #decimal
172+
| IntervalDay isnull='?'? Lt precision=numericParameter Gt #intervalDay
173+
| IntervalCompound isnull='?'? Lt precision=numericParameter Gt #intervalCompound
172174
| PrecisionTimestamp isnull='?'? Lt precision=numericParameter Gt #precisionTimestamp
173175
| PrecisionTimestampTZ isnull='?'? Lt precision=numericParameter Gt #precisionTimestampTZ
174176
| Struct isnull='?'? Lt expr (Comma expr)* Gt #struct
@@ -205,4 +207,3 @@ expr
205207
| (Bang) expr #NotExpr
206208
| ifExpr=expr QMark thenExpr=expr Colon elseExpr=expr #Ternary
207209
;
208-

core/src/main/java/io/substrait/expression/AbstractExpressionVisitor.java

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ public OUTPUT visit(Expression.IntervalDayLiteral expr) throws EXCEPTION {
9494
return visitFallback(expr);
9595
}
9696

97+
@Override
98+
public OUTPUT visit(Expression.IntervalCompoundLiteral expr) throws EXCEPTION {
99+
return visitFallback(expr);
100+
}
101+
97102
@Override
98103
public OUTPUT visit(Expression.UUIDLiteral expr) throws EXCEPTION {
99104
return visitFallback(expr);

core/src/main/java/io/substrait/expression/Expression.java

+33-2
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,12 @@ abstract static class IntervalDayLiteral implements Literal {
333333

334334
public abstract int seconds();
335335

336-
public abstract int microseconds();
336+
public abstract long subseconds();
337+
338+
public abstract int precision();
337339

338340
public Type getType() {
339-
return Type.withNullability(nullable()).INTERVAL_DAY;
341+
return Type.withNullability(nullable()).intervalDay(precision());
340342
}
341343

342344
public static ImmutableExpression.IntervalDayLiteral.Builder builder() {
@@ -348,6 +350,35 @@ public <R, E extends Throwable> R accept(ExpressionVisitor<R, E> visitor) throws
348350
}
349351
}
350352

353+
@Value.Immutable
354+
abstract static class IntervalCompoundLiteral implements Literal {
355+
// Flattened IntervalYearLiteral
356+
public abstract int years();
357+
358+
public abstract int months();
359+
360+
// Flattened IntervalDayLiteral
361+
public abstract int days();
362+
363+
public abstract int seconds();
364+
365+
public abstract long subseconds();
366+
367+
public abstract int precision();
368+
369+
public Type getType() {
370+
return Type.withNullability(nullable()).intervalCompound(precision());
371+
}
372+
373+
public static ImmutableExpression.IntervalCompoundLiteral.Builder builder() {
374+
return ImmutableExpression.IntervalCompoundLiteral.builder();
375+
}
376+
377+
public <R, E extends Throwable> R accept(ExpressionVisitor<R, E> visitor) throws E {
378+
return visitor.visit(this);
379+
}
380+
}
381+
351382
@Value.Immutable
352383
abstract static class UUIDLiteral implements Literal {
353384
public abstract UUID value();

core/src/main/java/io/substrait/expression/ExpressionCreator.java

+23-3
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,36 @@ public static Expression.IntervalYearLiteral intervalYear(
161161
}
162162

163163
public static Expression.IntervalDayLiteral intervalDay(boolean nullable, int days, int seconds) {
164-
return intervalDay(nullable, days, seconds, 0);
164+
return intervalDay(nullable, days, seconds, 0, 0);
165165
}
166166

167167
public static Expression.IntervalDayLiteral intervalDay(
168-
boolean nullable, int days, int seconds, int microseconds) {
168+
boolean nullable, int days, int seconds, long subseconds, int precision) {
169169
return Expression.IntervalDayLiteral.builder()
170170
.nullable(nullable)
171171
.days(days)
172172
.seconds(seconds)
173-
.microseconds(microseconds)
173+
.subseconds(subseconds)
174+
.precision(precision)
175+
.build();
176+
}
177+
178+
public static Expression.IntervalCompoundLiteral intervalCompound(
179+
boolean nullable,
180+
int years,
181+
int months,
182+
int days,
183+
int seconds,
184+
long subseconds,
185+
int precision) {
186+
return Expression.IntervalCompoundLiteral.builder()
187+
.nullable(nullable)
188+
.years(years)
189+
.months(months)
190+
.days(days)
191+
.seconds(seconds)
192+
.subseconds(subseconds)
193+
.precision(precision)
174194
.build();
175195
}
176196

core/src/main/java/io/substrait/expression/ExpressionVisitor.java

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public interface ExpressionVisitor<R, E extends Throwable> {
3939

4040
R visit(Expression.IntervalDayLiteral expr) throws E;
4141

42+
R visit(Expression.IntervalCompoundLiteral expr) throws E;
43+
4244
R visit(Expression.UUIDLiteral expr) throws E;
4345

4446
R visit(Expression.FixedCharLiteral expr) throws E;

core/src/main/java/io/substrait/expression/proto/ExpressionProtoConverter.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,27 @@ public Expression visit(io.substrait.expression.Expression.IntervalDayLiteral ex
159159
Expression.Literal.IntervalDayToSecond.newBuilder()
160160
.setDays(expr.days())
161161
.setSeconds(expr.seconds())
162-
.setMicroseconds(expr.microseconds())));
162+
.setSubseconds(expr.subseconds())
163+
.setPrecision(expr.precision())));
164+
}
165+
166+
@Override
167+
public Expression visit(io.substrait.expression.Expression.IntervalCompoundLiteral expr) {
168+
return lit(
169+
bldr ->
170+
bldr.setNullable(expr.nullable())
171+
.setIntervalCompound(
172+
Expression.Literal.IntervalCompound.newBuilder()
173+
.setIntervalYearToMonth(
174+
Expression.Literal.IntervalYearToMonth.newBuilder()
175+
.setYears(expr.years())
176+
.setMonths(expr.months()))
177+
.setIntervalDayToSecond(
178+
Expression.Literal.IntervalDayToSecond.newBuilder()
179+
.setDays(expr.days())
180+
.setSeconds(expr.seconds())
181+
.setSubseconds(expr.subseconds())
182+
.setPrecision(expr.precision()))));
163183
}
164184

165185
@Override

core/src/main/java/io/substrait/expression/proto/ProtoExpressionConverter.java

+32-5
Original file line numberDiff line numberDiff line change
@@ -340,11 +340,38 @@ public Expression.Literal from(io.substrait.proto.Expression.Literal literal) {
340340
literal.getNullable(),
341341
literal.getIntervalYearToMonth().getYears(),
342342
literal.getIntervalYearToMonth().getMonths());
343-
case INTERVAL_DAY_TO_SECOND -> ExpressionCreator.intervalDay(
344-
literal.getNullable(),
345-
literal.getIntervalDayToSecond().getDays(),
346-
literal.getIntervalDayToSecond().getSeconds(),
347-
literal.getIntervalDayToSecond().getMicroseconds());
343+
case INTERVAL_DAY_TO_SECOND -> {
344+
// Handle deprecated version that doesn't provide precision and that uses microseconds
345+
// instead of subseconds, for backwards compatibility
346+
int precision =
347+
literal.getIntervalDayToSecond().hasPrecision()
348+
? literal.getIntervalDayToSecond().getPrecision()
349+
: 6; // microseconds
350+
long subseconds =
351+
literal.getIntervalDayToSecond().hasPrecision()
352+
? literal.getIntervalDayToSecond().getSubseconds()
353+
: literal.getIntervalDayToSecond().getMicroseconds();
354+
yield ExpressionCreator.intervalDay(
355+
literal.getNullable(),
356+
literal.getIntervalDayToSecond().getDays(),
357+
literal.getIntervalDayToSecond().getSeconds(),
358+
subseconds,
359+
precision);
360+
}
361+
case INTERVAL_COMPOUND -> {
362+
if (!literal.getIntervalCompound().getIntervalDayToSecond().hasPrecision()) {
363+
throw new RuntimeException(
364+
"Interval compound with deprecated version of interval day (ie. no precision) is not supported");
365+
}
366+
yield ExpressionCreator.intervalCompound(
367+
literal.getNullable(),
368+
literal.getIntervalCompound().getIntervalYearToMonth().getYears(),
369+
literal.getIntervalCompound().getIntervalYearToMonth().getMonths(),
370+
literal.getIntervalCompound().getIntervalDayToSecond().getDays(),
371+
literal.getIntervalCompound().getIntervalDayToSecond().getSeconds(),
372+
literal.getIntervalCompound().getIntervalDayToSecond().getSubseconds(),
373+
literal.getIntervalCompound().getIntervalDayToSecond().getPrecision());
374+
}
348375
case FIXED_CHAR -> ExpressionCreator.fixedChar(literal.getNullable(), literal.getFixedChar());
349376
case VAR_CHAR -> ExpressionCreator.varChar(
350377
literal.getNullable(), literal.getVarChar().getValue(), literal.getVarChar().getLength());

core/src/main/java/io/substrait/function/ParameterizedType.java

+30
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,36 @@ public static ImmutableParameterizedType.Decimal.Builder builder() {
106106
}
107107
}
108108

109+
@Value.Immutable
110+
abstract static class IntervalDay extends BaseParameterizedType implements NullableType {
111+
public abstract StringLiteral precision();
112+
113+
@Override
114+
<R, E extends Throwable> R accept(final ParameterizedTypeVisitor<R, E> parameterizedTypeVisitor)
115+
throws E {
116+
return parameterizedTypeVisitor.visit(this);
117+
}
118+
119+
public static ImmutableParameterizedType.IntervalDay.Builder builder() {
120+
return ImmutableParameterizedType.IntervalDay.builder();
121+
}
122+
}
123+
124+
@Value.Immutable
125+
abstract static class IntervalCompound extends BaseParameterizedType implements NullableType {
126+
public abstract StringLiteral precision();
127+
128+
@Override
129+
<R, E extends Throwable> R accept(final ParameterizedTypeVisitor<R, E> parameterizedTypeVisitor)
130+
throws E {
131+
return parameterizedTypeVisitor.visit(this);
132+
}
133+
134+
public static ImmutableParameterizedType.IntervalCompound.Builder builder() {
135+
return ImmutableParameterizedType.IntervalCompound.builder();
136+
}
137+
}
138+
109139
@Value.Immutable
110140
abstract static class PrecisionTimestamp extends BaseParameterizedType implements NullableType {
111141
public abstract StringLiteral precision();

core/src/main/java/io/substrait/function/ParameterizedTypeCreator.java

+14
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ public ParameterizedType decimalE(String precision, String scale) {
4949
.build();
5050
}
5151

52+
public ParameterizedType intervalDayE(String precision) {
53+
return ParameterizedType.IntervalDay.builder()
54+
.nullable(nullable)
55+
.precision(parameter(precision, false))
56+
.build();
57+
}
58+
59+
public ParameterizedType intervalCompoundE(String precision) {
60+
return ParameterizedType.IntervalCompound.builder()
61+
.nullable(nullable)
62+
.precision(parameter(precision, false))
63+
.build();
64+
}
65+
5266
public ParameterizedType precisionTimestampE(String precision) {
5367
return ParameterizedType.PrecisionTimestamp.builder()
5468
.nullable(nullable)

core/src/main/java/io/substrait/function/ParameterizedTypeVisitor.java

+14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ public interface ParameterizedTypeVisitor<R, E extends Throwable> extends TypeVi
1111

1212
R visit(ParameterizedType.Decimal expr) throws E;
1313

14+
R visit(ParameterizedType.IntervalDay expr) throws E;
15+
16+
R visit(ParameterizedType.IntervalCompound expr) throws E;
17+
1418
R visit(ParameterizedType.PrecisionTimestamp expr) throws E;
1519

1620
R visit(ParameterizedType.PrecisionTimestampTZ expr) throws E;
@@ -60,6 +64,16 @@ public R visit(ParameterizedType.PrecisionTimestampTZ expr) throws E {
6064
throw t();
6165
}
6266

67+
@Override
68+
public R visit(ParameterizedType.IntervalDay expr) throws E {
69+
throw t();
70+
}
71+
72+
@Override
73+
public R visit(ParameterizedType.IntervalCompound expr) throws E {
74+
throw t();
75+
}
76+
6377
@Override
6478
public R visit(ParameterizedType.Struct expr) throws E {
6579
throw t();

core/src/main/java/io/substrait/function/ToTypeString.java

+15
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ public String visit(final Type.IntervalDay expr) {
9090
return "iday";
9191
}
9292

93+
@Override
94+
public String visit(final Type.IntervalCompound expr) {
95+
return "icompound";
96+
}
97+
9398
@Override
9499
public String visit(final Type.UUID expr) {
95100
return "uuid";
@@ -165,6 +170,16 @@ public String visit(ParameterizedType.Decimal expr) throws RuntimeException {
165170
return "dec";
166171
}
167172

173+
@Override
174+
public String visit(ParameterizedType.IntervalDay expr) throws RuntimeException {
175+
return "iday";
176+
}
177+
178+
@Override
179+
public String visit(ParameterizedType.IntervalCompound expr) throws RuntimeException {
180+
return "icompound";
181+
}
182+
168183
@Override
169184
public String visit(ParameterizedType.PrecisionTimestamp expr) throws RuntimeException {
170185
return "pts";

core/src/main/java/io/substrait/function/TypeExpression.java

+30
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,36 @@ public static ImmutableTypeExpression.Decimal.Builder builder() {
8484
}
8585
}
8686

87+
@Value.Immutable
88+
abstract static class IntervalDay extends BaseTypeExpression implements NullableType {
89+
90+
public abstract TypeExpression precision();
91+
92+
@Override
93+
<R, E extends Throwable> R acceptE(final TypeExpressionVisitor<R, E> visitor) throws E {
94+
return visitor.visit(this);
95+
}
96+
97+
public static ImmutableTypeExpression.IntervalDay.Builder builder() {
98+
return ImmutableTypeExpression.IntervalDay.builder();
99+
}
100+
}
101+
102+
@Value.Immutable
103+
abstract static class IntervalCompound extends BaseTypeExpression implements NullableType {
104+
105+
public abstract TypeExpression precision();
106+
107+
@Override
108+
<R, E extends Throwable> R acceptE(final TypeExpressionVisitor<R, E> visitor) throws E {
109+
return visitor.visit(this);
110+
}
111+
112+
public static ImmutableTypeExpression.IntervalCompound.Builder builder() {
113+
return ImmutableTypeExpression.IntervalCompound.builder();
114+
}
115+
}
116+
87117
@Value.Immutable
88118
abstract static class PrecisionTimestamp extends BaseTypeExpression implements NullableType {
89119

core/src/main/java/io/substrait/function/TypeExpressionCreator.java

+11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ public TypeExpression decimalE(TypeExpression precision, TypeExpression scale) {
3535
.build();
3636
}
3737

38+
public TypeExpression intervalDayE(TypeExpression precision) {
39+
return TypeExpression.IntervalDay.builder().nullable(nullable).precision(precision).build();
40+
}
41+
42+
public TypeExpression intervalCompoundE(TypeExpression precision) {
43+
return TypeExpression.IntervalCompound.builder()
44+
.nullable(nullable)
45+
.precision(precision)
46+
.build();
47+
}
48+
3849
public TypeExpression precisionTimestampE(TypeExpression precision) {
3950
return TypeExpression.PrecisionTimestamp.builder()
4051
.nullable(nullable)

0 commit comments

Comments
 (0)