diff --git ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/RexNodeConverter.java ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/RexNodeConverter.java index f6a6ff24e2..fef28dd20d 100644 --- ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/RexNodeConverter.java +++ ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/RexNodeConverter.java @@ -337,6 +337,8 @@ private RexNode convert(ExprNodeGenericFuncDesc func) throws SemanticException { if (calciteOp.getKind() == SqlKind.CASE) { // If it is a case operator, we need to rewrite it childRexNodeLst = rewriteCaseChildren(func, childRexNodeLst); + // Adjust branch types by inserting explicit casts if the actual is ambigous + childRexNodeLst = adjustCaseBranchTypes(childRexNodeLst, retType); } else if (HiveExtractDate.ALL_FUNCTIONS.contains(calciteOp)) { // If it is a extract operator, we need to rewrite it childRexNodeLst = rewriteExtractDateChildren(calciteOp, childRexNodeLst); @@ -362,6 +364,8 @@ private RexNode convert(ExprNodeGenericFuncDesc func) throws SemanticException { // This allows to be further reduced to OR, if possible calciteOp = SqlStdOperatorTable.CASE; childRexNodeLst = rewriteCoalesceChildren(func, childRexNodeLst); + // Adjust branch types by inserting explicit casts if the actual is ambigous + childRexNodeLst = adjustCaseBranchTypes(childRexNodeLst, retType); } else if (calciteOp == HiveToDateSqlOperator.INSTANCE) { childRexNodeLst = rewriteToDateChildren(childRexNodeLst); } @@ -471,6 +475,34 @@ private RexNode handleExplicitCast(ExprNodeGenericFuncDesc func, List c return newChildRexNodeLst; } + /** + * Adds explicit casts if Calcite's type system could not resolve the CASE branches to a common type. + * + * Calcite is more stricter than hive w.r.t type conversions. + * If a CASE has branches with string/int/boolean branch types; there is no common type. + */ + private List adjustCaseBranchTypes(List nodes, RelDataType retType) { + List branchTypes = new ArrayList<>(); + for (int i = 1; i < nodes.size(); i += 2) { + branchTypes.add(nodes.get(i).getType()); + } + RelDataType commonType = cluster.getTypeFactory().leastRestrictive(branchTypes); + if (commonType != null) { + // conversion is possible; not changes are neccessary + return nodes; + } + List newNodes = new ArrayList<>(); + for (int i = 0; i < nodes.size(); i++) { + RexNode node = nodes.get(i); + if (i % 2 == 1) { + newNodes.add(cluster.getRexBuilder().makeCast(retType, node)); + } else { + newNodes.add(node); + } + } + return newNodes; + } + private List rewriteExtractDateChildren(SqlOperator op, List childRexNodeLst) throws SemanticException { List newChildRexNodeLst = new ArrayList<>(2); diff --git ql/src/test/results/clientpositive/llap/vector_case_when_conversion.q.out ql/src/test/results/clientpositive/llap/vector_case_when_conversion.q.out index 8e077105c6..4da58e0c2b 100644 --- ql/src/test/results/clientpositive/llap/vector_case_when_conversion.q.out +++ ql/src/test/results/clientpositive/llap/vector_case_when_conversion.q.out @@ -209,8 +209,8 @@ STAGE PLANS: Select Vectorization: className: VectorSelectOperator native: true - projectedOutputColumnNums: [6, 2, 4, 1, 18] - selectExpressions: VectorUDFAdaptor(CASE WHEN (cstring1 is not null) THEN (cstring1) WHEN (cint is not null) THEN (cint) WHEN (cfloat is not null) THEN (cfloat) WHEN (csmallint is not null) THEN (csmallint) ELSE ('none') END)(children: VectorUDFAdaptor(cstring1 is not null) -> 14:boolean, VectorUDFAdaptor(cint is not null) -> 15:boolean, VectorUDFAdaptor(cfloat is not null) -> 16:boolean, VectorUDFAdaptor(csmallint is not null) -> 17:boolean) -> 18:string + projectedOutputColumnNums: [6, 2, 4, 1, 21] + selectExpressions: VectorUDFAdaptor(CASE WHEN (cstring1 is not null) THEN (cstring1) WHEN (cint is not null) THEN (CAST( cint AS STRING)) WHEN (cfloat is not null) THEN (CAST( cfloat AS STRING)) WHEN (csmallint is not null) THEN (CAST( csmallint AS STRING)) ELSE ('none') END)(children: VectorUDFAdaptor(cstring1 is not null) -> 14:boolean, VectorUDFAdaptor(cint is not null) -> 15:boolean, VectorUDFAdaptor(CAST( cint AS STRING)) -> 16:string, VectorUDFAdaptor(cfloat is not null) -> 17:boolean, VectorUDFAdaptor(CAST( cfloat AS STRING)) -> 18:string, VectorUDFAdaptor(csmallint is not null) -> 19:boolean, VectorUDFAdaptor(CAST( csmallint AS STRING)) -> 20:string) -> 21:string Reduce Sink Vectorization: className: VectorReduceSinkObjectHashOperator native: true @@ -517,8 +517,8 @@ STAGE PLANS: Select Vectorization: className: VectorSelectOperator native: true - projectedOutputColumnNums: [6, 2, 4, 1, 18] - selectExpressions: VectorUDFAdaptor(CASE WHEN (cstring1 is not null) THEN (cstring1) WHEN (cint is not null) THEN (cint) WHEN (cfloat is not null) THEN (cfloat) WHEN (csmallint is not null) THEN (csmallint) ELSE (null) END)(children: VectorUDFAdaptor(cstring1 is not null) -> 14:boolean, VectorUDFAdaptor(cint is not null) -> 15:boolean, VectorUDFAdaptor(cfloat is not null) -> 16:boolean, VectorUDFAdaptor(csmallint is not null) -> 17:boolean) -> 18:string + projectedOutputColumnNums: [6, 2, 4, 1, 21] + selectExpressions: VectorUDFAdaptor(CASE WHEN (cstring1 is not null) THEN (cstring1) WHEN (cint is not null) THEN (CAST( cint AS STRING)) WHEN (cfloat is not null) THEN (CAST( cfloat AS STRING)) WHEN (csmallint is not null) THEN (CAST( csmallint AS STRING)) ELSE (null) END)(children: VectorUDFAdaptor(cstring1 is not null) -> 14:boolean, VectorUDFAdaptor(cint is not null) -> 15:boolean, VectorUDFAdaptor(CAST( cint AS STRING)) -> 16:string, VectorUDFAdaptor(cfloat is not null) -> 17:boolean, VectorUDFAdaptor(CAST( cfloat AS STRING)) -> 18:string, VectorUDFAdaptor(csmallint is not null) -> 19:boolean, VectorUDFAdaptor(CAST( csmallint AS STRING)) -> 20:string) -> 21:string Reduce Sink Vectorization: className: VectorReduceSinkObjectHashOperator native: true