diff --git a/fe/src/main/java/org/apache/impala/analysis/CaseExpr.java b/fe/src/main/java/org/apache/impala/analysis/CaseExpr.java index 44406901f..3d9666225 100644 --- a/fe/src/main/java/org/apache/impala/analysis/CaseExpr.java +++ b/fe/src/main/java/org/apache/impala/analysis/CaseExpr.java @@ -127,16 +127,17 @@ public class CaseExpr extends Expr { if (candidate.isNullLiteral()) { // An example case is DECODE(foo, NULL, bar), since NULLs are considered // equal, this becomes CASE WHEN foo IS NULL THEN bar END. - children_.add(encodedIsNull); + children_.add(encodedIsNull.clone()); } else { children_.add(new BinaryPredicate( - BinaryPredicate.Operator.EQ, encoded, candidate)); + BinaryPredicate.Operator.EQ, encoded.clone(), candidate)); } } else { children_.add(new CompoundPredicate(CompoundPredicate.Operator.OR, new CompoundPredicate(CompoundPredicate.Operator.AND, - encodedIsNull, new IsNullPredicate(candidate, false)), - new BinaryPredicate(BinaryPredicate.Operator.EQ, encoded, candidate))); + encodedIsNull.clone(), new IsNullPredicate(candidate, false)), + new BinaryPredicate(BinaryPredicate.Operator.EQ, encoded.clone(), + candidate))); } // Add the value @@ -148,6 +149,11 @@ public class CaseExpr extends Expr { hasElseExpr_ = true; children_.add(decodeExpr.getChild(childIdx)); } + + // Check that these exprs were cloned above, as reusing the same Expr object in + // different places can lead to bugs, eg. if the Expr has multiple parents, they may + // try to cast it to different types. + Preconditions.checkState(!contains(encoded) && !contains(encodedIsNull)); } /** diff --git a/fe/src/main/java/org/apache/impala/common/TreeNode.java b/fe/src/main/java/org/apache/impala/common/TreeNode.java index 3231e332e..730fd4411 100644 --- a/fe/src/main/java/org/apache/impala/common/TreeNode.java +++ b/fe/src/main/java/org/apache/impala/common/TreeNode.java @@ -157,6 +157,15 @@ public abstract class TreeNode> { return false; } + /** + * Returns true if this node or any of its children is 'node'. + */ + public boolean contains(NodeType node) { + if (this == node) return true; + for (NodeType child: children_) if (child.contains(node)) return true; + return false; + } + /** * For each node in nodeList, return true if any subexpression satisfies * contains('predicate'). diff --git a/testdata/workloads/functional-query/queries/QueryTest/exprs.test b/testdata/workloads/functional-query/queries/QueryTest/exprs.test index 4b1ba7690..ed3c9d9ce 100644 --- a/testdata/workloads/functional-query/queries/QueryTest/exprs.test +++ b/testdata/workloads/functional-query/queries/QueryTest/exprs.test @@ -2649,3 +2649,19 @@ select count(*) from functional.alltypestiny group by concat(uuid(), "_test") ---- TYPES BIGINT ==== +---- QUERY +# IMPALA-4716: Tests that decode with 'when' values of different types is analyzed +# correctly when expr rewrites are enabled. +select decode(0, 1, 0, id, 1, 2) a from functional.alltypestiny order by a +---- RESULTS +1 +2 +2 +2 +2 +2 +2 +2 +---- TYPES +TINYINT +====