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
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* Copyright 2023-2026 LinkedIn Corporation. All rights reserved.
* Licensed under the BSD-2 Clause license.
* See LICENSE in the project root for license information.
*/
package com.linkedin.coral.common;

import java.util.ArrayList;
import java.util.List;

import com.google.common.base.Preconditions;

import org.apache.calcite.plan.Context;
import org.apache.calcite.plan.Contexts;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptSchema;
import org.apache.calcite.rel.core.Values;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Frameworks;
import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.tools.RelBuilderFactory;
import org.apache.calcite.util.Pair;

import static org.apache.calcite.rel.core.RelFactories.DEFAULT_AGGREGATE_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_EXCHANGE_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_FILTER_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_JOIN_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_MATCH_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_PROJECT_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_REPEAT_UNION_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_SET_OP_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_SNAPSHOT_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_SORT_EXCHANGE_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_SORT_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_SPOOL_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_TABLE_SCAN_FACTORY;
import static org.apache.calcite.rel.core.RelFactories.DEFAULT_VALUES_FACTORY;


/**
* CoralRelBuilder overrides {@link #rename} to handle {@link CoralUncollect} nodes.
* Instead of wrapping CoralUncollect with a Project RelNode, it rebuilds the CoralUncollect
* using {@link CoralUncollect#copy(org.apache.calcite.rel.type.RelDataType)} to set the rowType.
*
* <p>This avoids an extra and unnecessary (SELECT ... FROM ... AS ...) wrapper in unparsed SQL.
*
* <p>This class was previously named {@code HiveRelBuilder} and has been renamed to better
* reflect that it is used across all Coral translation targets.
*/
public class CoralRelBuilder extends RelBuilder {
protected CoralRelBuilder(Context context, RelOptCluster cluster, RelOptSchema relOptSchema) {
super(context, cluster, relOptSchema);
}

public static RelBuilder create(FrameworkConfig config) {
return Frameworks.withPrepare(config, (cluster, relOptSchema, rootSchema, statement) -> {
cluster = RelOptCluster.create(cluster.getPlanner(),
new RexBuilder(new CoralJavaTypeFactoryImpl(cluster.getTypeFactory().getTypeSystem())));
return new CoralRelBuilder(config.getContext(), cluster, relOptSchema);
});
}

public static RelBuilderFactory proto(final Context context) {
return (cluster, schema) -> new CoralRelBuilder(context, cluster, schema);
}

public static final RelBuilderFactory LOGICAL_BUILDER =
CoralRelBuilder.proto(Contexts.of(DEFAULT_PROJECT_FACTORY, DEFAULT_FILTER_FACTORY, DEFAULT_JOIN_FACTORY,
DEFAULT_SORT_FACTORY, DEFAULT_EXCHANGE_FACTORY, DEFAULT_SORT_EXCHANGE_FACTORY, DEFAULT_AGGREGATE_FACTORY,
DEFAULT_MATCH_FACTORY, DEFAULT_SET_OP_FACTORY, DEFAULT_VALUES_FACTORY, DEFAULT_TABLE_SCAN_FACTORY,
DEFAULT_SNAPSHOT_FACTORY, DEFAULT_SPOOL_FACTORY, DEFAULT_REPEAT_UNION_FACTORY));

@Override
public RelBuilder rename(List<String> fieldNames) {
final List<String> oldFieldNames = peek().getRowType().getFieldNames();
Preconditions.checkArgument(fieldNames.size() <= oldFieldNames.size(), "More names than fields");
final List<String> newFieldNames = new ArrayList<>(oldFieldNames);
for (int i = 0; i < fieldNames.size(); i++) {
final String s = fieldNames.get(i);
if (s != null) {
newFieldNames.set(i, s);
}
}
if (oldFieldNames.equals(newFieldNames)) {
return this;
}
if (peek() instanceof Values) {
final Values v = (Values) build();
final RelDataTypeFactory.Builder b = getTypeFactory().builder();
for (Pair<String, RelDataTypeField> p : Pair.zip(newFieldNames, v.getRowType().getFieldList())) {
b.add(p.left, p.right.getType());
}
return values(v.tuples, b.build());
}
if (peek() instanceof CoralUncollect) {
final CoralUncollect v = (CoralUncollect) build();
final RelDataTypeFactory.Builder b = getTypeFactory().builder();
for (Pair<String, RelDataTypeField> p : Pair.zip(newFieldNames, v.getRowType().getFieldList())) {
b.add(p.left, p.right.getType());
}
push(v.copy(b.build()));
return this;
}

return project(fields(), newFieldNames, true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/**
* Copyright 2017-2026 LinkedIn Corporation. All rights reserved.
* Licensed under the BSD-2 Clause license.
* See LICENSE in the project root for license information.
*/
package com.linkedin.coral.common;

import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;


/**
* Coral's type system configuration for Calcite, defining precision and scale defaults
* compatible with Hive/Spark SQL semantics.
*
* <p>This class was previously named {@code HiveTypeSystem} and has been renamed to better
* reflect that it is a dialect-agnostic Calcite type system configuration used across
* all Coral translation targets (Hive, Spark, Trino, etc.).
*/
public class CoralTypeSystem extends RelDataTypeSystemImpl {
private static final int MAX_DECIMAL_PRECISION = 38;
private static final int MAX_DECIMAL_SCALE = 38;
private static final int DEFAULT_DECIMAL_PRECISION = 10;
private static final int MAX_CHAR_PRECISION = Integer.MAX_VALUE;
private static final int DEFAULT_VARCHAR_PRECISION = 65535;
private static final int DEFAULT_CHAR_PRECISION = 255;
private static final int MAX_BINARY_PRECISION = Integer.MAX_VALUE;
private static final int MAX_TIMESTAMP_PRECISION = 9;
private static final int DEFAULT_TINYINT_PRECISION = 3;
private static final int DEFAULT_SMALLINT_PRECISION = 5;
private static final int DEFAULT_INTEGER_PRECISION = 10;
private static final int DEFAULT_BIGINT_PRECISION = 19;

@Override
public int getMaxScale(SqlTypeName typeName) {
switch (typeName) {
case DECIMAL:
return getMaxNumericScale();
case INTERVAL_YEAR:
case INTERVAL_MONTH:
case INTERVAL_YEAR_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
return SqlTypeName.MAX_INTERVAL_FRACTIONAL_SECOND_PRECISION;
default:
return -1;
}
}

@Override
public int getDefaultPrecision(SqlTypeName typeName) {
switch (typeName) {
case BINARY:
case VARBINARY:
case TIME:
case TIMESTAMP:
return RelDataType.PRECISION_NOT_SPECIFIED;
case CHAR:
return DEFAULT_CHAR_PRECISION;
case VARCHAR:
return DEFAULT_VARCHAR_PRECISION;
case DECIMAL:
return DEFAULT_DECIMAL_PRECISION;
case INTERVAL_YEAR:
case INTERVAL_MONTH:
case INTERVAL_YEAR_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
return SqlTypeName.DEFAULT_INTERVAL_START_PRECISION;
case TINYINT:
return DEFAULT_TINYINT_PRECISION;
case SMALLINT:
return DEFAULT_SMALLINT_PRECISION;
case INTEGER:
return DEFAULT_INTEGER_PRECISION;
case BIGINT:
return DEFAULT_BIGINT_PRECISION;
default:
return -1;
}
}

@Override
public int getMaxPrecision(SqlTypeName typeName) {
switch (typeName) {
case DECIMAL:
return getMaxNumericPrecision();
case VARCHAR:
case CHAR:
return MAX_CHAR_PRECISION;
case VARBINARY:
case BINARY:
return MAX_BINARY_PRECISION;
case TIME:
case TIMESTAMP:
return MAX_TIMESTAMP_PRECISION;
case INTERVAL_YEAR:
case INTERVAL_MONTH:
case INTERVAL_YEAR_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
return SqlTypeName.MAX_INTERVAL_START_PRECISION;
default:
return -1;
}
}

@Override
public int getMaxNumericScale() {
return MAX_DECIMAL_SCALE;
}

@Override
public int getMaxNumericPrecision() {
return MAX_DECIMAL_PRECISION;
}

@Override
public RelDataType deriveSumType(RelDataTypeFactory typeFactory, RelDataType argumentType) {
switch (argumentType.getSqlTypeName()) {
case TINYINT:
case SMALLINT:
return nullableType(typeFactory, SqlTypeName.INTEGER);
case INTEGER:
case BIGINT:
return nullableType(typeFactory, SqlTypeName.BIGINT);
case REAL:
case FLOAT:
case DOUBLE:
return nullableType(typeFactory, SqlTypeName.DOUBLE);
case DECIMAL:
return nullableType(typeFactory, SqlTypeName.DECIMAL);
default:
return argumentType;
}
}

@Override
public boolean shouldConvertRaggedUnionTypesToVarying() {
return true;
}

@Override
public boolean isSchemaCaseSensitive() {
return false;
}

@Override
public RelDataType deriveDecimalDivideType(RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) {
if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) {
if (SqlTypeUtil.isDecimal(type1) || SqlTypeUtil.isDecimal(type2)) {
return super.deriveDecimalDivideType(typeFactory, type1, type2);
} else {
return nullableType(typeFactory, SqlTypeName.DOUBLE);
}
}
return null;
}

@Override
public RelDataType deriveDecimalMultiplyType(RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) {
if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) {
if (SqlTypeUtil.isDecimal(type1) || SqlTypeUtil.isDecimal(type2)) {
return super.deriveDecimalMultiplyType(typeFactory, type1, type2);
} else if (SqlTypeUtil.isBigint(type1) || SqlTypeUtil.isBigint(type2)) {
return nullableType(typeFactory, SqlTypeName.BIGINT);
}
}
return null;
}

private RelDataType nullableType(RelDataTypeFactory typeFactory, SqlTypeName typeName) {
return typeFactory.createTypeWithNullability(typeFactory.createSqlType(typeName), true);
}
}
Loading
Loading