This is an automated email from the ASF dual-hosted git repository. michaelsmith pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/impala.git
commit b59be457eb22c5be22eaa6c687f1fe2c8ee4aff0 Author: Steve Carlin <[email protected]> AuthorDate: Mon Dec 22 14:37:35 2025 -0800 IMPALA-14639: Calcite planner should not allow date cast to int The following query was passing before this commit (but now fails correctly): select cast(date '2000-12-21' as int); Testing: Added test to calcite.test, also, tests exist in test_date_queries.TestDateQueriesAllFormat.test_queries Change-Id: Icad76e6c1db489ecdfa620109baa47232331a42d Reviewed-on: http://gerrit.cloudera.org:8080/24048 Reviewed-by: Michael Smith <[email protected]> Reviewed-by: Aman Sinha <[email protected]> Tested-by: Impala Public Jenkins <[email protected]> --- .../java/org/apache/impala/analysis/CastExpr.java | 37 +++++++++++++--------- .../impala/calcite/functions/RexCallConverter.java | 21 +++++++++--- .../queries/QueryTest/calcite.test | 6 ++++ 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/fe/src/main/java/org/apache/impala/analysis/CastExpr.java b/fe/src/main/java/org/apache/impala/analysis/CastExpr.java index 91abc6781..e86afa152 100644 --- a/fe/src/main/java/org/apache/impala/analysis/CastExpr.java +++ b/fe/src/main/java/org/apache/impala/analysis/CastExpr.java @@ -403,29 +403,36 @@ public class CastExpr extends Expr { noOp_ = childType.equals(type_); if (noOp_) return; - FunctionName fnName = new FunctionName(BuiltinsDb.NAME, getFnName(type_)); - Type[] args = { childType }; + fn_ = getFunction(childType, type_, isImplicit_); + if (fn_ == null) { + throw new AnalysisException("Invalid type cast of " + getChild(0).toSql() + + " from " + childType + " to " + type_); + } + + Preconditions.checkState(type_.matchesType(fn_.getReturnType()), + type_ + " != " + fn_.getReturnType()); + } + + public static Function getFunction(Type fromType, Type toType, boolean isImplicit) + throws AnalysisException { + Function fn = null; + FunctionName fnName = new FunctionName(BuiltinsDb.NAME, getFnName(toType)); + Type[] args = { fromType }; Function searchDesc = new Function(fnName, args, Type.INVALID, false); - if (isImplicit_) { - fn_ = BuiltinsDb.getInstance().getFunction(searchDesc, + if (isImplicit) { + fn = BuiltinsDb.getInstance().getFunction(searchDesc, CompareMode.IS_NONSTRICT_SUPERTYPE_OF); - Preconditions.checkState(fn_ != null); + Preconditions.checkState(fn != null); } else { - fn_ = BuiltinsDb.getInstance().getFunction(searchDesc, + fn = BuiltinsDb.getInstance().getFunction(searchDesc, CompareMode.IS_IDENTICAL); - if (fn_ == null) { + if (fn == null) { // allow for promotion from CHAR to STRING; only if no exact match is found - fn_ = BuiltinsDb.getInstance().getFunction( + fn = BuiltinsDb.getInstance().getFunction( searchDesc.promoteCharsToStrings(), CompareMode.IS_IDENTICAL); } } - if (fn_ == null) { - throw new AnalysisException("Invalid type cast of " + getChild(0).toSql() + - " from " + childType + " to " + type_); - } - - Preconditions.checkState(type_.matchesType(fn_.getReturnType()), - type_ + " != " + fn_.getReturnType()); + return fn; } /** diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java index f71a67ba6..57a42eb07 100644 --- a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java @@ -33,6 +33,7 @@ import org.apache.impala.analysis.Analyzer; import org.apache.impala.analysis.ArithmeticExpr; import org.apache.impala.analysis.BinaryPredicate; import org.apache.impala.analysis.CaseWhenClause; +import org.apache.impala.analysis.CastExpr; import org.apache.impala.analysis.CompoundPredicate; import org.apache.impala.analysis.Expr; import org.apache.impala.analysis.FunctionCallExpr; @@ -44,6 +45,7 @@ import org.apache.impala.calcite.rules.ImpalaRexExecutor; import org.apache.impala.calcite.type.ImpalaTypeConverter; import org.apache.impala.catalog.Function; import org.apache.impala.catalog.Type; +import org.apache.impala.common.AnalysisException; import org.apache.impala.common.ImpalaException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -191,13 +193,14 @@ public class RexCallConverter { private static Expr createCastExpr(RexCall call, List<Expr> params, Analyzer analyzer) throws ImpalaException { Type impalaRetType = ImpalaTypeConverter.createImpalaType(call.getType()); - if (params.get(0).getType() == Type.NULL) { + Expr paramsOperand = params.get(0); + if (paramsOperand.getType() == Type.NULL) { return new AnalyzedNullLiteral(impalaRetType); } // no need for redundant cast. - if (params.get(0).getType().equals(impalaRetType)) { - return params.get(0); + if (paramsOperand.getType().equals(impalaRetType)) { + return paramsOperand; } // Hack logic: Partition pruning needs the exact number when the column @@ -212,8 +215,16 @@ public class RexCallConverter { // Small hack: Most cast expressions have "isImplicit" set to true. If this // is the case, then it blocks "analyze" from working through the cast. We // need to analyze the expression before creating the cast around it. - params.get(0).analyze(analyzer); - return new AnalyzedCastExpr(impalaRetType, params.get(0)); + paramsOperand.analyze(analyzer); + + + // call getFunction which will return null if the cast is not valid. + Function fn = CastExpr.getFunction(paramsOperand.getType(), impalaRetType, false); + if (fn == null) { + throw new AnalysisException("Invalid type cast " + + "from " + paramsOperand.getType() + " to " + impalaRetType); + } + return new AnalyzedCastExpr(impalaRetType, paramsOperand); } private static Expr createDecodeExpr(Function fn, List<Expr> params, diff --git a/testdata/workloads/functional-query/queries/QueryTest/calcite.test b/testdata/workloads/functional-query/queries/QueryTest/calcite.test index 0d4f03767..b5471c28f 100644 --- a/testdata/workloads/functional-query/queries/QueryTest/calcite.test +++ b/testdata/workloads/functional-query/queries/QueryTest/calcite.test @@ -1076,6 +1076,12 @@ where 10.1 not in (tinyint_col, smallint_col, int_col, bigint_col, float_col, do 8990 ---- TYPES bigint +===== +---- QUERY +# Test that casts are not allowed between DATE and numerical types +select cast(date '2000-12-21' as int); +---- CATCH +AnalysisException: Invalid type cast from DATE to INT ==== ---- QUERY # Test for varchar ndv with both 1 and 2 parameters
