Repository: drill
Updated Branches:
  refs/heads/merge_09_19_16 [created] a29f1e292


DRILL-4927: Add support for Null Equality Joins

These changes are a subset of the original pull request from DRILL-4539 
(PR-462).
- Added changes to support Null Equality Joins;
- Created tests for it.

close apache/drill#603


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/a29f1e29
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/a29f1e29
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/a29f1e29

Branch: refs/heads/merge_09_19_16
Commit: a29f1e292fc8068ce13d336ba93abcc64f31da26
Parents: 46b424c
Author: Roman Kulyk <rom.ku...@gmail.com>
Authored: Tue Oct 4 13:58:13 2016 +0000
Committer: Aman Sinha <asi...@maprtech.com>
Committed: Tue Oct 11 23:02:26 2016 -0700

----------------------------------------------------------------------
 .../exec/physical/impl/join/JoinUtils.java      | 37 ++++-----
 .../exec/planner/common/DrillJoinRelBase.java   |  9 ++-
 .../planner/logical/DrillFilterJoinRules.java   |  3 +-
 .../exec/planner/logical/DrillJoinRel.java      |  2 +-
 .../exec/planner/logical/DrillJoinRule.java     | 53 ++++++-------
 .../exec/planner/physical/HashJoinPrel.java     |  2 +-
 .../exec/planner/physical/JoinPruleBase.java    |  5 +-
 .../exec/planner/physical/MergeJoinPrel.java    |  2 +-
 .../planner/physical/NestedLoopJoinPrel.java    |  2 +-
 .../planner/physical/NestedLoopJoinPrule.java   |  5 +-
 .../planner/sql/handlers/DefaultSqlHandler.java |  6 +-
 .../java/org/apache/drill/TestJoinNullable.java | 79 ++++++++++++++++++++
 pom.xml                                         |  2 +-
 13 files changed, 149 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/join/JoinUtils.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/join/JoinUtils.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/join/JoinUtils.java
index 61640bc..be363d2 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/join/JoinUtils.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/join/JoinUtils.java
@@ -86,17 +86,19 @@ public class JoinUtils {
      * Check if the given RelNode contains any Cartesian join.
      * Return true if find one. Otherwise, return false.
      *
-     * @param relNode   the RelNode to be inspected.
-     * @param leftKeys  a list used for the left input into the join which has
-     *                  equi-join keys. It can be empty or not (but not null),
-     *                  this method will clear this list before using it.
-     * @param rightKeys a list used for the right input into the join which has
-     *                  equi-join keys. It can be empty or not (but not null),
-     *                  this method will clear this list before using it.
-     * @return          Return true if the given relNode contains Cartesian 
join.
-     *                  Otherwise, return false
+     * @param relNode     the RelNode to be inspected.
+     * @param leftKeys    a list used for the left input into the join which 
has
+     *                    equi-join keys. It can be empty or not (but not 
null),
+     *                    this method will clear this list before using it.
+     * @param rightKeys   a list used for the right input into the join which 
has
+     *                    equi-join keys. It can be empty or not (but not 
null),
+     *                    this method will clear this list before using it.
+     * @param filterNulls The join key positions for which null values will not
+     *                    match.
+     * @return            Return true if the given relNode contains Cartesian 
join.
+     *                    Otherwise, return false
      */
-  public static boolean checkCartesianJoin(RelNode relNode, List<Integer> 
leftKeys, List<Integer> rightKeys) {
+  public static boolean checkCartesianJoin(RelNode relNode, List<Integer> 
leftKeys, List<Integer> rightKeys, List<Boolean> filterNulls) {
     if (relNode instanceof Join) {
       leftKeys.clear();
       rightKeys.clear();
@@ -105,20 +107,20 @@ public class JoinUtils {
       RelNode left = joinRel.getLeft();
       RelNode right = joinRel.getRight();
 
-      RexNode remaining = RelOptUtil.splitJoinCondition(left, right, 
joinRel.getCondition(), leftKeys, rightKeys);
-      if(joinRel.getJoinType() == JoinRelType.INNER) {
-        if(leftKeys.isEmpty() || rightKeys.isEmpty()) {
+      RexNode remaining = RelOptUtil.splitJoinCondition(left, right, 
joinRel.getCondition(), leftKeys, rightKeys, filterNulls);
+      if (joinRel.getJoinType() == JoinRelType.INNER) {
+        if (leftKeys.isEmpty() || rightKeys.isEmpty()) {
           return true;
         }
       } else {
-        if(!remaining.isAlwaysTrue() || leftKeys.isEmpty() || 
rightKeys.isEmpty()) {
+        if (!remaining.isAlwaysTrue() || leftKeys.isEmpty() || 
rightKeys.isEmpty()) {
           return true;
         }
       }
     }
 
     for (RelNode child : relNode.getInputs()) {
-      if(checkCartesianJoin(child, leftKeys, rightKeys)) {
+      if (checkCartesianJoin(child, leftKeys, rightKeys, filterNulls)) {
         return true;
       }
     }
@@ -249,13 +251,14 @@ public class JoinUtils {
   }
 
   public static JoinCategory getJoinCategory(RelNode left, RelNode right, 
RexNode condition,
-      List<Integer> leftKeys, List<Integer> rightKeys) {
+      List<Integer> leftKeys, List<Integer> rightKeys, List<Boolean> 
filterNulls) {
     if (condition.isAlwaysTrue()) {
       return JoinCategory.CARTESIAN;
     }
     leftKeys.clear();
     rightKeys.clear();
-    RexNode remaining = RelOptUtil.splitJoinCondition(left, right, condition, 
leftKeys, rightKeys);
+    filterNulls.clear();
+    RexNode remaining = RelOptUtil.splitJoinCondition(left, right, condition, 
leftKeys, rightKeys, filterNulls);
 
     if (!remaining.isAlwaysTrue() || (leftKeys.size() == 0 || rightKeys.size() 
== 0) ) {
       // for practical purposes these cases could be treated as inequality

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/common/DrillJoinRelBase.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/common/DrillJoinRelBase.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/common/DrillJoinRelBase.java
index 2d6f7d6..6bd0e9c 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/common/DrillJoinRelBase.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/common/DrillJoinRelBase.java
@@ -48,7 +48,12 @@ import com.google.common.collect.Lists;
  */
 public abstract class DrillJoinRelBase extends Join implements DrillRelNode {
   protected List<Integer> leftKeys = Lists.newArrayList();
-  protected List<Integer> rightKeys = Lists.newArrayList() ;
+  protected List<Integer> rightKeys = Lists.newArrayList();
+
+  /**
+   * The join key positions for which null values will not match.
+   */
+  protected List<Boolean> filterNulls = Lists.newArrayList();
   private final double joinRowFactor;
 
   public DrillJoinRelBase(RelOptCluster cluster, RelTraitSet traits, RelNode 
left, RelNode right, RexNode condition,
@@ -59,7 +64,7 @@ public abstract class DrillJoinRelBase extends Join 
implements DrillRelNode {
 
   @Override
   public RelOptCost computeSelfCost(RelOptPlanner planner) {
-    JoinCategory category = JoinUtils.getJoinCategory(left, right, condition, 
leftKeys, rightKeys);
+    JoinCategory category = JoinUtils.getJoinCategory(left, right, condition, 
leftKeys, rightKeys, filterNulls);
     if (category == JoinCategory.CARTESIAN || category == 
JoinCategory.INEQUALITY) {
       if (PrelUtil.getPlannerSettings(planner).isNestedLoopJoinEnabled()) {
         if (PrelUtil.getPlannerSettings(planner).isNlJoinForScalarOnly()) {

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillFilterJoinRules.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillFilterJoinRules.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillFilterJoinRules.java
index 2affb0c..0c1fdb3 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillFilterJoinRules.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillFilterJoinRules.java
@@ -53,8 +53,9 @@ public class DrillFilterJoinRules {
           List<RexNode> tmpLeftKeys = Lists.newArrayList();
           List<RexNode> tmpRightKeys = Lists.newArrayList();
           List<RelDataTypeField> sysFields = Lists.newArrayList();
+          List<Integer> filterNulls = Lists.newArrayList();
 
-          RexNode remaining = RelOptUtil.splitJoinCondition(sysFields, 
join.getLeft(), join.getRight(), exp, tmpLeftKeys, tmpRightKeys, null, null);
+          RexNode remaining = RelOptUtil.splitJoinCondition(sysFields, 
join.getLeft(), join.getRight(), exp, tmpLeftKeys, tmpRightKeys, filterNulls, 
null);
 
           if (remaining.isAlwaysTrue()) {
             return true;

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRel.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRel.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRel.java
index ca08363..18abdd5 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRel.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRel.java
@@ -52,7 +52,7 @@ public class DrillJoinRel extends DrillJoinRelBase implements 
DrillRel {
       JoinRelType joinType)  {
     super(cluster, traits, left, right, condition, joinType);
     assert traits.contains(DrillRel.DRILL_LOGICAL);
-    RelOptUtil.splitJoinCondition(left, right, condition, leftKeys, rightKeys);
+    RelOptUtil.splitJoinCondition(left, right, condition, leftKeys, rightKeys, 
filterNulls);
   }
 
   public DrillJoinRel(RelOptCluster cluster, RelTraitSet traits, RelNode left, 
RelNode right, RexNode condition,

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRule.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRule.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRule.java
index f3b9f6a..d41ae72 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRule.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillJoinRule.java
@@ -63,14 +63,15 @@ public class DrillJoinRule extends RelOptRule {
 
     List<Integer> leftKeys = Lists.newArrayList();
     List<Integer> rightKeys = Lists.newArrayList();
+    List<Boolean> filterNulls = Lists.newArrayList();
     int numLeftFields = convertedLeft.getRowType().getFieldCount();
 
     boolean addFilter = false;
     RexNode origJoinCondition = join.getCondition();
     RexNode newJoinCondition = origJoinCondition;
 
-    RexNode remaining = RelOptUtil.splitJoinCondition(convertedLeft, 
convertedRight, origJoinCondition, leftKeys, rightKeys);
-    boolean hasEquijoins = (leftKeys.size() == rightKeys.size() && 
leftKeys.size() > 0) ? true : false;
+    RexNode remaining = RelOptUtil.splitJoinCondition(convertedLeft, 
convertedRight, origJoinCondition, leftKeys, rightKeys, filterNulls);
+    boolean hasEquijoins = leftKeys.size() == rightKeys.size() && 
leftKeys.size() > 0;
 
     // If the join involves equijoins and non-equijoins, then we can process 
the non-equijoins through
     // a filter right after the join
@@ -79,35 +80,15 @@ public class DrillJoinRule extends RelOptRule {
     if (! remaining.isAlwaysTrue()) {
       if (hasEquijoins && join.getJoinType()== JoinRelType.INNER) {
         addFilter = true;
-        List<RexNode> equijoinList = Lists.newArrayList();
-        List<RelDataTypeField> leftTypes = 
convertedLeft.getRowType().getFieldList();
-        List<RelDataTypeField> rightTypes = 
convertedRight.getRowType().getFieldList();
-        RexBuilder builder = join.getCluster().getRexBuilder();
-
-        for (int i=0; i < leftKeys.size(); i++) {
-          int leftKeyOrdinal = leftKeys.get(i).intValue();
-          int rightKeyOrdinal = rightKeys.get(i).intValue();
-
-          equijoinList.add(builder.makeCall(
-              SqlStdOperatorTable.EQUALS,
-              builder.makeInputRef(leftTypes.get(leftKeyOrdinal).getType(), 
leftKeyOrdinal),
-              builder.makeInputRef(rightTypes.get(rightKeyOrdinal).getType(), 
rightKeyOrdinal + numLeftFields)
-             ) );
-        }
-        newJoinCondition = RexUtil.composeConjunction(builder, equijoinList, 
false);
-      } else {
-//        tracer.warning("Non-equijoins are only supported in the presence of 
an equijoin.");
-//        return;
+        newJoinCondition = buildJoinCondition(convertedLeft, convertedRight, 
leftKeys, rightKeys, filterNulls, join.getCluster().getRexBuilder());
       }
+    } else {
+      newJoinCondition = buildJoinCondition(convertedLeft, convertedRight, 
leftKeys, rightKeys, filterNulls, join.getCluster().getRexBuilder());
     }
-    //else {
-    //
-    //  return;
-    // }
 
     try {
       if (!addFilter) {
-       RelNode joinRel = new DrillJoinRel(join.getCluster(), traits, 
convertedLeft, convertedRight, origJoinCondition,
+       RelNode joinRel = new DrillJoinRel(join.getCluster(), traits, 
convertedLeft, convertedRight, newJoinCondition,
                                          join.getJoinType(), leftKeys, 
rightKeys);
        call.transformTo(joinRel);
       } else {
@@ -119,4 +100,24 @@ public class DrillJoinRule extends RelOptRule {
       tracer.warning(e.toString());
     }
   }
+
+  private RexNode buildJoinCondition(RelNode convertedLeft, RelNode 
convertedRight, List<Integer> leftKeys,
+      List<Integer> rightKeys, List<Boolean> filterNulls, RexBuilder builder) {
+    List<RexNode> equijoinList = Lists.newArrayList();
+    final int numLeftFields = convertedLeft.getRowType().getFieldCount();
+    List<RelDataTypeField> leftTypes = 
convertedLeft.getRowType().getFieldList();
+    List<RelDataTypeField> rightTypes = 
convertedRight.getRowType().getFieldList();
+
+    for (int i=0; i < leftKeys.size(); i++) {
+      int leftKeyOrdinal = leftKeys.get(i).intValue();
+      int rightKeyOrdinal = rightKeys.get(i).intValue();
+
+      equijoinList.add(builder.makeCall(
+           filterNulls.get(i) ? SqlStdOperatorTable.EQUALS : 
SqlStdOperatorTable.IS_NOT_DISTINCT_FROM,
+           builder.makeInputRef(leftTypes.get(leftKeyOrdinal).getType(), 
leftKeyOrdinal),
+           builder.makeInputRef(rightTypes.get(rightKeyOrdinal).getType(), 
rightKeyOrdinal + numLeftFields)
+      ));
+    }
+    return RexUtil.composeConjunction(builder, equijoinList, false);
+  }
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/HashJoinPrel.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/HashJoinPrel.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/HashJoinPrel.java
index dc21bdb..ec023f6 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/HashJoinPrel.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/HashJoinPrel.java
@@ -52,7 +52,7 @@ public class HashJoinPrel  extends JoinPrel {
       JoinRelType joinType, boolean swapped) throws InvalidRelException {
     super(cluster, traits, left, right, condition, joinType);
     this.swapped = swapped;
-    joincategory = JoinUtils.getJoinCategory(left, right, condition, leftKeys, 
rightKeys);
+    joincategory = JoinUtils.getJoinCategory(left, right, condition, leftKeys, 
rightKeys, filterNulls);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/JoinPruleBase.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/JoinPruleBase.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/JoinPruleBase.java
index fd0ea69..4342478 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/JoinPruleBase.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/JoinPruleBase.java
@@ -53,8 +53,9 @@ public abstract class JoinPruleBase extends Prule {
   protected boolean checkPreconditions(DrillJoinRel join, RelNode left, 
RelNode right,
       PlannerSettings settings) {
     List<Integer> leftKeys = Lists.newArrayList();
-    List<Integer> rightKeys = Lists.newArrayList() ;
-    JoinCategory category = JoinUtils.getJoinCategory(left, right, 
join.getCondition(), leftKeys, rightKeys);
+    List<Integer> rightKeys = Lists.newArrayList();
+    List<Boolean> filterNulls = Lists.newArrayList();
+    JoinCategory category = JoinUtils.getJoinCategory(left, right, 
join.getCondition(), leftKeys, rightKeys, filterNulls);
     if (category == JoinCategory.CARTESIAN || category == 
JoinCategory.INEQUALITY) {
       return false;
     }

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/MergeJoinPrel.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/MergeJoinPrel.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/MergeJoinPrel.java
index e7141d9..423f7e5 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/MergeJoinPrel.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/MergeJoinPrel.java
@@ -48,7 +48,7 @@ public class MergeJoinPrel  extends JoinPrel {
   public MergeJoinPrel(RelOptCluster cluster, RelTraitSet traits, RelNode 
left, RelNode right, RexNode condition,
       JoinRelType joinType) throws InvalidRelException {
     super(cluster, traits, left, right, condition, joinType);
-    joincategory = JoinUtils.getJoinCategory(left, right, condition, leftKeys, 
rightKeys);
+    joincategory = JoinUtils.getJoinCategory(left, right, condition, leftKeys, 
rightKeys, filterNulls);
   }
 
 

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrel.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrel.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrel.java
index b35017e..d229b33 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrel.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrel.java
@@ -45,7 +45,7 @@ public class NestedLoopJoinPrel  extends JoinPrel {
   public NestedLoopJoinPrel(RelOptCluster cluster, RelTraitSet traits, RelNode 
left, RelNode right, RexNode condition,
                       JoinRelType joinType) throws InvalidRelException {
     super(cluster, traits, left, right, condition, joinType);
-    RelOptUtil.splitJoinCondition(left, right, condition, leftKeys, rightKeys);
+    RelOptUtil.splitJoinCondition(left, right, condition, leftKeys, rightKeys, 
filterNulls);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrule.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrule.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrule.java
index 24be433..2cda15a 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrule.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/NestedLoopJoinPrule.java
@@ -54,8 +54,9 @@ public class NestedLoopJoinPrule extends JoinPruleBase {
     }
 
     List<Integer> leftKeys = Lists.newArrayList();
-    List<Integer> rightKeys = Lists.newArrayList() ;
-    JoinCategory category = JoinUtils.getJoinCategory(left, right, 
join.getCondition(), leftKeys, rightKeys);
+    List<Integer> rightKeys = Lists.newArrayList();
+    List<Boolean> filterNulls = Lists.newArrayList();
+    JoinCategory category = JoinUtils.getJoinCategory(left, right, 
join.getCondition(), leftKeys, rightKeys, filterNulls);
     if (category == JoinCategory.EQUALITY
         && (settings.isHashJoinEnabled() || settings.isMergeJoinEnabled())) {
       return false;

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DefaultSqlHandler.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DefaultSqlHandler.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DefaultSqlHandler.java
index e5359a3..2d0c069 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DefaultSqlHandler.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DefaultSqlHandler.java
@@ -269,7 +269,7 @@ public class DefaultSqlHandler extends AbstractSqlHandler {
     } catch (RelOptPlanner.CannotPlanException ex) {
       logger.error(ex.getMessage());
 
-      if(JoinUtils.checkCartesianJoin(relNode, new ArrayList<Integer>(), new 
ArrayList<Integer>())) {
+      if(JoinUtils.checkCartesianJoin(relNode, new ArrayList<Integer>(), new 
ArrayList<Integer>(), new ArrayList<Boolean>())) {
         throw new UnsupportedRelOperatorException("This query cannot be 
planned possibly due to either a cartesian join or an inequality join");
       } else {
         throw ex;
@@ -428,7 +428,7 @@ public class DefaultSqlHandler extends AbstractSqlHandler {
     } catch (RelOptPlanner.CannotPlanException ex) {
       logger.error(ex.getMessage());
 
-      if(JoinUtils.checkCartesianJoin(drel, new ArrayList<Integer>(), new 
ArrayList<Integer>())) {
+      if(JoinUtils.checkCartesianJoin(drel, new ArrayList<Integer>(), new 
ArrayList<Integer>(), new ArrayList<Boolean>())) {
         throw new UnsupportedRelOperatorException("This query cannot be 
planned possibly due to either a cartesian join or an inequality join");
       } else {
         throw ex;
@@ -451,7 +451,7 @@ public class DefaultSqlHandler extends AbstractSqlHandler {
       } catch (RelOptPlanner.CannotPlanException ex) {
         logger.error(ex.getMessage());
 
-        if(JoinUtils.checkCartesianJoin(drel, new ArrayList<Integer>(), new 
ArrayList<Integer>())) {
+        if(JoinUtils.checkCartesianJoin(drel, new ArrayList<Integer>(), new 
ArrayList<Integer>(), new ArrayList<Boolean>())) {
           throw new UnsupportedRelOperatorException("This query cannot be 
planned possibly due to either a cartesian join or an inequality join");
         } else {
           throw ex;

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java 
b/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java
index 320a992..b34c7fd 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java
@@ -414,4 +414,83 @@ public class TestJoinNullable extends BaseTestQuery{
     assertEquals("Number of output rows", expectedRecordCount, 
actualRecordCount);
   }
 
+  @Test
+  public void testNullEqualInWhereConditionHashJoin() throws Exception {
+    final String query = "SELECT * FROM "
+        + "cp.`jsoninput/nullableOrdered1.json` t1, "
+        + "cp.`jsoninput/nullableOrdered2.json` t2 "
+        + "WHERE t1.key = t2.key OR (t1.key IS NULL AND t2.key IS NULL)";
+    nullEqualJoinHelper(query);
+  }
+
+  @Test
+  public void testNullEqualInWhereConditionMergeJoin() throws Exception {
+    try {
+      test("alter session set `planner.enable_hashjoin` = false");
+      final String query = "SELECT * FROM "
+          + "cp.`jsoninput/nullableOrdered1.json` t1, "
+          + "cp.`jsoninput/nullableOrdered2.json` t2 "
+          + "WHERE t1.key = t2.key OR (t1.key IS NULL AND t2.key IS NULL)";
+      nullEqualJoinHelper(query);
+    } finally {
+      test("alter session set `planner.enable_hashjoin` = true");
+    }
+  }
+
+  @Test
+  public void testNullEqualHashJoin() throws Exception {
+    final String query = "SELECT * FROM "
+        + "cp.`jsoninput/nullableOrdered1.json` t1 JOIN "
+        + "cp.`jsoninput/nullableOrdered2.json` t2 "
+        + "ON t1.key = t2.key OR (t1.key IS NULL AND t2.key IS NULL)";
+    nullEqualJoinHelper(query);
+  }
+
+  @Test
+  public void testNullEqualMergeJoin() throws Exception {
+    try {
+      test("alter session set `planner.enable_hashjoin` = false");
+      final String query = "SELECT * FROM "
+          + "cp.`jsoninput/nullableOrdered1.json` t1 JOIN "
+          + "cp.`jsoninput/nullableOrdered2.json` t2 "
+          + "ON t1.key = t2.key OR (t1.key IS NULL AND t2.key IS NULL)";
+      nullEqualJoinHelper(query);
+    } finally {
+      test("alter session set `planner.enable_hashjoin` = true");
+    }
+  }
+
+  public void nullEqualJoinHelper(final String query) throws Exception {
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("key", "data", "data0", "key0")
+        .baselineValues(null, "L_null_1", "R_null_1", null)
+        .baselineValues(null, "L_null_2", "R_null_1", null)
+        .baselineValues("A", "L_A_1", "R_A_1", "A")
+        .baselineValues("A", "L_A_2", "R_A_1", "A")
+        .baselineValues(null, "L_null_1", "R_null_2", null)
+        .baselineValues(null, "L_null_2", "R_null_2", null)
+        .baselineValues(null, "L_null_1", "R_null_3", null)
+        .baselineValues(null, "L_null_2", "R_null_3", null)
+        .go();
+  }
+
+  @Test
+  public void withNullEqualAdditionFilter() throws Exception {
+    final String query = "SELECT * FROM "
+        + "cp.`jsoninput/nullableOrdered1.json` t1 JOIN "
+        + "cp.`jsoninput/nullableOrdered2.json` t2 "
+        + "ON (t1.key = t2.key OR (t1.key IS NULL AND t2.key IS NULL)) AND 
t1.data LIKE '%1%'";
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("key", "data", "data0", "key0")
+        .baselineValues(null, "L_null_1", "R_null_1", null)
+        .baselineValues("A", "L_A_1", "R_A_1", "A")
+        .baselineValues(null, "L_null_1", "R_null_2", null)
+        .baselineValues(null, "L_null_1", "R_null_3", null)
+        .go();
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/a29f1e29/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0768720..0e92098 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1446,7 +1446,7 @@
           <dependency>
             <groupId>org.apache.calcite</groupId>
             <artifactId>calcite-core</artifactId>
-            <version>1.4.0-drill-r18</version>
+            <version>1.4.0-drill-r19</version>
             <exclusions>
               <exclusion>
                 <groupId>org.jgrapht</groupId>

Reply via email to