IMPALA-14414: Calcite planner: Added new code to handle nan/inf

The current code works for NaN and Inf, but it breaks when upgrading
to v1.40.  This commit changes the code to handle these when we do
the upgrade to 1.40 and adds a basic test into the calcite.test to ensure
that when the upgrade happens, it does not break.

Change-Id: I8593a4942a2fe785a0c77134b78a9d97257225fc
Reviewed-on: http://gerrit.cloudera.org:8080/23561
Reviewed-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
This commit is contained in:
Steve Carlin
2025-09-05 13:09:01 -07:00
parent f34dea9b6f
commit 62bf609942
2 changed files with 48 additions and 5 deletions

View File

@@ -35,6 +35,7 @@ import org.apache.impala.catalog.Function;
import org.apache.impala.catalog.PrimitiveType;
import org.apache.impala.catalog.ScalarType;
import org.apache.impala.catalog.Type;
import org.apache.impala.common.AnalysisException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,16 +57,39 @@ public class RexLiteralConverter {
return NumericLiteral.create(
new BigDecimal(rexLiteral.getValueAs(Long.class)), Type.BIGINT);
}
switch (rexLiteral.getTypeName()) {
case NULL:
Type type = ImpalaTypeConverter.createImpalaType(rexLiteral.getType());
return new AnalyzedNullLiteral(type);
if (rexLiteral.isNull()) {
Type type = ImpalaTypeConverter.createImpalaType(rexLiteral.getType());
return new AnalyzedNullLiteral(type);
}
switch (rexLiteral.getType().getSqlTypeName()) {
case BOOLEAN:
Expr boolExpr = new BoolLiteral((Boolean) rexLiteral.getValueAs(Boolean.class));
return boolExpr;
case DOUBLE:
Double d = rexLiteral.getValueAs(Double.class);
// NumericLiteral will throw an exception if it is a Nan or Inf, so create
// a cast around it.
if (!NumericLiteral.isImpalaDouble(d)) {
return createCastNanOrInf(d, Type.DOUBLE);
}
return NumericLiteral.create(rexLiteral.getValueAs(BigDecimal.class),
Type.DOUBLE);
case FLOAT:
Float f = rexLiteral.getValueAs(Float.class);
// NumericLiteral will throw an exception if it is a Nan or Inf, so create
// a cast around it.
if (!NumericLiteral.isImpalaDouble(f)) {
return createCastNanOrInf(f, Type.FLOAT);
}
return NumericLiteral.create(rexLiteral.getValueAs(BigDecimal.class),
Type.FLOAT);
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT:
case DECIMAL:
case DOUBLE:
Expr numericExpr = NumericLiteral.create(rexLiteral.getValueAs(BigDecimal.class),
ImpalaTypeConverter.createImpalaType(rexLiteral.getType()));
return numericExpr;
@@ -108,4 +132,16 @@ public class RexLiteralConverter {
Function castFunc = FunctionResolver.getExactFunction("casttotimestamp", typeNames);
return new AnalyzedFunctionCallExpr(castFunc, argList, Type.TIMESTAMP);
}
private static Expr createCastNanOrInf(Object o, Type t) {
List<RelDataType> typeNames =
ImmutableList.of(ImpalaTypeConverter.getRelDataType(Type.STRING));
String nanOrInf = o.toString();
List<Expr> argList =
Lists.newArrayList(new StringLiteral(nanOrInf, Type.STRING, false));
Preconditions.checkState(t.equals(Type.DOUBLE) || t.equals(Type.FLOAT));
String fnName = t.equals(Type.DOUBLE) ? "casttodouble" : "casttofloat";
Function castFunc = FunctionResolver.getExactFunction(fnName, typeNames);
return new AnalyzedFunctionCallExpr(castFunc, argList, t);
}
}

View File

@@ -1121,3 +1121,10 @@ int
---- RUNTIME_PROFILE
row_regex: .*PlannerType: CalcitePlanner.*
====
---- QUERY
select cast('nan' as double), cast('inf' as float);
---- RESULTS
NaN,Inf
---- TYPES
DOUBLE, FLOAT
====