Skip to content

Commit 2870c6f

Browse files
committed
feat(core): support max length, scale, precision in Substrait dialect
Add max_length (FIXED_BINARY, VARCHAR, FIXED_CHAR) and max_scale (DECIMAL, alongside the existing max_precision) to the dialect supported_types model, mirroring the schema additions from substrait-io/substrait#1030. Closes #808
1 parent a1e48c6 commit 2870c6f

4 files changed

Lines changed: 102 additions & 1 deletion

File tree

core/src/main/java/io/substrait/dialect/SupportedType.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,30 @@ public abstract class SupportedType {
3636
public abstract Optional<SystemTypeMetadata> systemMetadata();
3737

3838
/**
39-
* The maximum precision supported for the type, if constrained.
39+
* The maximum precision supported for the type, if constrained. Applies to the
40+
* subsecond-precision temporal types ({@code PRECISION_TIME}, {@code PRECISION_TIMESTAMP}, {@code
41+
* PRECISION_TIMESTAMP_TZ}, {@code INTERVAL_COMPOUND}, {@code INTERVAL_DAY}) and, together with
42+
* {@link #maxScale()}, to {@code DECIMAL}.
4043
*
4144
* @return the optional maximum precision
4245
*/
4346
public abstract Optional<Integer> maxPrecision();
4447

48+
/**
49+
* The maximum scale supported for a {@code DECIMAL} type, if constrained.
50+
*
51+
* @return the optional maximum scale
52+
*/
53+
public abstract Optional<Integer> maxScale();
54+
55+
/**
56+
* The maximum length supported for a variable- or fixed-length type ({@code FIXED_BINARY}, {@code
57+
* VARCHAR}, {@code FIXED_CHAR}), if constrained.
58+
*
59+
* @return the optional maximum length
60+
*/
61+
public abstract Optional<Integer> maxLength();
62+
4563
/**
4664
* Dependency (alias) where a {@code USER_DEFINED} type is declared.
4765
*
@@ -66,6 +84,8 @@ public boolean isBare() {
6684
&& !metadata().isPresent()
6785
&& !systemMetadata().isPresent()
6886
&& !maxPrecision().isPresent()
87+
&& !maxScale().isPresent()
88+
&& !maxLength().isPresent()
6989
&& !source().isPresent()
7090
&& !name().isPresent();
7191
}

core/src/main/java/io/substrait/dialect/SupportedTypeDeserializer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ public SupportedType deserialize(JsonParser p, DeserializationContext ctxt) thro
3535
if (node.hasNonNull("max_precision")) {
3636
builder.maxPrecision(node.get("max_precision").asInt());
3737
}
38+
if (node.hasNonNull("max_scale")) {
39+
builder.maxScale(node.get("max_scale").asInt());
40+
}
41+
if (node.hasNonNull("max_length")) {
42+
builder.maxLength(node.get("max_length").asInt());
43+
}
3844
if (node.hasNonNull("source")) {
3945
builder.source(node.get("source").asText());
4046
}

core/src/main/java/io/substrait/dialect/SupportedTypeSerializer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ public void serialize(SupportedType value, JsonGenerator gen, SerializerProvider
3030
if (value.maxPrecision().isPresent()) {
3131
gen.writeNumberField("max_precision", value.maxPrecision().get());
3232
}
33+
if (value.maxScale().isPresent()) {
34+
gen.writeNumberField("max_scale", value.maxScale().get());
35+
}
36+
if (value.maxLength().isPresent()) {
37+
gen.writeNumberField("max_length", value.maxLength().get());
38+
}
3339
if (value.systemMetadata().isPresent()) {
3440
provider.defaultSerializeField("system_metadata", value.systemMetadata().get(), gen);
3541
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package io.substrait.dialect;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import com.networknt.schema.Error;
8+
import java.util.List;
9+
import org.junit.jupiter.api.Test;
10+
11+
/**
12+
* Verifies that the parameterized-type constraints {@code max_length} (for {@code FIXED_BINARY},
13+
* {@code VARCHAR}, {@code FIXED_CHAR}) and {@code max_precision}/{@code max_scale} (for {@code
14+
* DECIMAL}) serialize as configuration objects, validate against the published {@code
15+
* dialect_schema.yaml}, and survive a POJO &rarr; YAML &rarr; POJO round trip.
16+
*/
17+
class SupportedTypeParameterizedRoundTripTest {
18+
19+
private static Dialect dialectWith(SupportedType type) {
20+
return Dialect.builder().addSupportedTypes(type).build();
21+
}
22+
23+
private static void assertRoundTrips(SupportedType type) {
24+
assertFalse(type.isBare(), "configured type must not be bare");
25+
Dialect original = dialectWith(type);
26+
String yaml = Dialect.toYaml(original);
27+
28+
List<Error> errors = SchemaValidator.validate(yaml);
29+
assertTrue(errors.isEmpty(), () -> "Generated dialect failed schema validation: " + errors);
30+
31+
assertEquals(original, Dialect.load(yaml));
32+
}
33+
34+
@Test
35+
void fixedBinaryMaxLength() {
36+
SupportedType type =
37+
SupportedType.builder().type(TypeKind.FIXED_BINARY).maxLength(1024).build();
38+
39+
assertTrue(Dialect.toYaml(dialectWith(type)).contains("max_length: 1024"));
40+
assertRoundTrips(type);
41+
}
42+
43+
@Test
44+
void varcharMaxLength() {
45+
SupportedType type = SupportedType.builder().type(TypeKind.VARCHAR).maxLength(255).build();
46+
47+
assertTrue(Dialect.toYaml(dialectWith(type)).contains("max_length: 255"));
48+
assertRoundTrips(type);
49+
}
50+
51+
@Test
52+
void fixedCharMaxLength() {
53+
SupportedType type = SupportedType.builder().type(TypeKind.FIXED_CHAR).maxLength(64).build();
54+
55+
assertTrue(Dialect.toYaml(dialectWith(type)).contains("max_length: 64"));
56+
assertRoundTrips(type);
57+
}
58+
59+
@Test
60+
void decimalMaxPrecisionAndScale() {
61+
SupportedType type =
62+
SupportedType.builder().type(TypeKind.DECIMAL).maxPrecision(38).maxScale(10).build();
63+
64+
String yaml = Dialect.toYaml(dialectWith(type));
65+
assertTrue(yaml.contains("max_precision: 38"), yaml);
66+
assertTrue(yaml.contains("max_scale: 10"), yaml);
67+
assertRoundTrips(type);
68+
}
69+
}

0 commit comments

Comments
 (0)