You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2013/10/17 11:17:43 UTC

git commit: TAJO-260: Add between predicate. (hyunsik)

Updated Branches:
  refs/heads/master 4074a4692 -> 95fa2667b


TAJO-260: Add between predicate. (hyunsik)


Project: http://git-wip-us.apache.org/repos/asf/incubator-tajo/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tajo/commit/95fa2667
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tajo/tree/95fa2667
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tajo/diff/95fa2667

Branch: refs/heads/master
Commit: 95fa2667b12711c7438372ae6e0bcf74f0733f14
Parents: 4074a46
Author: Hyunsik Choi <hy...@apache.org>
Authored: Thu Oct 17 18:13:31 2013 +0900
Committer: Hyunsik Choi <hy...@apache.org>
Committed: Thu Oct 17 18:13:31 2013 +0900

----------------------------------------------------------------------
 CHANGES.txt                                     |   2 +
 .../apache/tajo/algebra/BetweenPredicate.java   |  65 +++++
 .../java/org/apache/tajo/algebra/OpType.java    |   1 +
 .../org/apache/tajo/engine/parser/SQLLexer.g4   |   3 +
 .../org/apache/tajo/engine/parser/SQLParser.g4  |  18 +-
 .../tajo/engine/eval/BetweenPredicateEval.java  | 238 +++++++++++++++++++
 .../org/apache/tajo/engine/eval/BinaryEval.java |  13 +-
 .../org/apache/tajo/engine/eval/EvalType.java   |   1 +
 .../apache/tajo/engine/parser/SQLAnalyzer.java  |   9 +
 .../tajo/engine/planner/LogicalPlanner.java     |   7 +
 .../apache/tajo/engine/eval/TestPredicates.java |  59 +++++
 .../tajo/engine/parser/TestSQLAnalyzer.java     |   6 +-
 12 files changed, 413 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index fed5976..a487e93 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,8 @@ Release 0.2.0 - unreleased
 
   NEW FEATURES
 
+    TAJO-260: Add between predicate. (hyunsik)
+
     TAJO-208: Implement char_length(string) function. (hyunsik)
 
     TAJO-99: Design the interface of join enumeration algorithm. (hyunsik)

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java
new file mode 100644
index 0000000..19f66af
--- /dev/null
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java
@@ -0,0 +1,65 @@
+/**
+ * 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.tajo.algebra;
+
+public class BetweenPredicate extends Expr {
+  private boolean not;
+  // if symmetric is not set, asymmetric is implicit.
+  private boolean symmetric = false;
+  private Expr predicand;
+  private Expr begin;
+  private Expr end;
+
+
+  public BetweenPredicate(boolean not, boolean symmetric, Expr predicand, Expr begin, Expr end) {
+    super(OpType.Between);
+    this.not = not;
+    this.symmetric = symmetric;
+    this.predicand = predicand;
+    this.begin = begin;
+    this.end = end;
+  }
+
+  public boolean isNot() {
+    return not;
+  }
+
+  public boolean isSymmetric() {
+    return symmetric;
+  }
+
+  public Expr predicand() {
+    return predicand;
+  }
+
+  public Expr begin() {
+    return begin;
+  }
+
+  public Expr end() {
+    return end;
+  }
+
+  @Override
+  boolean equalsTo(Expr expr) {
+    BetweenPredicate another = (BetweenPredicate) expr;
+    return symmetric == another.symmetric && predicand.equals(another.predicand) && begin.equals(another.begin) &&
+        end.equals(another.end);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
index 799b196..3651156 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
@@ -62,6 +62,7 @@ public enum OpType {
   GreaterThanOrEquals(BinaryOperator.class),
 
   // other predicates
+  Between(BetweenPredicate.class),
   CaseWhen(CaseWhenPredicate.class),
   IsNullPredicate(IsNullPredicate.class),
   InPredicate(InPredicate.class),

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4 b/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
index b20544f..f0992ef 100644
--- a/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
+++ b/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
@@ -118,9 +118,11 @@ AS : A S;
 ALL : A L L;
 AND : A N D;
 ANY : A N Y;
+ASYMMETRIC : A S Y M M E T R I C;
 ASC : A S C;
 AVG : A V G;
 
+BETWEEN : B E T W E E N;
 BOTH : B O T H;
 BY : B Y;
 
@@ -197,6 +199,7 @@ RIGHT : R I G H T;
 RLIKE : R L I K E;
 ROLLUP : R O L L U P;
 
+SYMMETRIC : S Y M M E T R I C;
 SET : S E T;
 SELECT : S E L E C T;
 SOME : S O M E;

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4 b/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
index 1c6816a..7d48834 100644
--- a/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
+++ b/tajo-core/tajo-core-backend/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
@@ -828,8 +828,9 @@ subquery
 
 predicate
   : comparison_predicate
+  | between_predicate
   | in_predicate
-  | pattern_matching_predicate
+  | pattern_matching_predicate // like predicate and other similar predicates
   | null_predicate
   | exists_predicate
   ;
@@ -856,6 +857,21 @@ comp_op
 
 /*
 ===============================================================================
+  8.3 <between predicate>
+===============================================================================
+*/
+
+between_predicate
+  : predicand=row_value_predicand between_predicate_part_2
+  ;
+
+between_predicate_part_2
+  : (NOT)? BETWEEN (ASYMMETRIC | SYMMETRIC)? begin=row_value_predicand AND end=row_value_predicand
+  ;
+
+
+/*
+===============================================================================
   8.4 <in predicate>
 ===============================================================================
 */

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java
new file mode 100644
index 0000000..0215928
--- /dev/null
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java
@@ -0,0 +1,238 @@
+/**
+ * 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.tajo.engine.eval;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.datum.DatumFactory;
+import org.apache.tajo.datum.NullDatum;
+import org.apache.tajo.storage.Tuple;
+
+public class BetweenPredicateEval extends EvalNode {
+  private static final TajoDataTypes.DataType RES_TYPE = CatalogUtil.newSimpleDataType(TajoDataTypes.Type.BOOLEAN);
+  @Expose private boolean not;
+  @Expose private boolean symmetric;
+  @Expose private EvalNode predicand;
+  @Expose private EvalNode begin;
+  @Expose private EvalNode end;
+
+  private Checker checker;
+
+  public BetweenPredicateEval(boolean not, boolean symmetric, EvalNode predicand, EvalNode begin, EvalNode end) {
+    super(EvalType.BETWEEN);
+    this.not = not;
+    this.symmetric = symmetric;
+    this.predicand = predicand;
+    this.begin = begin;
+    this.end = end;
+  }
+
+  private static interface Checker {
+    void eval(BetweenContext context, Schema schema, Tuple param);
+  }
+
+  private static class ConstantChecker implements Checker {
+    EvalNode predicand;
+    Datum begin;
+    Datum end;
+    private boolean not;
+
+    private ConstantChecker(boolean not, EvalNode predicand, Datum begin, Datum end) {
+      this.predicand = predicand;
+      this.not = not;
+      if (begin.compareTo(end) > 0) {
+        this.begin = end;
+        this.end = begin;
+      } else {
+        this.begin = begin;
+        this.end = end;
+      }
+    }
+
+    @Override
+    public void eval(BetweenContext context, Schema schema, Tuple param) {
+      predicand.eval(context.predicandContext, schema, param);
+      Datum predicandValue = predicand.terminate(context.predicandContext);
+
+      if (!(predicandValue instanceof NullDatum)) {
+        context.result =
+            DatumFactory.createBool(not ^ (predicandValue.greaterThanEqual(begin).asBool()
+                && predicandValue.lessThanEqual(end).asBool()));
+      } else {
+        context.result = NullDatum.get();
+      }
+    }
+  }
+
+  private static class AsymmetricChecker implements Checker {
+    EvalNode predicand;
+    EvalNode begin;
+    EvalNode end;
+    private boolean not;
+
+    private AsymmetricChecker(boolean not, EvalNode predicand, EvalNode begin, EvalNode end) {
+      this.not = not;
+      this.predicand = predicand;
+      this.begin = begin;
+      this.end = end;
+    }
+
+    @Override
+    public void eval(BetweenContext context, Schema schema, Tuple param) {
+      predicand.eval(context.predicandContext, schema, param);
+      Datum predicandValue = predicand.terminate(context.predicandContext);
+      begin.eval(context.beginContext, schema, param);
+      Datum beginValue = begin.terminate(context.beginContext);
+      end.eval(context.endContext, schema, param);
+      Datum endValue = begin.terminate(context.endContext);
+
+      if (!(predicandValue instanceof NullDatum || beginValue instanceof NullDatum || endValue instanceof NullDatum)) {
+        context.result =
+            DatumFactory.createBool(not ^ (predicandValue.greaterThanEqual(beginValue).asBool()
+                && predicandValue.lessThanEqual(endValue).asBool()));
+      } else {
+        context.result = NullDatum.get();
+      }
+    }
+  }
+
+  private static class SymmetricChecker implements Checker {
+    boolean not;
+    EvalNode predicand;
+    EvalNode begin;
+    EvalNode end;
+
+    SymmetricChecker(boolean not, EvalNode predicand, EvalNode begin, EvalNode end) {
+      this.not = not;
+      this.predicand = predicand;
+      this.begin = begin;
+      this.end = end;
+    }
+
+    @Override
+    public void eval(BetweenContext context, Schema schema, Tuple param) {
+      predicand.eval(context.predicandContext, schema, param);
+      Datum predicandValue = predicand.terminate(context.predicandContext);
+      begin.eval(context.beginContext, schema, param);
+      Datum beginValue = begin.terminate(context.beginContext);
+      end.eval(context.endContext, schema, param);
+      Datum endValue = begin.terminate(context.endContext);
+
+      if (!(predicandValue instanceof NullDatum || beginValue instanceof NullDatum || endValue instanceof NullDatum)) {
+        context.result = DatumFactory.createBool( not ^
+            (predicandValue.greaterThanEqual(beginValue).asBool() && predicandValue.lessThanEqual(endValue).asBool()) ||
+            (predicandValue.lessThanEqual(beginValue).asBool() && predicandValue.greaterThanEqual(endValue).asBool())
+        );
+      } else {
+        context.result = NullDatum.get();
+      }
+    }
+  }
+
+  @Override
+  public EvalContext newContext() {
+    return new BetweenContext();
+  }
+
+  @Override
+  public TajoDataTypes.DataType getValueType() {
+    return RES_TYPE;
+  }
+
+  @Override
+  public String getName() {
+    return "between";
+  }
+
+  @Override
+  public String toString() {
+    return predicand + " BETWEEN " + (symmetric ? "SYMMETRIC" : "ASYMMETRIC") + " " + begin + " AND " + end;
+  }
+
+  @Override
+  public void eval(EvalContext ctx, Schema schema, Tuple tuple) {
+    if (checker == null) {
+      if (begin.getType() == EvalType.CONST && end.getType() == EvalType.CONST) {
+        Datum beginValue = begin.terminate(null);
+        Datum endValue = end.terminate(null);
+
+        if (symmetric || beginValue.compareTo(endValue) <= 0) {
+          checker = new ConstantChecker(not, predicand, begin.terminate(null), end.terminate(null));
+        } else {
+          checker = new AsymmetricChecker(not, predicand, begin, end);
+        }
+      } else {
+        if (symmetric) {
+          checker = new SymmetricChecker(not, predicand, begin, end);
+        } else {
+          checker = new AsymmetricChecker(not, predicand, begin, end);
+        }
+      }
+    }
+
+    checker.eval((BetweenContext) ctx, schema, tuple);
+  }
+
+  @Override
+  public Datum terminate(EvalContext ctx) {
+    return ((BetweenContext)ctx).result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof BetweenPredicateEval) {
+      BetweenPredicateEval another = (BetweenPredicateEval) obj;
+      return not == another.not && symmetric == another.symmetric && predicand.equals(another.predicand) &&
+          begin.equals(another.begin) && end.equals(another.end);
+    }
+    return false;
+  }
+
+  private class BetweenContext implements EvalContext {
+    private EvalContext predicandContext;
+    private EvalContext beginContext;
+    private EvalContext endContext;
+    private Datum result;
+
+    BetweenContext() {
+      predicandContext = predicand.newContext();
+      beginContext = begin.newContext();
+      endContext = end.newContext();
+    }
+  }
+
+  @Deprecated
+  public void preOrder(EvalNodeVisitor visitor) {
+    visitor.visit(this);
+    predicand.preOrder(visitor);
+    begin.preOrder(visitor);
+    end.preOrder(visitor);
+  }
+
+  @Deprecated
+  public void postOrder(EvalNodeVisitor visitor) {
+    predicand.postOrder(visitor);
+    begin.postOrder(visitor);
+    end.postOrder(visitor);
+    visitor.visit(this);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
index 90a21c5..bb25909 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
@@ -145,12 +145,15 @@ public class BinaryEval extends EvalNode implements Cloneable {
     BinaryEvalCtx binCtx = (BinaryEvalCtx) ctx;
 
     switch(type) {
-      case AND:
-        return DatumFactory.createBool(leftExpr.terminate(binCtx.left).asBool()
-            && rightExpr.terminate(binCtx.right).asBool());
+      case AND: {
+        boolean left = leftExpr.terminate(binCtx.left).asBool();
+        boolean right = rightExpr.terminate(binCtx.right).asBool();
+        return DatumFactory.createBool(left && right);
+      }
       case OR:
-        return DatumFactory.createBool(leftExpr.terminate(binCtx.left).asBool()
-            || rightExpr.terminate(binCtx.right).asBool());
+        boolean left = leftExpr.terminate(binCtx.left).asBool();
+        boolean right = rightExpr.terminate(binCtx.right).asBool();
+        return DatumFactory.createBool(left || right);
 
       case EQUAL:
         return leftExpr.terminate(binCtx.left).equalsTo(rightExpr.terminate(binCtx.right));

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalType.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalType.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalType.java
index cd80b3a..c2dc2b8 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalType.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalType.java
@@ -49,6 +49,7 @@ public enum EvalType {
   CONCATENATE(BinaryEval.class, "||"),
 
   // Other predicates
+  BETWEEN(BetweenPredicateEval.class),
   CASE(CaseWhenEval.class),
   IF_THEN(CaseWhenEval.IfThenEval.class),
   IN(InEval.class),

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
index 0560e8b..79e2978 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
@@ -411,6 +411,15 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
   }
 
   @Override
+  public BetweenPredicate visitBetween_predicate(SQLParser.Between_predicateContext ctx) {
+    Expr predicand = visitRow_value_predicand(ctx.predicand);
+    Expr begin = visitRow_value_predicand(ctx.between_predicate_part_2().begin);
+    Expr end = visitRow_value_predicand(ctx.between_predicate_part_2().end);
+    return new BetweenPredicate(checkIfExist(ctx.between_predicate_part_2().NOT()),
+        checkIfExist(ctx.between_predicate_part_2().SYMMETRIC()), predicand, begin, end);
+  }
+
+  @Override
   public CaseWhenPredicate visitSimple_case(SQLParser.Simple_caseContext ctx) {
     Expr leftTerm = visitBoolean_value_expression(ctx.boolean_value_expression());
     CaseWhenPredicate caseWhen = new CaseWhenPredicate();

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
index 667344e..9f7fe25 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
@@ -949,6 +949,13 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
         NotExpr notExpr = (NotExpr) expr;
         return new NotEval(createEvalTree(plan, block, notExpr.getChild()));
 
+      case Between: {
+        BetweenPredicate between = (BetweenPredicate) expr;
+        BetweenPredicateEval betweenEval = new BetweenPredicateEval(between.isNot(), between.isSymmetric(),
+            createEvalTree(plan, block, between.predicand()), createEvalTree(plan, block, between.begin()),
+            createEvalTree(plan, block, between.end()));
+        return betweenEval;
+      }
       // pattern matching predicates
       case LikePredicate:
       case SimilarToPredicate:

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/eval/TestPredicates.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/eval/TestPredicates.java b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/eval/TestPredicates.java
index 6346844..d678b0b 100644
--- a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/eval/TestPredicates.java
+++ b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/eval/TestPredicates.java
@@ -47,4 +47,63 @@ public class TestPredicates extends ExprTestBase {
     testEval(schema2, "table1", "_123,",
         "select ltrim(col1, '_') is not null, upper(col2) is not null as a from table1", new String[]{"t", "f"});
   }
+
+  @Test
+  public void testBetween() {
+    Schema schema2 = new Schema();
+    schema2.addColumn("col1", TEXT);
+    schema2.addColumn("col2", TEXT);
+    schema2.addColumn("col3", TEXT);
+
+    // constant checker
+    testEval(schema2, "table1", "b,a,c", "select col1 between 'a' and 'c' from table1", new String[]{"t"});
+    testEval(schema2, "table1", "b,a,c", "select col1 between 'c' and 'a' from table1", new String[]{"f"});
+    testEval(schema2, "table1", "b,a,c", "select col1 between symmetric 'c' and 'a' from table1", new String[]{"t"});
+    testEval(schema2, "table1", "d,a,c", "select col1 between 'a' and 'c' from table1", new String[]{"f"});
+
+    // tests for inclusive
+    testEval(schema2, "table1", "a,a,c", "select col1 between col2 and col3 from table1", new String[]{"t"});
+    testEval(schema2, "table1", "b,a,c", "select col1 between col2 and col3 from table1", new String[]{"t"});
+    testEval(schema2, "table1", "c,a,c", "select col1 between col2 and col3 from table1", new String[]{"t"});
+    testEval(schema2, "table1", "d,a,c", "select col1 between col2 and col3 from table1", new String[]{"f"});
+
+    // tests for asymmetric and symmetric
+    testEval(schema2, "table1", "b,a,c", "select col1 between col3 and col2 from table1", new String[]{"f"});
+    testEval(schema2, "table1", "b,a,c", "select col1 between symmetric col3 and col2 from table1", new String[]{"t"});
+  }
+
+  @Test
+  public void testBetween2() { // for TAJO-249
+    Schema schema3 = new Schema();
+    schema3.addColumn("date_a", INT4);
+    schema3.addColumn("date_b", INT4);
+    schema3.addColumn("date_c", INT4);
+    schema3.addColumn("date_d", INT4);
+
+    String query = "select " +
+        "case " +
+        "when date_a BETWEEN 20130705 AND 20130715 AND ((date_b BETWEEN 20100101 AND 20120601) OR date_b > 20130715) " +
+        "AND (date_c < 20120601 OR date_c > 20130715) AND date_d > 20130715" +
+        "then 1 else 0 end from table1";
+
+    testEval(schema3, "table1", "20130715,20100102,20120525,20130716", query, new String [] {"1"});
+    testEval(schema3, "table1", "20130716,20100102,20120525,20130716", query, new String [] {"0"});
+
+    // date_b
+    testEval(schema3, "table1", "20130715,20100102,20120525,20130716", query, new String [] {"1"});
+    testEval(schema3, "table1", "20130715,20120602,20120525,20130716", query, new String [] {"0"});
+    testEval(schema3, "table1", "20130715,20091201,20120525,20130716", query, new String [] {"0"});
+    testEval(schema3, "table1", "20130715,20130716,20120525,20130716", query, new String [] {"1"});
+
+    // date_c
+    testEval(schema3, "table1", "20130715,20100102,20120525,20130716", query, new String [] {"1"});
+    testEval(schema3, "table1", "20130715,20100102,20120602,20130716", query, new String [] {"0"});
+
+    testEval(schema3, "table1", "20130715,20100102,20130716,20130716", query, new String [] {"1"});
+    testEval(schema3, "table1", "20130715,20100102,20130714,20130716", query, new String [] {"0"});
+
+    // date_d
+    testEval(schema3, "table1", "20130715,20100102,20120525,20130716", query, new String [] {"1"});
+    testEval(schema3, "table1", "20130715,20100102,20120525,20130705", query, new String [] {"0"});
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/95fa2667/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java
index 8c3c92b..894d9ea 100644
--- a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java
+++ b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java
@@ -340,7 +340,7 @@ public class TestSQLAnalyzer {
       "col is null", // 25
       "col is not null", // 26
       "col = null", // 27
-      "col != null" // 38
+      "col != null", // 38
   };
 
   public static Expr parseExpr(String sql) {
@@ -350,8 +350,8 @@ public class TestSQLAnalyzer {
     SQLParser parser = new SQLParser(tokens);
     parser.setBuildParseTree(true);
     SQLAnalyzer visitor = new SQLAnalyzer();
-    Boolean_value_expressionContext context = parser.boolean_value_expression();
-    return visitor.visitBoolean_value_expression(context);
+    SQLParser.Value_expressionContext context = parser.value_expression();
+    return visitor.visitValue_expression(context);
   }
 
   @Test