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/12/08 12:22:55 UTC

git commit: TAJO-344: Tajo cannot recognize negative numeric expressions. (hyunsik)

Updated Branches:
  refs/heads/master 8ce2a9402 -> 17d9cd735


TAJO-344: Tajo cannot recognize negative numeric expressions. (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/17d9cd73
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tajo/tree/17d9cd73
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tajo/diff/17d9cd73

Branch: refs/heads/master
Commit: 17d9cd7357d9e4e079a61acbaa755206c5f019db
Parents: 8ce2a94
Author: Hyunsik Choi <hy...@apache.org>
Authored: Sun Dec 8 20:22:29 2013 +0900
Committer: Hyunsik Choi <hy...@apache.org>
Committed: Sun Dec 8 20:22:29 2013 +0900

----------------------------------------------------------------------
 CHANGES.txt                                     |   2 +
 .../org/apache/tajo/algebra/LiteralValue.java   |  10 +-
 .../java/org/apache/tajo/algebra/OpType.java    |   1 +
 .../org/apache/tajo/algebra/SignedExpr.java     |  38 +++++++
 .../java/org/apache/tajo/datum/Float4Datum.java |   2 +-
 .../java/org/apache/tajo/datum/Float8Datum.java |   4 +-
 .../java/org/apache/tajo/datum/Int2Datum.java   |   6 +-
 .../java/org/apache/tajo/datum/Int4Datum.java   |   2 +-
 .../java/org/apache/tajo/datum/Int8Datum.java   |   2 +-
 .../org/apache/tajo/datum/NumericDatum.java     |  18 ++-
 .../org/apache/tajo/engine/parser/SQLParser.g4  |   6 +-
 .../org/apache/tajo/engine/eval/EvalNode.java   |   2 +-
 .../org/apache/tajo/engine/eval/EvalType.java   |   1 +
 .../org/apache/tajo/engine/eval/SignedEval.java | 114 +++++++++++++++++++
 .../apache/tajo/engine/function/math/Round.java |  12 +-
 .../tajo/engine/function/string/Substr.java     |  37 +++---
 .../apache/tajo/engine/parser/SQLAnalyzer.java  |  14 ++-
 .../tajo/engine/planner/LogicalPlanner.java     |   9 ++
 .../tajo/engine/function/TestMathFunctions.java |  49 +++-----
 .../TestStringOperatorsAndFunctions.java        |  40 +++----
 20 files changed, 271 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 71ba16d..0959159 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -109,6 +109,8 @@ Release 0.8.0 - unreleased
 
   BUG FIXES
 
+    TAJO-344: Tajo cannot recognize negative numeric expressions. (hyunsik)
+
     TAJO-393: Unit tests must use test-data directory. (hyunsik)
 
     TAJO-388: limit clause does not work properly. (hyunsik)

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java
index d713d00..4f106f2 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java
@@ -19,8 +19,8 @@
 package org.apache.tajo.algebra;
 
 public class LiteralValue extends Expr {
-  private String value;
   private LiteralType valueType;
+  private String value;
 
   public static enum LiteralType {
     Boolean,
@@ -36,14 +36,14 @@ public class LiteralValue extends Expr {
     this.valueType = valueType;
   }
 
-  public String getValue() {
-    return this.value;
-  }
-
   public LiteralType getValueType() {
     return this.valueType;
   }
 
+  public String getValue() {
+    return this.value;
+  }
+
   public boolean equalsTo(Expr expr) {
     LiteralValue another = (LiteralValue) expr;
     boolean a = valueType.equals(another.valueType);

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/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 3651156..3f8868b 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
@@ -84,6 +84,7 @@ public enum OpType {
   Modular(BinaryOperator.class),
 
   // other expressions
+  Sign(SignedExpr.class),
   Column(ColumnReferenceExpr.class),
   Target(Target.class),
   Function(FunctionExpr.class),

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java
new file mode 100644
index 0000000..15fcb51
--- /dev/null
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java
@@ -0,0 +1,38 @@
+/**
+ * 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 SignedExpr extends UnaryOperator {
+  private boolean negative;
+
+  public SignedExpr(boolean negative, Expr operand) {
+    super(OpType.Sign);
+    this.negative = negative;
+    setChild(operand);
+  }
+
+  public boolean isNegative() {
+    return negative;
+  }
+
+  @Override
+  boolean equalsTo(Expr expr) {
+    return negative == ((SignedExpr)expr).negative;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
index 990b6a4..87ff079 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
@@ -26,7 +26,7 @@ import org.apache.tajo.util.NumberUtil;
 
 import java.nio.ByteBuffer;
 
-public class Float4Datum extends Datum implements NumericDatum {
+public class Float4Datum extends NumericDatum {
   private static final int size = 4;
   @Expose float val;
 

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
index bb49e28..c6535c2 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
@@ -32,12 +32,10 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 
-public class Float8Datum extends Datum implements NumericDatum {
+public class Float8Datum extends NumericDatum {
   private static final int size = 8;
   @Expose private double val;
 
-  private static final Log LOG = LogFactory.getLog(Float8Datum.class);
-
 	public Float8Datum() {
 		super(TajoDataTypes.Type.FLOAT8);
 	}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
index bee8c47..1cb9d1d 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
@@ -19,8 +19,6 @@
 package org.apache.tajo.datum;
 
 import com.google.gson.annotations.Expose;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 import org.apache.tajo.common.TajoDataTypes;
 import org.apache.tajo.datum.exception.InvalidOperationException;
 import org.apache.tajo.util.NumberUtil;
@@ -28,12 +26,10 @@ import org.apache.tajo.util.NumberUtil;
 import java.nio.ByteBuffer;
 
 
-public class Int2Datum extends Datum implements NumericDatum {
+public class Int2Datum extends NumericDatum {
   private static final int size = 2;  
   @Expose private short val;
 
-  private static final Log LOG = LogFactory.getLog(Int2Datum.class);
-
   public Int2Datum() {
     super(TajoDataTypes.Type.INT2);
   }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
index ee66700..fa7f6c5 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
@@ -26,7 +26,7 @@ import org.apache.tajo.util.NumberUtil;
 import java.nio.ByteBuffer;
 
 
-public class Int4Datum extends Datum implements NumericDatum {
+public class Int4Datum extends NumericDatum {
   private static final int size = 4;
   @Expose private int val;
 	

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
index a1664c5..ffe9654 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
@@ -27,7 +27,7 @@ import org.apache.tajo.util.NumberUtil;
 import java.nio.ByteBuffer;
 
 
-public class Int8Datum extends Datum implements NumericDatum {
+public class Int8Datum extends NumericDatum {
   private static final int size = 8;
   @Expose private long val;
 

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-common/src/main/java/org/apache/tajo/datum/NumericDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/NumericDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/NumericDatum.java
index 745b720..b12ae91 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/NumericDatum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/NumericDatum.java
@@ -19,15 +19,21 @@
 package org.apache.tajo.datum;
 
 
-public interface NumericDatum {
+import org.apache.tajo.common.TajoDataTypes;
 
-  Datum plus(Datum datum);
+public abstract class NumericDatum extends Datum {
+
+  public NumericDatum(TajoDataTypes.Type type) {
+    super(type);
+  }
+
+  public abstract Datum plus(Datum datum);
   
-  Datum minus(Datum datum);
+  public abstract Datum minus(Datum datum);
   
-  Datum multiply(Datum datum);
+  public abstract Datum multiply(Datum datum);
   
-  Datum divide(Datum datum);
+  public abstract Datum divide(Datum datum);
   
-  void inverseSign();
+  public abstract void inverseSign();
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/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 b2c9717..c806846 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
@@ -441,7 +441,11 @@ numeric_value_expression
   ;
 
 term
-  : left=numeric_primary ((MULTIPLY|DIVIDE|MODULAR) right=numeric_primary)*
+  : left=factor ((MULTIPLY|DIVIDE|MODULAR) right=factor)*
+  ;
+
+factor
+  : (sign)? numeric_primary
   ;
 
 array

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalNode.java
index 45f3736..2e797fb 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/EvalNode.java
@@ -88,7 +88,7 @@ public abstract class EvalNode implements Cloneable, GsonObject {
 	
 	public void eval(EvalContext ctx, Schema schema, Tuple tuple) {}
 
-  public abstract Datum terminate(EvalContext ctx);
+  public abstract <T extends Datum> T terminate(EvalContext ctx);
 
   @Deprecated
 	public void preOrder(EvalNodeVisitor visitor) {

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/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 c2dc2b8..71db934 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
@@ -55,6 +55,7 @@ public enum EvalType {
   IN(InEval.class),
 
   // Value or Reference
+  SIGNED(SignedEval.class),
   CAST(CastEval.class),
   ROW_CONSTANT(RowConstantEval.class),
   FIELD(FieldEval.class),

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/SignedEval.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/SignedEval.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/SignedEval.java
new file mode 100644
index 0000000..b0ba813
--- /dev/null
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/eval/SignedEval.java
@@ -0,0 +1,114 @@
+/**
+ * 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.Schema;
+import org.apache.tajo.common.TajoDataTypes.DataType;
+import org.apache.tajo.datum.NumericDatum;
+import org.apache.tajo.storage.Tuple;
+
+public class SignedEval extends EvalNode implements Cloneable {
+  @Expose private EvalNode childEval;
+  @Expose private boolean negative;
+
+  public SignedEval(boolean negative, EvalNode childEval) {
+    super(EvalType.SIGNED);
+    this.negative = negative;
+    this.childEval = childEval;
+  }
+
+  @Override
+  public EvalContext newContext() {
+    SignedEvalCtx newCtx = new SignedEvalCtx();
+    newCtx.childExprCtx = childEval.newContext();
+    return newCtx;
+  }
+
+  public boolean isNegative() {
+    return negative;
+  }
+
+  public EvalNode getChild() {
+    return childEval;
+  }
+
+  @Override
+  public DataType getValueType() {
+    return childEval.getValueType();
+  }
+
+  @Override
+  public String getName() {
+    return "?";
+  }
+
+  @Override
+  public void eval(EvalContext ctx, Schema schema, Tuple tuple) {
+    childEval.eval(((SignedEvalCtx) ctx).childExprCtx, schema, tuple);
+  }
+
+  @Override
+  public NumericDatum terminate(EvalContext ctx) {
+    NumericDatum result = childEval.terminate(((SignedEvalCtx) ctx).childExprCtx);
+    if (negative) {
+      result.inverseSign();
+    }
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return (negative ? "-" : "+") + childEval.toString();
+  }
+
+  @Override
+  public void preOrder(EvalNodeVisitor visitor) {
+    visitor.visit(this);
+    childEval.preOrder(visitor);
+  }
+
+  @Override
+  public void postOrder(EvalNodeVisitor visitor) {    
+    childEval.postOrder(visitor);
+    visitor.visit(this);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof SignedEval) {
+      SignedEval other = (SignedEval) obj;
+      return negative == other.negative && this.childEval.equals(other.childEval);
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    SignedEval eval = (SignedEval) super.clone();
+    eval.negative = negative;
+    eval.childEval = (EvalNode) this.childEval.clone();
+    return eval;
+  }
+
+  private class SignedEvalCtx implements EvalContext {
+    EvalContext childExprCtx;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/math/Round.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/math/Round.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/math/Round.java
index f95e330..e033df0 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/math/Round.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/math/Round.java
@@ -45,6 +45,16 @@ public class Round extends GeneralFunction {
       return NullDatum.get();
     }
 
-    return DatumFactory.createInt8(Math.round(valueDatum.asFloat8()));
+    double value = valueDatum.asFloat8();
+
+    // Note: there are various round up/down approaches (https://en.wikipedia.org/wiki/Rounding#Tie-breaking).
+    //       Math.round uses an approach different from other programming languages, so the results of round function
+    //       can be different from other DBMSs. For example, Math.round(-5.5) returns -5. In contrast,
+    //       round function in MySQL and PostgreSQL returns -6. The below code is a workaround code for this.
+    if (value < 0) {
+      return DatumFactory.createInt8((long) Math.ceil(value - 0.5d));
+    } else {
+      return DatumFactory.createInt8((long) Math.floor(value + 0.5d));
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/string/Substr.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/string/Substr.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/string/Substr.java
index cb42e3c..9652cdc 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/string/Substr.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/function/string/Substr.java
@@ -36,40 +36,45 @@ public class Substr extends GeneralFunction {
     super(new Column[] {
         new Column("text", TajoDataTypes.Type.TEXT),
         new Column("from", TajoDataTypes.Type.INT4),
-        new Column("length", TajoDataTypes.Type.INT4)    //optional
+        new Column("count", TajoDataTypes.Type.INT4)    //optional
     });
   }
 
   @Override
   public Datum eval(Tuple params) {
     Datum valueDatum = params.get(0);
-    if(valueDatum instanceof NullDatum) {
+    Datum fromDatum = params.get(1);
+    Datum countDatum = params.size() > 2 ? params.get(2) : null;
+
+    if(valueDatum instanceof NullDatum || fromDatum instanceof NullDatum || countDatum instanceof NullDatum) {
       return NullDatum.get();
     }
-    Datum fromDatum = params.get(1);
-    Datum lengthDatum = params.size() > 2 ? params.get(2) : null;
 
     String value = valueDatum.asChars();
-    int valueLength = value.length();
+    int from = fromDatum.asInt4();
+    int strLength = value.length();
+    int count;
 
-    int from = fromDatum.asInt4() - 1;
-    if (from >= valueLength) {
-      return DatumFactory.createText("");
+    if (countDatum == null) {
+      count = strLength;
+    } else {
+      count = (countDatum.asInt4() + from) - 1;
     }
 
-    int length = (lengthDatum == null) ? valueLength : lengthDatum.asInt4();
+    if (count > strLength) {
+      count = strLength;
+    }
 
-    if (from < 0) {
+    if (from < 1) {
       from = 0;
-      length = (lengthDatum == null) ? value.length() : length - 1;
+    } else {
+      from --;
     }
 
-    int to = from + length;
-
-    if (to > valueLength) {
-      to = valueLength;
+    if (from >= count) {
+      return DatumFactory.createText("");
     }
 
-    return DatumFactory.createText(value.substring(from, to));
+    return DatumFactory.createText(value.substring(from, count));
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/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 737f774..d8f259e 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
@@ -592,14 +592,14 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
 
   @Override
   public Expr visitTerm(SQLParser.TermContext ctx) {
-    Expr current = visitNumeric_primary(ctx.numeric_primary(0));
+    Expr current = visitFactor(ctx.factor(0));
 
     Expr left;
     Expr right;
     for (int i = 1; i < ctx.getChildCount(); i++) {
       left = current;
       TerminalNode operator = (TerminalNode) ctx.getChild(i++);
-      right = visitNumeric_primary((Numeric_primaryContext) ctx.getChild(i));
+      right = visitFactor((FactorContext) ctx.getChild(i));
 
       if (operator.getSymbol().getType() == MULTIPLY) {
         current = new BinaryOperator(OpType.Multiply, left, right);
@@ -613,6 +613,14 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
     return current;
   }
 
+  @Override public Expr visitFactor(SQLParser.FactorContext ctx) {
+    Expr current = visitNumeric_primary(ctx.numeric_primary());
+    if (checkIfExist(ctx.sign()) && checkIfExist(ctx.sign().MINUS())) {
+      current = new SignedExpr(true, current);
+    }
+    return current;
+  }
+
   @Override
   public Expr visitNumeric_primary(SQLParser.Numeric_primaryContext ctx) {
     if (ctx.value_expression_primary() != null) {
@@ -761,7 +769,7 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
 
   @Override public FunctionExpr visitGeneral_set_function(SQLParser.General_set_functionContext ctx) {
     String signature = ctx.set_function_type().getText();
-    boolean distinct = checkIfExist(ctx.set_qualifier()) && checkIfExist(ctx.set_qualifier().DISTINCT()) ? true : false;
+    boolean distinct = checkIfExist(ctx.set_qualifier()) && checkIfExist(ctx.set_qualifier().DISTINCT());
     Expr param = visitValue_expression(ctx.value_expression());
 
     return new GeneralSetFunctionExpr(signature, distinct, param);

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/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 76b9239..5e48f4a 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
@@ -1065,6 +1065,15 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
             throw new RuntimeException("Unsupported type: " + literal.getValueType());
         }
 
+      case Sign:
+        SignedExpr signedExpr = (SignedExpr) expr;
+        EvalNode numericExpr = createEvalTree(plan, block, signedExpr.getChild());
+        if (signedExpr.isNegative()) {
+          return new SignedEval(signedExpr.isNegative(), numericExpr);
+        } else {
+          return numericExpr;
+        }
+
       case Cast:
         CastExpr cast = (CastExpr) expr;
         return new CastEval(createEvalTree(plan, block, cast.getOperand()),

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestMathFunctions.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestMathFunctions.java b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestMathFunctions.java
index fa9e40d..9004415 100644
--- a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestMathFunctions.java
+++ b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestMathFunctions.java
@@ -34,9 +34,9 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select round(5.5) as col1 ", new String[]{"6"});
     testSimpleEval("select round(5.6) as col1 ", new String[]{"6"});
 
-//    testSimpleEval("select round(-5.1) as col1 ", new String[]{"-5"});
-//    testSimpleEval("select round(-5.5) as col1 ", new String[]{"-6"});
-//    testSimpleEval("select round(-5.6) as col1 ", new String[]{"-6"});
+    testSimpleEval("select round(-5.1) as col1 ", new String[]{"-5"});
+    testSimpleEval("select round(-5.5) as col1 ", new String[]{"-6"});
+    testSimpleEval("select round(-5.6) as col1 ", new String[]{"-6"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -52,8 +52,9 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select floor(5.1) as col1 ", new String[]{"5"});
     testSimpleEval("select floor(5.5) as col1 ", new String[]{"5"});
     testSimpleEval("select floor(5.6) as col1 ", new String[]{"5"});
-//    testSimpleEval("select floor(-5.1) as col1 ", new String[]{"-6"});
-//    testSimpleEval("select floor(-5.6) as col1 ", new String[]{"-6"});
+
+    testSimpleEval("select floor(-5.1) as col1 ", new String[]{"-6"});
+    testSimpleEval("select floor(-5.6) as col1 ", new String[]{"-6"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -70,8 +71,9 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select ceil(5.1) as col1 ", new String[]{"6"});
     testSimpleEval("select ceil(5.5) as col1 ", new String[]{"6"});
     testSimpleEval("select ceil(5.6) as col1 ", new String[]{"6"});
-//    testSimpleEval("select ceil(-5.1) as col1 ", new String[]{"-5"});
-//    testSimpleEval("select ceil(-5.6) as col1 ", new String[]{"-5"});
+
+    testSimpleEval("select ceil(-5.1) as col1 ", new String[]{"-5"});
+    testSimpleEval("select ceil(-5.6) as col1 ", new String[]{"-5"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -87,7 +89,7 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select sin(0.0) as col1 ", new String[]{"0.0"});
     testSimpleEval("select sin(0.7) as col1 ", new String[]{"0.644217687237691"});
     testSimpleEval("select sin(1.2) as col1 ", new String[]{"0.9320390859672263"});
-//    testSimpleEval("select sin(-0.5) as col1 ", new String[]{"-0.479425538604203"});
+    testSimpleEval("select sin(-0.5) as col1 ", new String[]{"-0.479425538604203"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -104,7 +106,7 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select cos(0.0) as col1 ", new String[]{"1.0"});
     testSimpleEval("select cos(0.7) as col1 ", new String[]{"0.7648421872844885"});
     testSimpleEval("select cos(1.2) as col1 ", new String[]{"0.3623577544766736"});
-//    testSimpleEval("select cos(-0.5) as col1 ", new String[]{"0.8775825618903728"});
+    testSimpleEval("select cos(-0.5) as col1 ", new String[]{"0.8775825618903728"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -120,7 +122,7 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select tan(0.0) as col1 ", new String[]{"0.0"});
     testSimpleEval("select tan(0.3) as col1 ", new String[]{"0.30933624960962325"});
     testSimpleEval("select tan(0.8) as col1 ", new String[]{"1.0296385570503641"});
-//    testSimpleEval("select tan(-0.5) as col1 ", new String[]{"-0.5463024898437905"});
+    testSimpleEval("select tan(-0.5) as col1 ", new String[]{"-0.5463024898437905"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -136,7 +138,7 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select asin(0.0) as col1 ", new String[]{"0.0"});
     testSimpleEval("select asin(0.3) as col1 ", new String[]{"0.3046926540153975"});
     testSimpleEval("select asin(0.8) as col1 ", new String[]{"0.9272952180016123"});
-//    testSimpleEval("select asin(-0.5) as col1 ", new String[]{"-0.5235987755982989"});
+    testSimpleEval("select asin(-0.5) as col1 ", new String[]{"-0.5235987755982989"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -152,7 +154,7 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select acos(0.0) as col1 ", new String[]{"1.5707963267948966"});
     testSimpleEval("select acos(0.3) as col1 ", new String[]{"1.2661036727794992"});
     testSimpleEval("select acos(0.8) as col1 ", new String[]{"0.6435011087932843"});
-//    testSimpleEval("select acos(-0.5) as col1 ", new String[]{"2.0943951023931957"});
+    testSimpleEval("select acos(-0.5) as col1 ", new String[]{"2.0943951023931957"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -168,7 +170,7 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select atan(0.0) as col1 ", new String[]{"0.0"});
     testSimpleEval("select atan(0.8) as col1 ", new String[]{"0.6747409422235527"});
     testSimpleEval("select atan(1.2) as col1 ", new String[]{"0.8760580505981934"});
-//    testSimpleEval("select atan(-0.5) as col1 ", new String[]{"-0.4636476090008061"});
+    testSimpleEval("select atan(-0.5) as col1 ", new String[]{"-0.4636476090008061"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -184,8 +186,8 @@ public class TestMathFunctions extends ExprTestBase {
     testSimpleEval("select atan2(0.8, 0.0) as col1 ", new String[]{"1.5707963267948966"});
     testSimpleEval("select atan2(0.8, 1.1) as col1 ", new String[]{"0.628796286415433"});
     testSimpleEval("select atan2(2.7, 0.3) as col1 ", new String[]{"1.460139105621001"});
-//    testSimpleEval("select atan(-0.5, 0.3) as col1 ", new String[]{"-1.0303768265243125"});
-//    testSimpleEval("select atan(-0.2, -1.3) as col1 ", new String[]{"-2.988943325194528"});
+    testSimpleEval("select atan2(-0.5, 0.3) as col1 ", new String[]{"-1.0303768265243125"});
+    testSimpleEval("select atan2(-0.2, -1.3) as col1 ", new String[]{"-2.988943325194528"});
 
     Schema schema = new Schema();
     schema.addColumn("col1", FLOAT8);
@@ -227,21 +229,4 @@ public class TestMathFunctions extends ExprTestBase {
     testEval(schema, "table1", "9,2,3", "select div(col1 + col2, col3) from table1", 
         new String[]{"3"});
   }
-/*
-  @Test
-  public void testDiv() throws IOException {
-    testSimpleEval("select mod(9,4) as col1 ", new String[]{"1"});
-    testSimpleEval("select mod(200000000001,200000000000) as col1 ", new String[]{"1"});
-    testSimpleEval("select mod(200000000000,2) as col1 ", new String[]{"0"});
-    testSimpleEval("select mod(2,200000000000) as col1 ", new String[]{"2"});
-
-    Schema schema = new Schema();
-    schema.addColumn("col1", INT8);
-    schema.addColumn("col2", INT8);
-    schema.addColumn("col3", INT8);
-
-    testEval(schema, "table1", "9,2,3", "select mod(col1 + col2, col3) from table1", 
-        new String[]{"2"});
-  }
-*/
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/17d9cd73/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestStringOperatorsAndFunctions.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestStringOperatorsAndFunctions.java b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestStringOperatorsAndFunctions.java
index bbe965f..e37eca5 100644
--- a/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestStringOperatorsAndFunctions.java
+++ b/tajo-core/tajo-core-backend/src/test/java/org/apache/tajo/engine/function/TestStringOperatorsAndFunctions.java
@@ -158,12 +158,13 @@ public class TestStringOperatorsAndFunctions extends ExprTestBase {
     testSimpleEval("select left('abcdef',5) as col1 ", new String[]{"abcde"});
     testSimpleEval("select left('abcdef',6) as col1 ", new String[]{"abcdef"});
     testSimpleEval("select left('abcdef',7) as col1 ", new String[]{"abcdef"});
-//    testSimpleEval("select from_left('abcdef',-1) as col1 ", new String[]{"abcde"});
-//    testSimpleEval("select from_left('abcdef',-2) as col1 ", new String[]{"abcd"});
-//    testSimpleEval("select from_left('abcdef',-3) as col1 ", new String[]{"abc"});
-//    testSimpleEval("select from_left('abcdef',-4) as col1 ", new String[]{"ab"});
-//    testSimpleEval("select from_left('abcdef',-5) as col1 ", new String[]{"a"});
-//    testSimpleEval("select from_left('abcdef',-6) as col1 ", new String[]{""});
+
+    testSimpleEval("select left('abcdef',-1) as col1 ", new String[]{"abcde"});
+    testSimpleEval("select left('abcdef',-2) as col1 ", new String[]{"abcd"});
+    testSimpleEval("select left('abcdef',-3) as col1 ", new String[]{"abc"});
+    testSimpleEval("select left('abcdef',-4) as col1 ", new String[]{"ab"});
+    testSimpleEval("select left('abcdef',-5) as col1 ", new String[]{"a"});
+    testSimpleEval("select left('abcdef',-6) as col1 ", new String[]{""});
 
     Schema schema = new Schema();
     schema.addColumn("col1", TEXT);
@@ -186,12 +187,13 @@ public class TestStringOperatorsAndFunctions extends ExprTestBase {
     testSimpleEval("select right('abcdef',5) as col1 ", new String[]{"bcdef"});
     testSimpleEval("select right('abcdef',6) as col1 ", new String[]{"abcdef"});
     testSimpleEval("select right('abcdef',7) as col1 ", new String[]{"abcdef"});
-//    testSimpleEval("select from_right('abcdef',-1) as col1 ", new String[]{"bcdef"});
-//    testSimpleEval("select from_right('abcdef',-2) as col1 ", new String[]{"cdef"});
-//    testSimpleEval("select from_right('abcdef',-3) as col1 ", new String[]{"def"});
-//    testSimpleEval("select from_right('abcdef',-4) as col1 ", new String[]{"ef"});
-//    testSimpleEval("select from_right('abcdef',-5) as col1 ", new String[]{"f"});
-//    testSimpleEval("select from_right('abcdef',-6) as col1 ", new String[]{""});
+
+    testSimpleEval("select right('abcdef',-1) as col1 ", new String[]{"bcdef"});
+    testSimpleEval("select right('abcdef',-2) as col1 ", new String[]{"cdef"});
+    testSimpleEval("select right('abcdef',-3) as col1 ", new String[]{"def"});
+    testSimpleEval("select right('abcdef',-4) as col1 ", new String[]{"ef"});
+    testSimpleEval("select right('abcdef',-5) as col1 ", new String[]{"f"});
+    testSimpleEval("select right('abcdef',-6) as col1 ", new String[]{""});
 
     Schema schema = new Schema();
     schema.addColumn("col1", TEXT);
@@ -335,16 +337,10 @@ public class TestStringOperatorsAndFunctions extends ExprTestBase {
     testSimpleEval("select substr('일이삼사오', 2, 2) as col1 ", new String[]{"이삼"});
     testSimpleEval("select substr('일이삼사오', 3) as col1 ", new String[]{"삼사오"});
 
-    //TODO If there is a minus value in function argument, next error occurred.
-    //org.apache.tajo.engine.parser.SQLSyntaxError: ERROR: syntax error at or near 'substr'
-    //LINE 1:7 select substr('abcdef', -1, 100) as col1
-    //               ^^^^^^
-    //at org.apache.tajo.engine.parser.SQLAnalyzer.parse(SQLAnalyzer.java:64)
-
-//    testSimpleEval("select substr('abcdef', -1) as col1 ", new String[]{"abcdef"});
-//    testSimpleEval("select substr('abcdef', -1, 100) as col1 ", new String[]{"abcdef"});
-//    testSimpleEval("select substr('abcdef', -1, 3) as col1 ", new String[]{"a"});
-//    testSimpleEval("select substr('abcdef', -1, 1) as col1 ", new String[]{""});
+    testSimpleEval("select substr('abcdef', -1) as col1 ", new String[]{"abcdef"});
+    testSimpleEval("select substr('abcdef', -1, 100) as col1 ", new String[]{"abcdef"});
+    testSimpleEval("select substr('abcdef', -1, 3) as col1 ", new String[]{"a"});
+    testSimpleEval("select substr('abcdef', -1, 1) as col1 ", new String[]{""});
 
     Schema schema = new Schema();
     schema.addColumn("col1", TEXT);