You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by ta...@apache.org on 2018/09/27 20:43:17 UTC

[7/7] impala git commit: IMPALA-6202. mod and % should be equivalent.

IMPALA-6202. mod and % should be equivalent.

Currently in DECIMAL V2 mode, typeof(9.9 % 3) is DECIMAL(2,1) and typeof(mod(9.9, 3)) is
DECIMAL(4,1), while both are expected to be DECIMAL(2,1). This jira fixes V2 mode by
replacing "mod" with "%" at parser stage thus they share the same code path afterwards.

Testing:
Added unit tests and done real cluster testing.

Change-Id: Ib0067da04083859ffbf662a29007154461bea2ba
Reviewed-on: http://gerrit.cloudera.org:8080/11443
Tested-by: Impala Public Jenkins <im...@cloudera.com>
Reviewed-by: Tim Armstrong <ta...@cloudera.com>


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

Branch: refs/heads/master
Commit: ba27b038148f0694662c14710f18ee6e94cf82b7
Parents: 3f78b74
Author: Yongjun Zhang <yz...@cloudera.com>
Authored: Thu Sep 20 12:25:06 2018 -0700
Committer: Tim Armstrong <ta...@cloudera.com>
Committed: Thu Sep 27 20:41:29 2018 +0000

----------------------------------------------------------------------
 fe/src/main/cup/sql-parser.cup                  | 17 +++++-
 .../impala/analysis/FunctionCallExpr.java       | 14 ++++-
 .../java/org/apache/impala/catalog/Type.java    |  2 +
 .../java/org/apache/impala/catalog/View.java    |  2 +
 .../org/apache/impala/service/Frontend.java     | 11 +++-
 .../impala/analysis/AnalyzeExprsTest.java       | 57 ++++++++++++++------
 .../org/apache/impala/analysis/ParserTest.java  |  2 +
 .../apache/impala/common/FrontendTestBase.java  |  3 +-
 .../org/apache/impala/service/JdbcTest.java     |  2 +
 .../PlannerTest/parquet-filtering-disabled.test |  6 +--
 .../queries/PlannerTest/parquet-filtering.test  | 16 +++---
 .../queries/QueryTest/decimal-exprs.test        | 28 ++++++++++
 12 files changed, 127 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/main/cup/sql-parser.cup
----------------------------------------------------------------------
diff --git a/fe/src/main/cup/sql-parser.cup b/fe/src/main/cup/sql-parser.cup
index 4b6006a..ebe653a 100644
--- a/fe/src/main/cup/sql-parser.cup
+++ b/fe/src/main/cup/sql-parser.cup
@@ -52,6 +52,7 @@ import org.apache.impala.thrift.TFunctionCategory;
 import org.apache.impala.thrift.THdfsFileFormat;
 import org.apache.impala.thrift.TOwnerType;
 import org.apache.impala.thrift.TPrivilegeLevel;
+import org.apache.impala.thrift.TQueryOptions;
 import org.apache.impala.thrift.TShowStatsOp;
 import org.apache.impala.thrift.TTablePropertyType;
 import org.apache.impala.thrift.TPrincipalType;
@@ -68,6 +69,10 @@ parser code {:
   // for generating syntax error message
   private final List<Integer> expectedTokenIds_ = new ArrayList<Integer>();
 
+  // Currently used to tell if it's decimal V1 or V2 mode.
+  // TODO: remove when V1 code is dropped.
+  private TQueryOptions queryOptions;
+
   // to avoid reporting trivial tokens as expected tokens in error messages
   private boolean reportExpectedToken(Integer tokenId, int numExpectedTokens) {
     if (SqlScanner.isKeyword(tokenId) ||
@@ -96,6 +101,14 @@ parser code {:
     return msg;
   }
 
+  public void setQueryOptions(TQueryOptions options) {
+    queryOptions = options;
+  }
+
+  public TQueryOptions getQueryOptions() {
+    return queryOptions;
+  }
+
   // override to save error token
   public void syntax_error(java_cup.runtime.Symbol token) {
     errorToken_ = token;
@@ -2972,10 +2985,10 @@ function_call_expr ::=
   function_name:fn_name LPAREN RPAREN
   {:
     RESULT = FunctionCallExpr.createExpr(
-        fn_name, new FunctionParams(new ArrayList<Expr>()));
+        fn_name, new FunctionParams(new ArrayList<Expr>()), parser.getQueryOptions());
   :}
   | function_name:fn_name LPAREN function_params:params RPAREN
-  {: RESULT = FunctionCallExpr.createExpr(fn_name, params); :}
+  {: RESULT = FunctionCallExpr.createExpr(fn_name, params, parser.getQueryOptions()); :}
   // Below is a special case for EXTRACT. Idents are used to avoid adding new keywords.
   | function_name:fn_name LPAREN ident_or_default:u KW_FROM expr:t RPAREN
   {:  RESULT = new ExtractFromExpr(fn_name, u, t); :}

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/main/java/org/apache/impala/analysis/FunctionCallExpr.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/FunctionCallExpr.java b/fe/src/main/java/org/apache/impala/analysis/FunctionCallExpr.java
index 291420f..36ef596 100644
--- a/fe/src/main/java/org/apache/impala/analysis/FunctionCallExpr.java
+++ b/fe/src/main/java/org/apache/impala/analysis/FunctionCallExpr.java
@@ -34,6 +34,7 @@ import org.apache.impala.thrift.TColumnType;
 import org.apache.impala.thrift.TExprNode;
 import org.apache.impala.thrift.TExprNodeType;
 import org.apache.impala.thrift.TFunctionBinaryType;
+import org.apache.impala.thrift.TQueryOptions;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
@@ -80,9 +81,10 @@ public class FunctionCallExpr extends Expr {
 
   /**
    * Returns an Expr that evaluates the function call <fnName>(<params>). The returned
-   * Expr is not necessarily a FunctionCallExpr (example: DECODE())
+   * Expr is not necessarily a FunctionCallExpr (example: DECODE()).
    */
-  public static Expr createExpr(FunctionName fnName, FunctionParams params) {
+  public static Expr createExpr(FunctionName fnName, FunctionParams params,
+      TQueryOptions options) {
     FunctionCallExpr functionCallExpr = new FunctionCallExpr(fnName, params);
     if (functionNameEqualsBuiltin(fnName, "decode")) {
       return new CaseExpr(functionCallExpr);
@@ -103,6 +105,14 @@ public class FunctionCallExpr extends Expr {
           new NullLiteral() // NULL
       ));
     }
+    // "mod" and "%" are equivalent in DECIMAL V2 mode.
+    Preconditions.checkArgument(options != null);
+    if (options.isDecimal_v2() && functionNameEqualsBuiltin(fnName, "mod")) {
+      List<Expr> plist = Lists.newArrayList(params.exprs());
+      Preconditions.checkArgument(plist.size() == 2);
+      return new ArithmeticExpr(ArithmeticExpr.Operator.MOD,
+          plist.get(0), plist.get(1));
+    }
     return functionCallExpr;
   }
 

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/main/java/org/apache/impala/catalog/Type.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/catalog/Type.java b/fe/src/main/java/org/apache/impala/catalog/Type.java
index a85cd40..664fca1 100644
--- a/fe/src/main/java/org/apache/impala/catalog/Type.java
+++ b/fe/src/main/java/org/apache/impala/catalog/Type.java
@@ -28,6 +28,7 @@ import org.apache.impala.analysis.TypeDef;
 import org.apache.impala.common.Pair;
 import org.apache.impala.thrift.TColumnType;
 import org.apache.impala.thrift.TPrimitiveType;
+import org.apache.impala.thrift.TQueryOptions;
 import org.apache.impala.thrift.TScalarType;
 import org.apache.impala.thrift.TStructField;
 import org.apache.impala.thrift.TTypeNode;
@@ -264,6 +265,7 @@ public abstract class Type {
     String stmt = String.format("CREATE TABLE $DUMMY ($DUMMY %s)", typeStr);
     SqlScanner input = new SqlScanner(new StringReader(stmt));
     SqlParser parser = new SqlParser(input);
+    parser.setQueryOptions(new TQueryOptions());
     CreateTableStmt createTableStmt;
     try {
       Object o = parser.parse().value;

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/main/java/org/apache/impala/catalog/View.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/catalog/View.java b/fe/src/main/java/org/apache/impala/catalog/View.java
index 8af5300..776c385 100644
--- a/fe/src/main/java/org/apache/impala/catalog/View.java
+++ b/fe/src/main/java/org/apache/impala/catalog/View.java
@@ -29,6 +29,7 @@ import org.apache.impala.analysis.QueryStmt;
 import org.apache.impala.analysis.SqlParser;
 import org.apache.impala.analysis.SqlScanner;
 import org.apache.impala.thrift.TCatalogObjectType;
+import org.apache.impala.thrift.TQueryOptions;
 import org.apache.impala.thrift.TTable;
 import org.apache.impala.thrift.TTableDescriptor;
 import org.apache.impala.thrift.TTableStats;
@@ -138,6 +139,7 @@ public class View extends Table implements FeView {
     // populate a view definition.
     SqlScanner input = new SqlScanner(new StringReader(inlineViewDef));
     SqlParser parser = new SqlParser(input);
+    parser.setQueryOptions(new TQueryOptions());
     ParseNode node = null;
     try {
       node = (ParseNode) parser.parse().value;

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/main/java/org/apache/impala/service/Frontend.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/service/Frontend.java b/fe/src/main/java/org/apache/impala/service/Frontend.java
index 2c527cc..847e8b1 100644
--- a/fe/src/main/java/org/apache/impala/service/Frontend.java
+++ b/fe/src/main/java/org/apache/impala/service/Frontend.java
@@ -1033,9 +1033,17 @@ public class Frontend {
     return result;
   }
 
+  @VisibleForTesting
   public StatementBase parse(String stmt) throws AnalysisException {
+    return parse(stmt, new TQueryOptions());
+  }
+
+  public StatementBase parse(String stmt, TQueryOptions options)
+      throws AnalysisException {
     SqlScanner input = new SqlScanner(new StringReader(stmt));
     SqlParser parser = new SqlParser(input);
+    Preconditions.checkArgument(options != null);
+    parser.setQueryOptions(options);
     try {
       return (StatementBase) parser.parse().value;
     } catch (Exception e) {
@@ -1090,7 +1098,8 @@ public class Frontend {
   private TExecRequest doCreateExecRequest(TQueryCtx queryCtx, EventSequence timeline,
       StringBuilder explainString) throws ImpalaException {
     // Parse stmt and collect/load metadata to populate a stmt-local table cache
-    StatementBase stmt = parse(queryCtx.client_request.stmt);
+    StatementBase stmt =
+        parse(queryCtx.client_request.stmt, queryCtx.client_request.query_options);
     StmtMetadataLoader metadataLoader =
         new StmtMetadataLoader(this, queryCtx.session.database, timeline);
     StmtTableCache stmtTableCache = metadataLoader.loadTables(stmt);

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java b/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java
index 905fae6..1908c21 100644
--- a/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java
+++ b/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java
@@ -2319,30 +2319,38 @@ public class AnalyzeExprsTest extends AnalyzerTest {
     testFuncExprDepthLimit("cast(", "1", " as int)");
   }
 
-  // Verifies the resulting expr decimal type is expectedType under decimal v1 and
+  // Verifies the resulting expr decimal type is expectedType under decimal v1 or
   // decimal v2.
-  private void testDecimalExpr(String expr,
-      Type decimalV1ExpectedType, Type decimalV2ExpectedType) {
+  private void testDecimalExpr(String expr, Type decimalExpectedType, boolean isV2) {
     TQueryOptions queryOpts = new TQueryOptions();
 
-    queryOpts.setDecimal_v2(false);
+    queryOpts.setDecimal_v2(isV2);
     SelectStmt selectStmt =
         (SelectStmt) AnalyzesOk("select " + expr, createAnalysisCtx(queryOpts));
     Expr root = selectStmt.resultExprs_.get(0);
     Type actualType = root.getType();
     Assert.assertTrue(
-        "Expr: " + expr + " Decimal Version: 1" +
-        " Expected: " + decimalV1ExpectedType + " Actual: " + actualType,
-        decimalV1ExpectedType.equals(actualType));
-
-    queryOpts.setDecimal_v2(true);
-    selectStmt = (SelectStmt) AnalyzesOk("select " + expr, createAnalysisCtx(queryOpts));
-    root = selectStmt.resultExprs_.get(0);
-    actualType = root.getType();
-    Assert.assertTrue(
-        "Expr: " + expr + " Decimal Version: 2" +
-            " Expected: " + decimalV2ExpectedType + " Actual: " + actualType,
-        decimalV2ExpectedType.equals(actualType));
+        "Expr: " + expr + " Decimal Version: " + (isV2? "2" : "1") +
+        " Expected: " + decimalExpectedType + " Actual: " + actualType,
+        decimalExpectedType.equals(actualType));
+  }
+
+  // Verifies the resulting expr decimal type is expectedType under decimal v1.
+  private void testDecimalExprV1(String expr, Type decimalExpectedType) {
+    testDecimalExpr(expr, decimalExpectedType, false);
+  }
+
+  // Verifies the resulting expr decimal type is expectedType under decimal v2.
+  private void testDecimalExprV2(String expr, Type decimalExpectedType) {
+    testDecimalExpr(expr, decimalExpectedType, true);
+  }
+
+  // Verifies the resulting expr decimal type is expectedType under decimal v1 and
+  // decimal v2.
+  private void testDecimalExpr(String expr,
+      Type decimalV1ExpectedType, Type decimalV2ExpectedType) {
+    testDecimalExprV1(expr, decimalV1ExpectedType);
+    testDecimalExprV2(expr, decimalV2ExpectedType);
   }
 
   // Verifies the resulting expr decimal type is exptectedType
@@ -2350,6 +2358,23 @@ public class AnalyzeExprsTest extends AnalyzerTest {
     testDecimalExpr(expr, expectedType, expectedType);
   }
 
+  // Verify that mod and % returns the same type when it's DECIMAL V2 mdoe.
+  // See IMPALA-6202.
+  @Test
+  public void TestModReturnType() {
+    testDecimalExprV2("mod(9.8, 3)", ScalarType.createDecimalType(2,1));
+    testDecimalExprV2("9.7 % 3", ScalarType.createDecimalType(2,1));
+
+    testDecimalExprV2("mod(109.8, 3)", ScalarType.createDecimalType(4,1));
+    testDecimalExprV2("109.7 % 3", ScalarType.createDecimalType(4,1));
+
+    testDecimalExprV2("mod(109.8, 3.45)", ScalarType.createDecimalType(3,2));
+    testDecimalExprV2("109.7 % 3.45", ScalarType.createDecimalType(3,2));
+
+    testDecimalExprV1("mod(9.6, 3)", ScalarType.createDecimalType(4,1));
+    testDecimalExprV1("9.5 % 3", Type.DOUBLE);
+ }
+
   @Test
   public void TestDecimalArithmetic() {
     String decimal_10_0 = "cast(1 as decimal(10,0))";

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/test/java/org/apache/impala/analysis/ParserTest.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/org/apache/impala/analysis/ParserTest.java b/fe/src/test/java/org/apache/impala/analysis/ParserTest.java
index 5ad2876..f7d6bda 100644
--- a/fe/src/test/java/org/apache/impala/analysis/ParserTest.java
+++ b/fe/src/test/java/org/apache/impala/analysis/ParserTest.java
@@ -30,6 +30,7 @@ import org.apache.impala.analysis.TimestampArithmeticExpr.TimeUnit;
 import org.apache.impala.common.AnalysisException;
 import org.apache.impala.common.FrontendTestBase;
 import org.apache.impala.compat.MetastoreShim;
+import org.apache.impala.thrift.TQueryOptions;
 import org.junit.Test;
 
 import com.google.common.base.Preconditions;
@@ -67,6 +68,7 @@ public class ParserTest extends FrontendTestBase {
   public void ParserError(String stmt, String expectedErrorString) {
     SqlScanner input = new SqlScanner(new StringReader(stmt));
     SqlParser parser = new SqlParser(input);
+    parser.setQueryOptions(new TQueryOptions());
     Object result = null; // Save this object to make debugging easier
     try {
       result = parser.parse().value;

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java b/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java
index e13e51b..042b287 100644
--- a/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java
+++ b/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java
@@ -275,6 +275,7 @@ public class FrontendTestBase {
   public ParseNode ParsesOk(String stmt) {
     SqlScanner input = new SqlScanner(new StringReader(stmt));
     SqlParser parser = new SqlParser(input);
+    parser.setQueryOptions(new TQueryOptions());
     ParseNode node = null;
     try {
       node = (ParseNode) parser.parse().value;
@@ -439,7 +440,7 @@ public class FrontendTestBase {
 
   protected AnalysisResult parseAndAnalyze(String stmt, AnalysisContext ctx, Frontend fe)
       throws ImpalaException {
-    StatementBase parsedStmt = fe.parse(stmt);
+    StatementBase parsedStmt = fe.parse(stmt, ctx.getQueryOptions());
     StmtMetadataLoader mdLoader =
         new StmtMetadataLoader(fe, ctx.getQueryCtx().session.database, null);
     StmtTableCache stmtTableCache = mdLoader.loadTables(parsedStmt);

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/fe/src/test/java/org/apache/impala/service/JdbcTest.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/org/apache/impala/service/JdbcTest.java b/fe/src/test/java/org/apache/impala/service/JdbcTest.java
index 28b032b..0350897 100644
--- a/fe/src/test/java/org/apache/impala/service/JdbcTest.java
+++ b/fe/src/test/java/org/apache/impala/service/JdbcTest.java
@@ -40,6 +40,7 @@ import org.apache.impala.analysis.CreateTableStmt;
 import org.apache.impala.analysis.SqlParser;
 import org.apache.impala.analysis.SqlScanner;
 import org.apache.impala.testutil.ImpalaJdbcClient;
+import org.apache.impala.thrift.TQueryOptions;
 import org.apache.impala.util.Metrics;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -100,6 +101,7 @@ public class JdbcTest {
     // that we do not execute arbitrary SQL here and pollute the test setup.
     SqlScanner input = new SqlScanner(new StringReader(createTableSql));
     SqlParser parser = new SqlParser(input);
+    parser.setQueryOptions(new TQueryOptions());
     Object result = parser.parse().value;
     if (!(result instanceof CreateTableStmt)) {
       throw new Exception("Given stmt is not a CREATE TABLE stmt: " + createTableSql);

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering-disabled.test
----------------------------------------------------------------------
diff --git a/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering-disabled.test b/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering-disabled.test
index 0e7f58c..426ef38 100644
--- a/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering-disabled.test
+++ b/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering-disabled.test
@@ -58,7 +58,7 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional_parquet.alltypes]
    partitions=22/24 files=22 size=172.28KB
-   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, int_col % 2 = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    stored statistics:
      table: rows=unavailable size=unavailable
      partitions: 0/22 rows=unavailable
@@ -88,7 +88,7 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional_parquet.alltypes]
    partitions=24/24 files=24 size=188.29KB
-   predicates: id IN (int_col), id NOT IN (0, 1, 2), string_col IN ('aaaa', 'bbbb', 'cccc', NULL), mod(int_col, 50) IN (0, 1)
+   predicates: id IN (int_col), id NOT IN (0, 1, 2), int_col % 50 IN (0, 1), string_col IN ('aaaa', 'bbbb', 'cccc', NULL)
    stored statistics:
      table: rows=unavailable size=unavailable
      partitions: 0/24 rows=unavailable
@@ -334,7 +334,7 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional.alltypesmixedformat]
    partitions=4/4 files=4 size=66.61KB
-   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, int_col % 2 = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    stored statistics:
      table: rows=unavailable size=unavailable
      partitions: 0/4 rows=unavailable

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering.test
----------------------------------------------------------------------
diff --git a/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering.test b/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering.test
index d5f07f5..247edb3 100644
--- a/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering.test
+++ b/testdata/workloads/functional-planner/queries/PlannerTest/parquet-filtering.test
@@ -82,14 +82,14 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional_parquet.alltypes]
    partitions=22/24 files=22 size=172.93KB
-   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, int_col % 2 = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    stored statistics:
      table: rows=unavailable size=unavailable
      partitions: 0/22 rows=unavailable
      columns missing stats: id, bool_col, tinyint_col, smallint_col, int_col, bigint_col, float_col, double_col, date_string_col, string_col, timestamp_col
    extrapolated-rows=disabled max-scan-range-rows=unavailable
    parquet statistics predicates: bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), date_string_col > '1993-10-01'
-   parquet dictionary predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   parquet dictionary predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, int_col % 2 = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    mem-estimate=128.00MB mem-reservation=88.00KB thread-reservation=1
    tuple-ids=0 row-size=80B cardinality=unavailable
    in pipelines: 00(GETNEXT)
@@ -116,7 +116,7 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional.alltypes]
    partitions=22/24 files=22 size=437.72KB
-   predicates: id = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, tinyint_col < 50, mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   predicates: id = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, tinyint_col < 50, int_col % 2 = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    stored statistics:
      table: rows=7300 size=478.45KB
      partitions: 22/22 rows=6680
@@ -149,13 +149,13 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional_parquet.alltypes]
    partitions=24/24 files=24 size=188.92KB
-   predicates: id IN (int_col), id NOT IN (0, 1, 2), string_col IN ('aaaa', 'bbbb', 'cccc', NULL), mod(int_col, 50) IN (0, 1)
+   predicates: id IN (int_col), id NOT IN (0, 1, 2), int_col % 50 IN (0, 1), string_col IN ('aaaa', 'bbbb', 'cccc', NULL)
    stored statistics:
      table: rows=unavailable size=unavailable
      partitions: 0/24 rows=unavailable
      columns: unavailable
    extrapolated-rows=disabled max-scan-range-rows=unavailable
-   parquet dictionary predicates: id NOT IN (0, 1, 2), string_col IN ('aaaa', 'bbbb', 'cccc', NULL), mod(int_col, 50) IN (0, 1)
+   parquet dictionary predicates: id NOT IN (0, 1, 2), int_col % 50 IN (0, 1), string_col IN ('aaaa', 'bbbb', 'cccc', NULL)
    mem-estimate=48.00MB mem-reservation=24.00KB thread-reservation=1
    tuple-ids=0 row-size=24B cardinality=unavailable
    in pipelines: 00(GETNEXT)
@@ -582,14 +582,14 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional.alltypesmixedformat]
    partitions=4/4 files=4 size=66.61KB
-   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50,  int_col % 2 = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    stored statistics:
      table: rows=unavailable size=unavailable
      partitions: 0/4 rows=unavailable
      columns missing stats: id, bool_col, tinyint_col, smallint_col, int_col, bigint_col, float_col, double_col, date_string_col, string_col, timestamp_col
    extrapolated-rows=disabled max-scan-range-rows=unavailable
    parquet statistics predicates: bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), date_string_col > '1993-10-01'
-   parquet dictionary predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   parquet dictionary predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50,  int_col % 2 = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    mem-estimate=128.00MB mem-reservation=88.00KB thread-reservation=1
    tuple-ids=0 row-size=80B cardinality=unavailable
    in pipelines: 00(GETNEXT)
@@ -617,7 +617,7 @@ PLAN-ROOT SINK
 |
 00:SCAN HDFS [functional.alltypesmixedformat]
    partitions=0/4 files=0 size=0B
-   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), mod(int_col, 2) = 1, timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
+   predicates: bool_col, bigint_col < 5000, double_col > 100.00, float_col > 50.00, id = 1, tinyint_col < 50, int_col % 2 = 1, string_col IN ('aaaa', 'bbbb', 'cccc'), smallint_col IN (1, 2, 3, 4, 5), timestamp_cmp(timestamp_col, TIMESTAMP '2016-11-20 00:00:00') = 1, date_string_col > '1993-10-01'
    stored statistics:
      table: rows=unavailable size=unavailable
      partitions: 0/0 rows=unavailable

http://git-wip-us.apache.org/repos/asf/impala/blob/ba27b038/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
----------------------------------------------------------------------
diff --git a/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test b/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
index a4bfb83..c1ec721 100644
--- a/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
+++ b/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
@@ -512,3 +512,31 @@ select cast("1234.5" as decimal(5, 2));
 ---- CATCH
 UDF ERROR: String to Decimal cast overflowed
 ====
+---- QUERY
+# mod and % should be equivalent in Decimal V2 mode.
+set decimal_v2=true;
+select typeof(9.8 % 3);
+---- RESULTS
+'DECIMAL(2,1)'
+====
+---- QUERY
+# mod and % should be equivalent in Decimal V2 mode.
+set decimal_v2=true;
+select typeof(mod(9.7,3));
+---- RESULTS
+'DECIMAL(2,1)'
+====
+---- QUERY
+# mod and % are different in Decimal V1 mode for this case.
+set decimal_v2=false;
+select typeof(9.6 % 3);
+---- RESULTS
+'DOUBLE'
+====
+---- QUERY
+# mod and % are different in Decimal V1 mode for this case.
+set decimal_v2=false;
+select typeof(mod(9.6, 3));
+---- RESULTS
+'DECIMAL(4,1)'
+====