diff --git a/fe/src/main/java/org/apache/impala/analysis/Analyzer.java b/fe/src/main/java/org/apache/impala/analysis/Analyzer.java index 96ab09772..48193426d 100644 --- a/fe/src/main/java/org/apache/impala/analysis/Analyzer.java +++ b/fe/src/main/java/org/apache/impala/analysis/Analyzer.java @@ -1316,9 +1316,11 @@ public class Analyzer { Expr e = globalState_.conjuncts.get(conjunctId); Preconditions.checkState(e != null); if (!canEvalFullOuterJoinedConjunct(e, nodeTblRefIds) || - !canEvalAntiJoinedConjunct(e, nodeTblRefIds)) { + !canEvalAntiJoinedConjunct(e, nodeTblRefIds) || + !canEvalOuterJoinedConjunct(e, nodeTblRefIds)) { continue; } + if (ojClauseConjuncts != null && !ojClauseConjuncts.contains(conjunctId)) continue; result.add(e); } @@ -1326,8 +1328,8 @@ public class Analyzer { } /** - * Checks if a conjunct can be evaluated at a node materializing a list of tuple ids - * 'tids'. + * Returns false if 'e' references a full outer joined tuple and it is incorrect to + * evaluate 'e' at a node materializing 'tids'. Returns true otherwise. */ public boolean canEvalFullOuterJoinedConjunct(Expr e, List tids) { TableRef fullOuterJoin = getFullOuterJoinRef(e); @@ -1335,6 +1337,16 @@ public class Analyzer { return tids.containsAll(fullOuterJoin.getAllTableRefIds()); } + /** + * Returns false if 'e' originates from an outer-join On-clause and it is incorrect to + * evaluate 'e' at a node materializing 'tids'. Returns true otherwise. + */ + public boolean canEvalOuterJoinedConjunct(Expr e, List tids) { + TableRef outerJoin = globalState_.ojClauseByConjunct.get(e.getId()); + if (outerJoin == null) return true; + return tids.containsAll(outerJoin.getAllTableRefIds()); + } + /** * Returns true if predicate 'e' can be correctly evaluated by a tree materializing * 'tupleIds', otherwise false: diff --git a/testdata/workloads/functional-planner/queries/PlannerTest/outer-joins.test b/testdata/workloads/functional-planner/queries/PlannerTest/outer-joins.test index 3a39d14c5..2d5d6cd35 100644 --- a/testdata/workloads/functional-planner/queries/PlannerTest/outer-joins.test +++ b/testdata/workloads/functional-planner/queries/PlannerTest/outer-joins.test @@ -890,3 +890,32 @@ PLAN-ROOT SINK 04:SCAN HDFS [functional.alltypestiny e] partitions=4/4 files=4 size=460B ==== +# IMPALA-3125: Test that the On-clause predicates from an outer join are assigned to the +# corresponding outer-join node, even if the predicates do not reference the join rhs. +select a.id aid, b.id bid, a.int_col aint, b.int_col bint +from functional.alltypes a +inner join functional.alltypes b + on a.int_col = b.int_col +left outer join functional.alltypes c + on a.id = b.id and b.bigint_col = c.bigint_col +---- PLAN +PLAN-ROOT SINK +| +04:HASH JOIN [LEFT OUTER JOIN] +| hash predicates: b.bigint_col = c.bigint_col +| other join predicates: a.id = b.id +| +|--02:SCAN HDFS [functional.alltypes c] +| partitions=24/24 files=24 size=478.45KB +| +03:HASH JOIN [INNER JOIN] +| hash predicates: b.int_col = a.int_col +| runtime filters: RF000 <- a.int_col +| +|--00:SCAN HDFS [functional.alltypes a] +| partitions=24/24 files=24 size=478.45KB +| +01:SCAN HDFS [functional.alltypes b] + partitions=24/24 files=24 size=478.45KB + runtime filters: RF000 -> b.int_col +====