Repository: hive
Updated Branches:
  refs/heads/master 5dfe23efa -> 5cd6bb954


HIVE-14021: When converting to CNF, fail if the expression exceeds a threshold 
(Jesus Camacho Rodriguez, reviewed by Ashutosh Chauhan)


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

Branch: refs/heads/master
Commit: 5cd6bb9540d7857851b6042f4b30d413c7324a98
Parents: 5dfe23e
Author: Jesus Camacho Rodriguez <[email protected]>
Authored: Wed Jun 22 07:51:33 2016 -0700
Committer: Jesus Camacho Rodriguez <[email protected]>
Committed: Thu Jun 23 07:47:04 2016 -0700

----------------------------------------------------------------------
 .../org/apache/hadoop/hive/conf/HiveConf.java   |   3 +
 .../hive/ql/optimizer/calcite/HiveRexUtil.java  | 155 +++++++++++++++++++
 .../rules/HivePointLookupOptimizerRule.java     |  31 ++--
 .../calcite/rules/HivePreFilteringRule.java     |  25 +--
 .../hadoop/hive/ql/parse/CalcitePlanner.java    |   8 +-
 .../optimizer/calcite/TestCBOMaxNumToCNF.java   |  83 ++++++++++
 6 files changed, 274 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hive/blob/5cd6bb95/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java 
b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
index faf9088..1d1306f 100644
--- a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
+++ b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
@@ -990,6 +990,9 @@ public class HiveConf extends Configuration {
 
     // CBO related
     HIVE_CBO_ENABLED("hive.cbo.enable", true, "Flag to control enabling Cost 
Based Optimizations using Calcite framework."),
+    HIVE_CBO_CNF_NODES_LIMIT("hive.cbo.cnf.maxnodes", -1, "When converting to 
conjunctive normal form (CNF), fail if" +
+        "the expression exceeds this threshold; the threshold is expressed in 
terms of number of nodes (leaves and" +
+        "interior nodes). -1 to not set up a threshold."),
     HIVE_CBO_RETPATH_HIVEOP("hive.cbo.returnpath.hiveop", false, "Flag to 
control calcite plan to hive operator conversion"),
     HIVE_CBO_EXTENDED_COST_MODEL("hive.cbo.costmodel.extended", false, "Flag 
to control enabling the extended cost model based on"
                                  + "CPU, IO and cardinality. Otherwise, the 
cost model is based on cardinality."),

http://git-wip-us.apache.org/repos/asf/hive/blob/5cd6bb95/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveRexUtil.java
----------------------------------------------------------------------
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveRexUtil.java 
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveRexUtil.java
index d466378..87fc1b9 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveRexUtil.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveRexUtil.java
@@ -38,6 +38,7 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.ControlFlowException;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 import org.slf4j.Logger;
@@ -51,6 +52,160 @@ public class HiveRexUtil {
 
   protected static final Logger LOG = 
LoggerFactory.getLogger(HiveRexUtil.class);
 
+
+  /** Converts an expression to conjunctive normal form (CNF).
+   *
+   * <p>The following expression is in CNF:
+   *
+   * <blockquote>(a OR b) AND (c OR d)</blockquote>
+   *
+   * <p>The following expression is not in CNF:
+   *
+   * <blockquote>(a AND b) OR c</blockquote>
+   *
+   * but can be converted to CNF:
+   *
+   * <blockquote>(a OR c) AND (b OR c)</blockquote>
+   *
+   * <p>The following expression is not in CNF:
+   *
+   * <blockquote>NOT (a OR NOT b)</blockquote>
+   *
+   * but can be converted to CNF by applying de Morgan's theorem:
+   *
+   * <blockquote>NOT a AND b</blockquote>
+   *
+   * <p>Expressions not involving AND, OR or NOT at the top level are in CNF.
+   */
+  public static RexNode toCnf(RexBuilder rexBuilder, RexNode rex) {
+    return new CnfHelper(rexBuilder).toCnf(rex);
+  }
+
+  public static RexNode toCnf(RexBuilder rexBuilder, int maxCNFNodeCount, 
RexNode rex) {
+    return new CnfHelper(rexBuilder, maxCNFNodeCount).toCnf(rex);
+  }
+
+  /** Helps {@link org.apache.calcite.rex.RexUtil#toCnf}. */
+  private static class CnfHelper {
+    final RexBuilder rexBuilder;
+    int currentCount;
+    final int maxNodeCount;
+
+    private CnfHelper(RexBuilder rexBuilder) {
+      this(rexBuilder, Integer.MAX_VALUE);
+    }
+
+    private CnfHelper(RexBuilder rexBuilder, int maxNodeCount) {
+      this.rexBuilder = rexBuilder;
+      this.maxNodeCount = maxNodeCount == -1 ? Integer.MAX_VALUE : 
maxNodeCount;
+    }
+
+    public RexNode toCnf(RexNode rex) {
+      try {
+        this.currentCount = 0;
+        return toCnf2(rex);
+      } catch (OverflowError e) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Transformation to CNF not carried out as number of 
resulting nodes "
+                  + "in expression is greater than the max number of nodes 
allowed");
+        }
+        Util.swallow(e, null);
+        return rex;
+      }
+    }
+
+    private RexNode toCnf2(RexNode rex) {
+      final List<RexNode> operands;
+      switch (rex.getKind()) {
+      case AND:
+        incrementAndCheck();
+        operands = RexUtil.flattenAnd(((RexCall) rex).getOperands());
+        final List<RexNode> cnfOperands = Lists.newArrayList();
+        for (RexNode node : operands) {
+          RexNode cnf = toCnf2(node);
+          switch (cnf.getKind()) {
+          case AND:
+            incrementAndCheck();
+            cnfOperands.addAll(((RexCall) cnf).getOperands());
+            break;
+          default:
+            incrementAndCheck();
+            cnfOperands.add(cnf);
+          }
+        }
+        return and(cnfOperands);
+      case OR:
+        incrementAndCheck();
+        operands = RexUtil.flattenOr(((RexCall) rex).getOperands());
+        final RexNode head = operands.get(0);
+        final RexNode headCnf = toCnf2(head);
+        final List<RexNode> headCnfs = RelOptUtil.conjunctions(headCnf);
+        final RexNode tail = or(Util.skip(operands));
+        final RexNode tailCnf = toCnf2(tail);
+        final List<RexNode> tailCnfs = RelOptUtil.conjunctions(tailCnf);
+        final List<RexNode> list = Lists.newArrayList();
+        for (RexNode h : headCnfs) {
+          for (RexNode t : tailCnfs) {
+            list.add(or(ImmutableList.of(h, t)));
+          }
+        }
+        return and(list);
+      case NOT:
+        final RexNode arg = ((RexCall) rex).getOperands().get(0);
+        switch (arg.getKind()) {
+        case NOT:
+          return toCnf2(((RexCall) arg).getOperands().get(0));
+        case OR:
+          operands = ((RexCall) arg).getOperands();
+          List<RexNode> transformedDisj = new ArrayList<>();
+          for (RexNode input : RexUtil.flattenOr(operands)) {
+            transformedDisj.add(rexBuilder.makeCall(input.getType(), 
SqlStdOperatorTable.NOT,
+                    ImmutableList.of(input)));
+          }
+          return toCnf2(and(transformedDisj));
+        case AND:
+          operands = ((RexCall) arg).getOperands();
+          List<RexNode> transformedConj = new ArrayList<>();
+          for (RexNode input : RexUtil.flattenAnd(operands)) {
+            transformedConj.add(rexBuilder.makeCall(input.getType(), 
SqlStdOperatorTable.NOT,
+                    ImmutableList.of(input)));
+          }
+          return toCnf2(or(transformedConj));
+        default:
+          incrementAndCheck();
+          return rex;
+        }
+      default:
+        incrementAndCheck();
+        return rex;
+      }
+    }
+
+    private RexNode and(Iterable<? extends RexNode> nodes) {
+      return RexUtil.composeConjunction(rexBuilder, nodes, false);
+    }
+
+    private RexNode or(Iterable<? extends RexNode> nodes) {
+      return RexUtil.composeDisjunction(rexBuilder, nodes, false);
+    }
+
+    private void incrementAndCheck() {
+      this.currentCount++;
+      if (this.currentCount > this.maxNodeCount) {
+        throw OverflowError.INSTANCE;
+      }
+    }
+
+    @SuppressWarnings("serial")
+    private static class OverflowError extends ControlFlowException {
+
+      public static final OverflowError INSTANCE = new OverflowError();
+
+      private OverflowError() {}
+    }
+  }
+
+
   /**
    * Simplifies a boolean expression.
    *

http://git-wip-us.apache.org/repos/asf/hive/blob/5cd6bb95/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePointLookupOptimizerRule.java
----------------------------------------------------------------------
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePointLookupOptimizerRule.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePointLookupOptimizerRule.java
index 9609a1e..4cfe782 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePointLookupOptimizerRule.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePointLookupOptimizerRule.java
@@ -69,11 +69,11 @@ public class HivePointLookupOptimizerRule extends 
RelOptRule {
 
 
   // Minimum number of OR clauses needed to transform into IN clauses
-  private final int min;
+  private final int minNumORClauses;
 
-  public HivePointLookupOptimizerRule(int min) {
+  public HivePointLookupOptimizerRule(int minNumORClauses) {
     super(operand(Filter.class, any()));
-    this.min = min;
+    this.minNumORClauses = minNumORClauses;
   }
 
   public void onMatch(RelOptRuleCall call) {
@@ -84,7 +84,8 @@ public class HivePointLookupOptimizerRule extends RelOptRule {
     final RexNode condition = RexUtil.pullFactors(rexBuilder, 
filter.getCondition());
 
     // 1. We try to transform possible candidates
-    RexTransformIntoInClause transformIntoInClause = new 
RexTransformIntoInClause(rexBuilder, filter, min);
+    RexTransformIntoInClause transformIntoInClause = new 
RexTransformIntoInClause(rexBuilder, filter,
+            minNumORClauses);
     RexNode newCondition = transformIntoInClause.apply(condition);
 
     // 2. We merge IN expressions
@@ -109,12 +110,12 @@ public class HivePointLookupOptimizerRule extends 
RelOptRule {
   protected static class RexTransformIntoInClause extends RexShuttle {
     private final RexBuilder rexBuilder;
     private final Filter filterOp;
-    private final int min;
+    private final int minNumORClauses;
 
-    RexTransformIntoInClause(RexBuilder rexBuilder, Filter filterOp, int min) {
+    RexTransformIntoInClause(RexBuilder rexBuilder, Filter filterOp, int 
minNumORClauses) {
       this.filterOp = filterOp;
       this.rexBuilder = rexBuilder;
-      this.min = min;
+      this.minNumORClauses = minNumORClauses;
     }
 
     @Override public RexNode visitCall(RexCall call) {
@@ -128,9 +129,9 @@ public class HivePointLookupOptimizerRule extends 
RelOptRule {
             if (operand.getKind() == SqlKind.OR) {
               try {
                 newOperand = transformIntoInClauseCondition(rexBuilder,
-                        filterOp.getRowType(), operand, min);
+                        filterOp.getRowType(), operand, minNumORClauses);
                 if (newOperand == null) {
-                  return call;
+                  newOperand = operand;
                 }
               } catch (SemanticException e) {
                 LOG.error("Exception in HivePointLookupOptimizerRule", e);
@@ -146,7 +147,7 @@ public class HivePointLookupOptimizerRule extends 
RelOptRule {
         case OR:
           try {
             node = transformIntoInClauseCondition(rexBuilder,
-                    filterOp.getRowType(), call, min);
+                    filterOp.getRowType(), call, minNumORClauses);
             if (node == null) {
               return call;
             }
@@ -162,23 +163,19 @@ public class HivePointLookupOptimizerRule extends 
RelOptRule {
     }
 
     private static RexNode transformIntoInClauseCondition(RexBuilder 
rexBuilder, RelDataType inputSchema,
-            RexNode condition, int min) throws SemanticException {
+            RexNode condition, int minNumORClauses) throws SemanticException {
       assert condition.getKind() == SqlKind.OR;
 
       // 1. We extract the information necessary to create the predicate for 
the new
       //    filter
       ListMultimap<RexInputRef,RexLiteral> columnConstantsMap = 
ArrayListMultimap.create();
       ImmutableList<RexNode> operands = RexUtil.flattenOr(((RexCall) 
condition).getOperands());
-      if (operands.size() < min) {
+      if (operands.size() < minNumORClauses) {
         // We bail out
         return null;
       }
       for (int i = 0; i < operands.size(); i++) {
-        RexNode operand = operands.get(i);
-
-        final RexNode operandCNF = RexUtil.toCnf(rexBuilder, operand);
-        final List<RexNode> conjunctions = RelOptUtil.conjunctions(operandCNF);
-
+        final List<RexNode> conjunctions = 
RelOptUtil.conjunctions(operands.get(i));
         for (RexNode conjunction: conjunctions) {
           // 1.1. If it is not a RexCall, we bail out
           if (!(conjunction instanceof RexCall)) {

http://git-wip-us.apache.org/repos/asf/hive/blob/5cd6bb95/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
----------------------------------------------------------------------
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
index 17fcc82..7c2a7e5 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
@@ -39,6 +39,7 @@ import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.hadoop.hive.ql.optimizer.calcite.HiveCalciteUtil;
 import org.apache.hadoop.hive.ql.optimizer.calcite.HiveRelFactories;
+import org.apache.hadoop.hive.ql.optimizer.calcite.HiveRexUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,19 +54,21 @@ public class HivePreFilteringRule extends RelOptRule {
                                                           
.getLogger(HivePreFilteringRule.class
                                                               .getName());
 
-  public static final HivePreFilteringRule INSTANCE   = new 
HivePreFilteringRule();
-
-  private final FilterFactory              filterFactory;
-
   private static final Set<SqlKind>        COMPARISON = 
EnumSet.of(SqlKind.EQUALS,
                                                           
SqlKind.GREATER_THAN_OR_EQUAL,
                                                           
SqlKind.LESS_THAN_OR_EQUAL,
                                                           
SqlKind.GREATER_THAN, SqlKind.LESS_THAN,
                                                           SqlKind.NOT_EQUALS);
 
-  private HivePreFilteringRule() {
+  private final FilterFactory              filterFactory;
+
+  // Max number of nodes when converting to CNF
+  private final int maxCNFNodeCount;
+
+  public HivePreFilteringRule(int maxCNFNodeCount) {
     super(operand(Filter.class, operand(RelNode.class, any())));
     this.filterFactory = HiveRelFactories.HIVE_FILTER_FACTORY;
+    this.maxCNFNodeCount = maxCNFNodeCount;
   }
 
   @Override
@@ -120,7 +123,7 @@ public class HivePreFilteringRule extends RelOptRule {
 
       for (RexNode operand : operands) {
         if (operand.getKind() == SqlKind.OR) {
-          extractedCommonOperands = extractCommonOperands(rexBuilder, operand);
+          extractedCommonOperands = extractCommonOperands(rexBuilder, operand, 
maxCNFNodeCount);
           for (RexNode extractedExpr : extractedCommonOperands) {
             if (operandsToPushDownDigest.add(extractedExpr.toString())) {
               operandsToPushDown.add(extractedExpr);
@@ -155,7 +158,7 @@ public class HivePreFilteringRule extends RelOptRule {
       break;
 
     case OR:
-      operandsToPushDown = extractCommonOperands(rexBuilder, 
topFilterCondition);
+      operandsToPushDown = extractCommonOperands(rexBuilder, 
topFilterCondition, maxCNFNodeCount);
       break;
     default:
       return;
@@ -191,7 +194,8 @@ public class HivePreFilteringRule extends RelOptRule {
 
   }
 
-  private static List<RexNode> extractCommonOperands(RexBuilder rexBuilder, 
RexNode condition) {
+  private static List<RexNode> extractCommonOperands(RexBuilder rexBuilder, 
RexNode condition,
+          int maxCNFNodeCount) {
     assert condition.getKind() == SqlKind.OR;
     Multimap<String, RexNode> reductionCondition = LinkedHashMultimap.create();
 
@@ -200,13 +204,12 @@ public class HivePreFilteringRule extends RelOptRule {
     Set<String> refsInAllOperands = null;
 
     // 1. We extract the information necessary to create the predicate for the
-    // new
-    // filter; currently we support comparison functions, in and between
+    // new filter; currently we support comparison functions, in and between
     ImmutableList<RexNode> operands = RexUtil.flattenOr(((RexCall) 
condition).getOperands());
     for (int i = 0; i < operands.size(); i++) {
       final RexNode operand = operands.get(i);
 
-      final RexNode operandCNF = RexUtil.toCnf(rexBuilder, operand);
+      final RexNode operandCNF = HiveRexUtil.toCnf(rexBuilder, 
maxCNFNodeCount, operand);
       final List<RexNode> conjunctions = RelOptUtil.conjunctions(operandCNF);
 
       Set<String> refsInCurrentOperand = Sets.newHashSet();

http://git-wip-us.apache.org/repos/asf/hive/blob/5cd6bb95/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java 
b/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
index d084552..0d4c1bb 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
@@ -1119,6 +1119,9 @@ public class CalcitePlanner extends SemanticAnalyzer {
 
       PerfLogger perfLogger = SessionState.getPerfLogger();
 
+      final int maxCNFNodeCount = 
conf.getIntVar(HiveConf.ConfVars.HIVE_CBO_CNF_NODES_LIMIT);
+      final int minNumORClauses = 
conf.getIntVar(HiveConf.ConfVars.HIVEPOINTLOOKUPOPTIMIZERMIN);
+
       //1. Distinct aggregate rewrite
       // Run this optimization early, since it is expanding the operator 
pipeline.
       if (!conf.getVar(HiveConf.ConfVars.HIVE_EXECUTION_ENGINE).equals("mr") &&
@@ -1139,7 +1142,7 @@ public class CalcitePlanner extends SemanticAnalyzer {
       // ((R1.x=R2.x) and R1.z=10)) and rand(1) < 0.1
       perfLogger.PerfLogBegin(this.getClass().getName(), PerfLogger.OPTIMIZER);
       basePlan = hepPlan(basePlan, false, mdProvider, null, 
HepMatchOrder.ARBITRARY,
-          HivePreFilteringRule.INSTANCE);
+          new HivePreFilteringRule(maxCNFNodeCount));
       perfLogger.PerfLogEnd(this.getClass().getName(), PerfLogger.OPTIMIZER,
         "Calcite: Prejoin ordering transformation, factor out common filter 
elements and separating deterministic vs non-deterministic UDF");
 
@@ -1165,8 +1168,7 @@ public class CalcitePlanner extends SemanticAnalyzer {
       rules.add(HiveReduceExpressionsRule.FILTER_INSTANCE);
       rules.add(HiveReduceExpressionsRule.JOIN_INSTANCE);
       if (conf.getBoolVar(HiveConf.ConfVars.HIVEPOINTLOOKUPOPTIMIZER)) {
-        final int min = 
conf.getIntVar(HiveConf.ConfVars.HIVEPOINTLOOKUPOPTIMIZERMIN);
-        rules.add(new HivePointLookupOptimizerRule(min));
+        rules.add(new HivePointLookupOptimizerRule(minNumORClauses));
       }
       rules.add(HiveJoinAddNotNullRule.INSTANCE_JOIN);
       rules.add(HiveJoinAddNotNullRule.INSTANCE_SEMIJOIN);

http://git-wip-us.apache.org/repos/asf/hive/blob/5cd6bb95/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBOMaxNumToCNF.java
----------------------------------------------------------------------
diff --git 
a/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBOMaxNumToCNF.java
 
b/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBOMaxNumToCNF.java
new file mode 100644
index 0000000..277ac1e
--- /dev/null
+++ 
b/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBOMaxNumToCNF.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hive.ql.optimizer.calcite;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.junit.Test;
+
+public class TestCBOMaxNumToCNF {
+
+  final int maxNumNodesCNF = 8;
+
+  @Test
+  public void testCBOMaxNumToCNF1() {
+    // OR(=($0, 1), AND(=($0, 0), =($1, 8)))
+    // transformation creates 7 nodes AND(OR(=($0, 1), =($0, 0)), OR(=($0, 1), 
=($1, 8)))
+    // thus, it is triggered
+    final RelDataTypeFactory typeFactory = new JavaTypeFactoryImpl();
+    final RexBuilder rexBuilder = new RexBuilder(typeFactory);
+    final RexNode cond = rexBuilder.makeCall(SqlStdOperatorTable.OR,
+            rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+                    
rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.INTEGER), 0),
+                    rexBuilder.makeLiteral(1, 
typeFactory.createSqlType(SqlTypeName.INTEGER), false)),
+            rexBuilder.makeCall(SqlStdOperatorTable.AND,
+                    rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+                            
rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.INTEGER), 0),
+                            rexBuilder.makeLiteral(0, 
typeFactory.createSqlType(SqlTypeName.INTEGER), false)),
+                    rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+                            
rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.INTEGER), 1),
+                            rexBuilder.makeLiteral(8, 
typeFactory.createSqlType(SqlTypeName.INTEGER), false))));
+    final RexNode newCond = HiveRexUtil.toCnf(rexBuilder, maxNumNodesCNF, 
cond);
+
+    assertEquals(newCond.toString(), "AND(OR(=($0, 1), =($0, 0)), OR(=($0, 1), 
=($1, 8)))");
+  }
+
+  @Test
+  public void testCBOMaxNumToCNF2() {
+    // OR(=($0, 1), =($0, 2), AND(=($0, 0), =($1, 8)))
+    // transformation creates 9 nodes AND(OR(=($0, 1), =($0, 2), =($0, 0)), 
OR(=($0, 1), =($0, 2), =($1, 8)))
+    // thus, it is NOT triggered
+    final RelDataTypeFactory typeFactory = new JavaTypeFactoryImpl();
+    final RexBuilder rexBuilder = new RexBuilder(typeFactory);
+    final RexNode cond = rexBuilder.makeCall(SqlStdOperatorTable.OR,
+            rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+                    
rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.INTEGER), 0),
+                    rexBuilder.makeLiteral(1, 
typeFactory.createSqlType(SqlTypeName.INTEGER), false)),
+            rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+                    
rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.INTEGER), 0),
+                    rexBuilder.makeLiteral(2, 
typeFactory.createSqlType(SqlTypeName.INTEGER), false)),
+            rexBuilder.makeCall(SqlStdOperatorTable.AND,
+                    rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+                            
rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.INTEGER), 0),
+                            rexBuilder.makeLiteral(0, 
typeFactory.createSqlType(SqlTypeName.INTEGER), false)),
+                    rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+                            
rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.INTEGER), 1),
+                            rexBuilder.makeLiteral(8, 
typeFactory.createSqlType(SqlTypeName.INTEGER), false))));
+    final RexNode newCond = HiveRexUtil.toCnf(rexBuilder, maxNumNodesCNF, 
cond);
+
+    assertEquals(newCond.toString(), "OR(=($0, 1), =($0, 2), AND(=($0, 0), 
=($1, 8)))");
+  }
+
+}

Reply via email to