IMPALA-9035: Simplify casting string to timestamp.

This change will help with queries generated by some BI tools.
Case 1:
Simplify 'string -> bigint -> timestamp' TO 'string -> timestamp':
cast(unix_timestamp('timestr') as timestamp) ->
cast('timestr' as timestamp)
Case 2:
Simplify 'string[fmt] -> bigint -> timestamp' TO 'string -> timestamp':
cast(unix_timestamp('timestr', 'fmt') as timestamp) ->
to_timestamp('timestr', 'fmt')

Tests:
Add front-end tests in ExprRewriteRulesTest.

Change-Id: I4ed72d6e7886eaf50d2be60cf45170ffaef5e72d
Reviewed-on: http://gerrit.cloudera.org:8080/14896
Reviewed-by: Attila Jeges <attilaj@cloudera.com>
Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
diff --git a/fe/src/main/java/org/apache/impala/analysis/Analyzer.java b/fe/src/main/java/org/apache/impala/analysis/Analyzer.java
index 1ef542b..fd85322 100644
--- a/fe/src/main/java/org/apache/impala/analysis/Analyzer.java
+++ b/fe/src/main/java/org/apache/impala/analysis/Analyzer.java
@@ -71,6 +71,7 @@
 import org.apache.impala.rewrite.NormalizeBinaryPredicatesRule;
 import org.apache.impala.rewrite.NormalizeCountStarRule;
 import org.apache.impala.rewrite.NormalizeExprsRule;
+import org.apache.impala.rewrite.SimplifyCastStringToTimestamp;
 import org.apache.impala.rewrite.SimplifyConditionalsRule;
 import org.apache.impala.rewrite.SimplifyDistinctFromRule;
 import org.apache.impala.service.FeSupport;
@@ -483,6 +484,7 @@
         rules.add(EqualityDisjunctsToInRule.INSTANCE);
         rules.add(NormalizeCountStarRule.INSTANCE);
         rules.add(SimplifyDistinctFromRule.INSTANCE);
+        rules.add(SimplifyCastStringToTimestamp.INSTANCE);
       }
       exprRewriter_ = new ExprRewriter(rules);
     }
diff --git a/fe/src/main/java/org/apache/impala/rewrite/SimplifyCastStringToTimestamp.java b/fe/src/main/java/org/apache/impala/rewrite/SimplifyCastStringToTimestamp.java
new file mode 100644
index 0000000..af6ffd8
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/rewrite/SimplifyCastStringToTimestamp.java
@@ -0,0 +1,77 @@
+// 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.impala.rewrite;
+
+import org.apache.impala.analysis.Analyzer;
+import org.apache.impala.analysis.CastExpr;
+import org.apache.impala.analysis.Expr;
+import org.apache.impala.analysis.FunctionCallExpr;
+import org.apache.impala.analysis.FunctionName;
+import org.apache.impala.analysis.TypeDef;
+import org.apache.impala.common.AnalysisException;
+import com.google.common.collect.Lists;
+
+
+/**
+ ** Case 1:
+ ** Simplify 'string -> bigint -> timestamp' TO 'string -> timestamp':
+ ** cast(unix_timestamp('timestr') as timestamp) -> cast('timestr' as timestamp)
+ **
+ ** Case 2:
+ ** Simplify 'string[fmt] -> bigint -> timestamp' TO 'string -> timestamp':
+ ** cast(unix_timestamp('timestr', 'fmt') as timestamp) -> to_timestamp('timestr', 'fmt')
+ **/
+
+public class SimplifyCastStringToTimestamp implements ExprRewriteRule {
+  public static ExprRewriteRule INSTANCE = new SimplifyCastStringToTimestamp();
+
+  @Override
+  public Expr apply(Expr expr, Analyzer analyzer) throws AnalysisException {
+    if (expr instanceof CastExpr &&
+        !((CastExpr)expr).isImplicit() &&
+        expr.getChild(0) instanceof FunctionCallExpr) {
+      if (!expr.isAnalyzed())
+        expr.analyze(analyzer);
+
+      FunctionCallExpr fce = (FunctionCallExpr)expr.getChild(0);
+      if (!expr.getType().isTimestamp() ||
+          !fce.getFnName().getFunction().equalsIgnoreCase("unix_timestamp"))
+        return expr;
+
+      Expr simplifiedExpr = null;
+      if (fce.getChildren().size() == 1 &&
+          fce.getChild(0).getType().isStringType()) {
+        // Handle Case 1
+        simplifiedExpr = new CastExpr(new TypeDef(expr.getType()), fce.getChild(0));
+      } else if (fce.getChildren().size() == 2 &&
+                 fce.getChild(0).getType().isStringType() &&
+                 fce.getChild(1).getType().isStringType()) {
+        // Handle Case 2
+        simplifiedExpr = new FunctionCallExpr(new FunctionName("to_timestamp"),
+            Lists.newArrayList(fce.getChildren()));
+      }
+
+      if (simplifiedExpr != null) {
+        simplifiedExpr.analyze(analyzer);
+        return simplifiedExpr;
+      }
+    }
+
+    return expr;
+  }
+}
diff --git a/fe/src/test/java/org/apache/impala/analysis/ExprRewriteRulesTest.java b/fe/src/test/java/org/apache/impala/analysis/ExprRewriteRulesTest.java
index 40e6b5b..0635210 100644
--- a/fe/src/test/java/org/apache/impala/analysis/ExprRewriteRulesTest.java
+++ b/fe/src/test/java/org/apache/impala/analysis/ExprRewriteRulesTest.java
@@ -40,6 +40,7 @@
 import org.apache.impala.rewrite.NormalizeBinaryPredicatesRule;
 import org.apache.impala.rewrite.NormalizeCountStarRule;
 import org.apache.impala.rewrite.NormalizeExprsRule;
+import org.apache.impala.rewrite.SimplifyCastStringToTimestamp;
 import org.apache.impala.rewrite.SimplifyConditionalsRule;
 import org.apache.impala.rewrite.SimplifyDistinctFromRule;
 import org.junit.BeforeClass;
@@ -770,6 +771,21 @@
     RewritesOk("if(bool_col <=> NULL, 1, 2)", rules, null);
   }
 
+  @Test
+  public void testSimplifyCastStringToTimestamp() throws ImpalaException {
+    ExprRewriteRule rule = SimplifyCastStringToTimestamp.INSTANCE;
+
+    // Can be simplified
+    RewritesOk("cast(unix_timestamp(date_string_col) as timestamp)", rule,
+        "CAST(date_string_col AS TIMESTAMP)");
+    RewritesOk("cast(unix_timestamp(date_string_col, 'yyyy-MM-dd') as timestamp)", rule,
+        "to_timestamp(date_string_col, 'yyyy-MM-dd')");
+
+    // Verify nothing happens
+    RewritesOk("cast(unix_timestamp(timestamp_col) as timestamp)", rule, null);
+    RewritesOk("cast(unix_timestamp() as timestamp)", rule, null);
+  }
+
   /**
    * NULLIF gets converted to an IF, and has cases where
    * it can be further simplified via SimplifyDistinctFromRule.