diff --git a/isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java b/isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java index 31c40905e..a46d2bc49 100644 --- a/isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java +++ b/isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java @@ -9,21 +9,37 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; +/** + * Custom {@link RelDataTypeSystem} implementation for Substrait. + * + *

Defines type system rules such as precision, scale, and interval qualifiers for Substrait + * integration with Calcite. + */ public class SubstraitTypeSystem extends RelDataTypeSystemImpl { + + /** Singleton instance of Substrait type system. */ public static final RelDataTypeSystem TYPE_SYSTEM = new SubstraitTypeSystem(); + /** Default type factory using the Substrait type system. */ public static final RelDataTypeFactory TYPE_FACTORY = new JavaTypeFactoryImpl(TYPE_SYSTEM); - // Interval qualifier from year to month + /** Interval qualifier from year to month. */ public static final SqlIntervalQualifier YEAR_MONTH_INTERVAL = new SqlIntervalQualifier(TimeUnit.YEAR, TimeUnit.MONTH, SqlParserPos.ZERO); - // Interval qualifier from day to fractional second at microsecond precision + /** Interval qualifier from day to fractional second at microsecond precision. */ public static final SqlIntervalQualifier DAY_SECOND_INTERVAL = new SqlIntervalQualifier(TimeUnit.DAY, -1, TimeUnit.SECOND, 6, SqlParserPos.ZERO); + /** Private constructor to enforce singleton usage. */ private SubstraitTypeSystem() {} + /** + * Returns the maximum precision for the given SQL type. + * + * @param typeName The {@link SqlTypeName} for which precision is requested. + * @return Maximum precision for the type. + */ @Override public int getMaxPrecision(final SqlTypeName typeName) { switch (typeName) { @@ -41,6 +57,11 @@ public int getMaxPrecision(final SqlTypeName typeName) { return super.getMaxPrecision(typeName); } + /** + * Returns the maximum numeric scale supported by this type system. + * + * @return Maximum numeric scale (38). + */ @Override public int getDefaultPrecision(final SqlTypeName typeName) { switch (typeName) { @@ -51,6 +72,11 @@ public int getDefaultPrecision(final SqlTypeName typeName) { } } + /** + * Returns the maximum numeric precision supported by this type system. + * + * @return Maximum numeric precision (38). + */ @Override public int getMaxScale(final SqlTypeName typeName) { switch (typeName) { @@ -60,6 +86,11 @@ public int getMaxScale(final SqlTypeName typeName) { return super.getMaxScale(typeName); } + /** + * Indicates whether ragged union types should be converted to varying types. + * + * @return {@code true}, as Substrait requires conversion to varying types. + */ @Override public boolean shouldConvertRaggedUnionTypesToVarying() { return true; diff --git a/isthmus/src/main/java/io/substrait/isthmus/TypeConverter.java b/isthmus/src/main/java/io/substrait/isthmus/TypeConverter.java index ac2faf119..63dc6227b 100644 --- a/isthmus/src/main/java/io/substrait/isthmus/TypeConverter.java +++ b/isthmus/src/main/java/io/substrait/isthmus/TypeConverter.java @@ -20,11 +20,27 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.jspecify.annotations.Nullable; +/** + * Utility for converting between Calcite {@link org.apache.calcite.rel.type.RelDataType} and + * Substrait {@link io.substrait.type.Type}. + * + *

Supports primitive, complex, and user-defined types in both directions. + * + * @see UserTypeMapper + * @see io.substrait.type.Type + * @see org.apache.calcite.rel.type.RelDataType + */ public class TypeConverter { private final UserTypeMapper userTypeMapper; // DEFAULT TypeConverter which does not handle user-defined types + /** + * Default {@link TypeConverter} instance that does not handle user-defined types. + * + *

Both {@link UserTypeMapper#toSubstrait(RelDataType)} and {@link + * UserTypeMapper#toCalcite(Type.UserDefined)} return {@code null} in this default configuration. + */ public static TypeConverter DEFAULT = new TypeConverter( new UserTypeMapper() { @@ -41,14 +57,39 @@ public RelDataType toCalcite(Type.UserDefined type) { } }); + /** + * Creates a {@link TypeConverter} with a provided user type mapper. + * + * @param userTypeMapper Mapper for converting user-defined types between Calcite and Substrait. + */ public TypeConverter(UserTypeMapper userTypeMapper) { this.userTypeMapper = userTypeMapper; } + /** + * Converts a Calcite {@link RelDataType} to a Substrait {@link Type}. + * + * @param type Calcite type to convert. + * @return Corresponding Substrait type. + * @throws UnsupportedOperationException if the type cannot be converted or has unsupported + * properties. + */ public Type toSubstrait(RelDataType type) { return toSubstrait(type, new ArrayList<>()); } + /** + * Converts a Calcite {@link RelDataType} of SQL type {@link SqlTypeName#ROW} to a Substrait + * {@link NamedStruct}. + * + *

Field names are extracted from the Calcite struct type and paired with the converted + * Substrait struct. + * + * @param type Calcite struct type ({@link SqlTypeName#ROW}). + * @return Substrait {@link NamedStruct} containing field names and struct type. + * @throws IllegalArgumentException if {@code type} is not a struct ({@code ROW}). + * @throws UnsupportedOperationException if any child field type cannot be converted. + */ public NamedStruct toNamedStruct(RelDataType type) { if (type.getSqlTypeName() != SqlTypeName.ROW) { throw new IllegalArgumentException("Expected type of struct."); @@ -153,11 +194,31 @@ private Type toSubstrait(RelDataType type, List names) { } } + /** + * Converts a Substrait {@link TypeExpression} to a Calcite {@link RelDataType}. + * + * @param relDataTypeFactory Calcite type factory. + * @param typeExpression Substrait type expression to convert. + * @return Calcite relational type. + * @throws UnsupportedOperationException if the expression contains unsupported precision or + * user-defined types cannot be mapped. + */ public RelDataType toCalcite( RelDataTypeFactory relDataTypeFactory, TypeExpression typeExpression) { return toCalcite(relDataTypeFactory, typeExpression, null); } + /** + * Converts a Substrait {@link TypeExpression} to a Calcite {@link RelDataType}, with optional + * field names for DFS/nested structs. + * + * @param relDataTypeFactory Calcite type factory. + * @param typeExpression Substrait type expression to convert. + * @param dfsFieldNames Optional list of field names to apply to struct fields, in DFS order. + * @return Calcite relational type. + * @throws UnsupportedOperationException if the expression contains unsupported precision or + * user-defined types cannot be mapped. + */ public RelDataType toCalcite( RelDataTypeFactory relDataTypeFactory, TypeExpression typeExpression, diff --git a/isthmus/src/main/java/io/substrait/isthmus/Utils.java b/isthmus/src/main/java/io/substrait/isthmus/Utils.java index 3382007f1..9804954eb 100644 --- a/isthmus/src/main/java/io/substrait/isthmus/Utils.java +++ b/isthmus/src/main/java/io/substrait/isthmus/Utils.java @@ -12,6 +12,11 @@ import org.apache.calcite.jdbc.CalciteSchema; import org.jspecify.annotations.NonNull; +/** + * Utility helpers for Substrait conversions and Calcite schema management. + * + *

Includes helpers for computing cartesian products and building hierarchical Calcite schemas. + */ public class Utils { /** * Compute the cartesian product for n lists. @@ -19,6 +24,12 @@ public class Utils { *

Based on Soln by * Thomas Preissler + * + * @param element type contained within each list. + * @param lists A list of lists whose cross product is computed. Null or empty inner lists are + * skipped. + * @return A stream of lists representing the cartesian product (each output list has one element + * from each input list), or an empty stream if {@code lists} is empty. */ public static Stream> crossProduct(List> lists) {