You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2017/04/19 19:18:12 UTC

[1/3] calcite git commit: [CALCITE-1754] In Csv adapter, convert DATE and TIME values to int, and TIMESTAMP values to long (Hongbin Ma)

Repository: calcite
Updated Branches:
  refs/heads/master cd136985a -> cc8398742


[CALCITE-1754] In Csv adapter, convert DATE and TIME values to int, and TIMESTAMP values to long (Hongbin Ma)

Close apache/calcite#432


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

Branch: refs/heads/master
Commit: cc839874242c69a8199f92097eaf113e5195e4a3
Parents: 8b69e4d
Author: Hongbin Ma <ma...@apache.org>
Authored: Sun Apr 16 22:14:17 2017 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Apr 19 10:39:17 2017 -0700

----------------------------------------------------------------------
 .../calcite/adapter/enumerable/RexImpTable.java |  6 +-
 .../org/apache/calcite/util/BuiltInMethod.java  |  1 +
 .../calcite/adapter/csv/CsvEnumerator.java      | 11 +--
 .../calcite/adapter/csv/CsvFieldType.java       |  4 +-
 .../java/org/apache/calcite/test/CsvTest.java   | 55 +++++++++++++
 .../apache/calcite/adapter/file/SqlTest.java    | 87 ++++++++++++++++++++
 6 files changed, 157 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/cc839874/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 2182c7c..dc69ebb 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -2123,7 +2123,11 @@ public class RexImpTable {
         case MINUS:
           trop1 = Expressions.negate(trop1);
         }
-        return Expressions.call(BuiltInMethod.ADD_MONTHS.method, trop0, trop1);
+        final BuiltInMethod method =
+            operand0.getType().getSqlTypeName() == SqlTypeName.TIMESTAMP
+                ? BuiltInMethod.ADD_MONTHS
+                : BuiltInMethod.ADD_MONTHS_INT;
+        return Expressions.call(method.method, trop0, trop1);
 
       case INTERVAL_DAY:
       case INTERVAL_DAY_HOUR:

http://git-wip-us.apache.org/repos/asf/calcite/blob/cc839874/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 458444b..f5161b2 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -254,6 +254,7 @@ public enum BuiltInMethod {
   FLOOR_DIV(DateTimeUtils.class, "floorDiv", long.class, long.class),
   FLOOR_MOD(DateTimeUtils.class, "floorMod", long.class, long.class),
   ADD_MONTHS(SqlFunctions.class, "addMonths", long.class, int.class),
+  ADD_MONTHS_INT(SqlFunctions.class, "addMonths", int.class, int.class),
   SUBTRACT_MONTHS(SqlFunctions.class, "subtractMonths", long.class,
       long.class),
   FLOOR(SqlFunctions.class, "floor", int.class, int.class),

http://git-wip-us.apache.org/repos/asf/calcite/blob/cc839874/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvEnumerator.java
----------------------------------------------------------------------
diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvEnumerator.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvEnumerator.java
index 262c283..327464b 100644
--- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvEnumerator.java
+++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvEnumerator.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.adapter.csv;
 
 import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.type.SqlTypeName;
@@ -142,7 +143,7 @@ class CsvEnumerator<E> implements Enumerator<E> {
         }
         final RelDataType type;
         if (fieldType == null) {
-          type = typeFactory.createJavaType(String.class);
+          type = typeFactory.createSqlType(SqlTypeName.VARCHAR);
         } else {
           type = fieldType.toType(typeFactory);
         }
@@ -165,7 +166,7 @@ class CsvEnumerator<E> implements Enumerator<E> {
     }
     if (names.isEmpty()) {
       names.add("line");
-      types.add(typeFactory.createJavaType(String.class));
+      types.add(typeFactory.createSqlType(SqlTypeName.VARCHAR));
     }
     return typeFactory.createStructType(Pair.zip(names, types));
   }
@@ -289,7 +290,7 @@ class CsvEnumerator<E> implements Enumerator<E> {
         }
         try {
           Date date = TIME_FORMAT_DATE.parse(string);
-          return new java.sql.Date(date.getTime());
+          return (int) (date.getTime() / DateTimeUtils.MILLIS_PER_DAY);
         } catch (ParseException e) {
           return null;
         }
@@ -299,7 +300,7 @@ class CsvEnumerator<E> implements Enumerator<E> {
         }
         try {
           Date date = TIME_FORMAT_TIME.parse(string);
-          return new java.sql.Time(date.getTime());
+          return (int) date.getTime();
         } catch (ParseException e) {
           return null;
         }
@@ -309,7 +310,7 @@ class CsvEnumerator<E> implements Enumerator<E> {
         }
         try {
           Date date = TIME_FORMAT_TIMESTAMP.parse(string);
-          return new java.sql.Timestamp(date.getTime());
+          return date.getTime();
         } catch (ParseException e) {
           return null;
         }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cc839874/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFieldType.java
----------------------------------------------------------------------
diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFieldType.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFieldType.java
index 7810926..cc9cd76 100644
--- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFieldType.java
+++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFieldType.java
@@ -66,7 +66,9 @@ enum CsvFieldType {
   }
 
   public RelDataType toType(JavaTypeFactory typeFactory) {
-    return typeFactory.createJavaType(clazz);
+    RelDataType javaType = typeFactory.createJavaType(clazz);
+    RelDataType sqlType = typeFactory.createSqlType(javaType.getSqlTypeName());
+    return typeFactory.createTypeWithNullability(sqlType, true);
   }
 
   public static CsvFieldType of(String typeString) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/cc839874/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
----------------------------------------------------------------------
diff --git a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
index 8c2d1d3..5d731d6 100644
--- a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
+++ b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
@@ -25,6 +25,7 @@ import org.apache.calcite.util.Util;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Ordering;
 
 import org.junit.Assert;
 import org.junit.Ignore;
@@ -44,6 +45,7 @@ import java.sql.Statement;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Properties;
@@ -306,6 +308,26 @@ public class CsvTest {
     };
   }
 
+  /** Returns a function that checks the contents of a result set against an
+   * expected string. */
+  private static Function<ResultSet, Void> expectUnordered(String... expected) {
+    final List<String> expectedLines =
+        Ordering.natural().immutableSortedCopy(Arrays.asList(expected));
+    return new Function<ResultSet, Void>() {
+      public Void apply(ResultSet resultSet) {
+        try {
+          final List<String> lines = new ArrayList<>();
+          CsvTest.collect(lines, resultSet);
+          Collections.sort(lines);
+          Assert.assertEquals(expectedLines, lines);
+        } catch (SQLException e) {
+          throw new RuntimeException(e);
+        }
+        return null;
+      }
+    };
+  }
+
   private void checkSql(String sql, String model, Function<ResultSet, Void> fn)
       throws SQLException {
     Connection connection = null;
@@ -392,6 +414,33 @@ public class CsvTest {
         .ok();
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1754">[CALCITE-1754]
+   * In Csv adapter, convert DATE and TIME values to int, and TIMESTAMP values
+   * to long</a>. */
+  @Test public void testGroupByTimestampAdd() throws SQLException {
+    final String sql = "select count(*) as c,\n"
+        + "  {fn timestampadd(SQL_TSI_DAY, 1, JOINEDAT) } as t\n"
+        + "from EMPS group by {fn timestampadd(SQL_TSI_DAY, 1, JOINEDAT ) } ";
+    sql("model", sql)
+        .returnsUnordered("C=1; T=1996-08-04",
+            "C=1; T=2002-05-04",
+            "C=1; T=2005-09-08",
+            "C=1; T=2007-01-02",
+            "C=1; T=2001-01-02")
+        .ok();
+
+    final String sql2 = "select count(*) as c,\n"
+        + "  {fn timestampadd(SQL_TSI_MONTH, 1, JOINEDAT) } as t\n"
+        + "from EMPS group by {fn timestampadd(SQL_TSI_MONTH, 1, JOINEDAT ) } ";
+    sql("model", sql2)
+        .returnsUnordered("C=1; T=2002-06-03",
+            "C=1; T=2005-10-07",
+            "C=1; T=2007-02-01",
+            "C=1; T=2001-02-01",
+            "C=1; T=1996-09-03")
+        .ok();
+  }
   @Test public void testBoolean() throws SQLException {
     sql("smart", "select empno, slacker from emps where slacker")
         .returns("EMPNO=100; SLACKER=true").ok();
@@ -801,6 +850,12 @@ public class CsvTest {
     Fluent returns(String... expectedLines) {
       return checking(expect(expectedLines));
     }
+
+    /** Sets the rows that are expected to be returned from the SQL query,
+     * in no particular order. */
+    Fluent returnsUnordered(String... expectedLines) {
+      return checking(expectUnordered(expectedLines));
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cc839874/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
----------------------------------------------------------------------
diff --git a/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java b/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
index ad39b64..27cc0f0 100644
--- a/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
+++ b/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
@@ -16,8 +16,12 @@
  */
 package org.apache.calcite.adapter.file;
 
+import org.apache.calcite.util.Util;
+
 import com.google.common.base.Function;
+import com.google.common.collect.Ordering;
 
+import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Test;
 
@@ -27,6 +31,10 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Properties;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -72,6 +80,44 @@ public class SqlTest {
     };
   }
 
+  /** Returns a function that checks the contents of a result set against an
+   * expected string. */
+  private static Function<ResultSet, Void> expectUnordered(String... expected) {
+    final List<String> expectedLines =
+        Ordering.natural().immutableSortedCopy(Arrays.asList(expected));
+    return new Function<ResultSet, Void>() {
+      public Void apply(ResultSet resultSet) {
+        try {
+          final List<String> lines = new ArrayList<>();
+          SqlTest.collect(lines, resultSet);
+          Collections.sort(lines);
+          Assert.assertEquals(expectedLines, lines);
+        } catch (SQLException e) {
+          throw new RuntimeException(e);
+        }
+        return null;
+      }
+    };
+  }
+
+  private static void collect(List<String> result, ResultSet resultSet)
+      throws SQLException {
+    final StringBuilder buf = new StringBuilder();
+    while (resultSet.next()) {
+      buf.setLength(0);
+      int n = resultSet.getMetaData().getColumnCount();
+      String sep = "";
+      for (int i = 1; i <= n; i++) {
+        buf.append(sep)
+            .append(resultSet.getMetaData().getColumnLabel(i))
+            .append("=")
+            .append(resultSet.getString(i));
+        sep = "; ";
+      }
+      result.add(Util.toLinux(buf.toString()));
+    }
+  }
+
   private void checkSql(String sql, String model, Function<ResultSet, Void> fn)
       throws SQLException {
     Connection connection = null;
@@ -229,6 +275,41 @@ public class SqlTest {
     });
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1754">[CALCITE-1754]
+   * In Csv adapter, convert DATE and TIME values to int, and TIMESTAMP values
+   * to long</a>. */
+  @Test public void testGroupByTimestampAdd() throws SQLException {
+    final String sql = "select count(*) as c,\n"
+        + "  {fn timestampadd(SQL_TSI_DAY, 1, JOINEDAT) } as t\n"
+        + "from EMPS group by {fn timestampadd(SQL_TSI_DAY, 1, JOINEDAT ) } ";
+    sql("sales-csv", sql)
+        .returnsUnordered("C=1; T=1996-08-04",
+            "C=1; T=2002-05-04",
+            "C=1; T=2005-09-08",
+            "C=1; T=2007-01-02",
+            "C=1; T=2001-01-02")
+        .ok();
+    final String sql2 = "select count(*) as c,\n"
+        + "  {fn timestampadd(SQL_TSI_MONTH, 1, JOINEDAT) } as t\n"
+        + "from EMPS group by {fn timestampadd(SQL_TSI_MONTH, 1, JOINEDAT ) } ";
+    sql("sales-csv", sql2)
+        .returnsUnordered("C=1; T=2002-06-03",
+            "C=1; T=2005-10-07",
+            "C=1; T=2007-02-01",
+            "C=1; T=2001-02-01",
+            "C=1; T=1996-09-03").ok();
+    final String sql3 = "select\n"
+        + " distinct {fn timestampadd(SQL_TSI_MONTH, 1, JOINEDAT) } as t\n"
+        + "from EMPS";
+    sql("sales-csv", sql3)
+        .returnsUnordered("T=2002-06-03",
+            "T=2005-10-07",
+            "T=2007-02-01",
+            "T=2001-02-01",
+            "T=1996-09-03").ok();
+  }
+
   /** Fluent API to perform test actions. */
   private class Fluent {
     private final String model;
@@ -260,6 +341,12 @@ public class SqlTest {
     Fluent returns(String... expectedLines) {
       return checking(expect(expectedLines));
     }
+
+    /** Sets the rows that are expected to be returned from the SQL query,
+     * in no particular order. */
+    Fluent returnsUnordered(String... expectedLines) {
+      return checking(expectUnordered(expectedLines));
+    }
   }
 }
 


[2/3] calcite git commit: Refactor CsvTest and SqlTest

Posted by jh...@apache.org.
Refactor CsvTest and SqlTest


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

Branch: refs/heads/master
Commit: 8b69e4da4a94985321b4ed0037255284093f4669
Parents: 0d2d11f
Author: Julian Hyde <jh...@apache.org>
Authored: Tue Apr 18 13:40:42 2017 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Apr 19 10:39:17 2017 -0700

----------------------------------------------------------------------
 .../java/org/apache/calcite/test/CsvTest.java   | 183 ++++++++++++-------
 .../apache/calcite/adapter/file/SqlTest.java    | 102 ++++++++---
 2 files changed, 185 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/8b69e4da/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
----------------------------------------------------------------------
diff --git a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
index 8c056e8..8c2d1d3 100644
--- a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
+++ b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
@@ -19,11 +19,11 @@ package org.apache.calcite.test;
 import org.apache.calcite.adapter.csv.CsvSchemaFactory;
 import org.apache.calcite.adapter.csv.CsvStreamTableFactory;
 import org.apache.calcite.jdbc.CalciteConnection;
-import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.schema.Schema;
 import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 
 import org.junit.Assert;
@@ -171,15 +171,15 @@ public class CsvTest {
    * Reads from a table.
    */
   @Test public void testSelect() throws SQLException {
-    checkSql("model", "select * from EMPS");
+    sql("model", "select * from EMPS").ok();
   }
 
   @Test public void testSelectSingleProjectGz() throws SQLException {
-    checkSql("smart", "select name from EMPS");
+    sql("smart", "select name from EMPS").ok();
   }
 
   @Test public void testSelectSingleProject() throws SQLException {
-    checkSql("smart", "select name from DEPTS");
+    sql("smart", "select name from DEPTS").ok();
   }
 
   /** Test case for
@@ -189,7 +189,7 @@ public class CsvTest {
     final String sql = "select empno * 3 as e3\n"
         + "from long_emps where empno = 100";
 
-    checkSql(sql, "bug", new Function1<ResultSet, Void>() {
+    sql("bug", sql).checking(new Function<ResultSet, Void>() {
       public Void apply(ResultSet resultSet) {
         try {
           assertThat(resultSet.next(), is(true));
@@ -201,74 +201,83 @@ public class CsvTest {
           throw new RuntimeException(e);
         }
       }
-    });
+    }).ok();
   }
 
   @Test public void testCustomTable() throws SQLException {
-    checkSql("model-with-custom-table", "select * from CUSTOM_TABLE.EMPS");
+    sql("model-with-custom-table", "select * from CUSTOM_TABLE.EMPS").ok();
   }
 
   @Test public void testPushDownProjectDumb() throws SQLException {
     // rule does not fire, because we're using 'dumb' tables in simple model
-    checkSql("model", "explain plan for select * from EMPS",
-        "PLAN=EnumerableInterpreter\n"
-            + "  BindableTableScan(table=[[SALES, EMPS]])\n");
+    final String sql = "explain plan for select * from EMPS";
+    final String expected = "PLAN=EnumerableInterpreter\n"
+        + "  BindableTableScan(table=[[SALES, EMPS]])\n";
+    sql("model", sql).returns(expected).ok();
   }
 
   @Test public void testPushDownProject() throws SQLException {
-    checkSql("smart", "explain plan for select * from EMPS",
-        "PLAN=CsvTableScan(table=[[SALES, EMPS]], fields=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])\n");
+    final String sql = "explain plan for select * from EMPS";
+    final String expected = "PLAN=CsvTableScan(table=[[SALES, EMPS]], "
+        + "fields=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])\n";
+    sql("smart", sql).returns(expected).ok();
   }
 
   @Test public void testPushDownProject2() throws SQLException {
-    checkSql("smart", "explain plan for select name, empno from EMPS",
-        "PLAN=CsvTableScan(table=[[SALES, EMPS]], fields=[[1, 0]])\n");
+    sql("smart", "explain plan for select name, empno from EMPS")
+        .returns("PLAN=CsvTableScan(table=[[SALES, EMPS]], fields=[[1, 0]])\n")
+        .ok();
     // make sure that it works...
-    checkSql("smart", "select name, empno from EMPS",
-        "NAME=Fred; EMPNO=100",
-        "NAME=Eric; EMPNO=110",
-        "NAME=John; EMPNO=110",
-        "NAME=Wilma; EMPNO=120",
-        "NAME=Alice; EMPNO=130");
+    sql("smart", "select name, empno from EMPS")
+        .returns("NAME=Fred; EMPNO=100",
+            "NAME=Eric; EMPNO=110",
+            "NAME=John; EMPNO=110",
+            "NAME=Wilma; EMPNO=120",
+            "NAME=Alice; EMPNO=130")
+        .ok();
   }
 
   @Test public void testFilterableSelect() throws SQLException {
-    checkSql("filterable-model", "select name from EMPS");
+    sql("filterable-model", "select name from EMPS").ok();
   }
 
   @Test public void testFilterableSelectStar() throws SQLException {
-    checkSql("filterable-model", "select * from EMPS");
+    sql("filterable-model", "select * from EMPS").ok();
   }
 
   /** Filter that can be fully handled by CsvFilterableTable. */
   @Test public void testFilterableWhere() throws SQLException {
-    checkSql("filterable-model",
-        "select empno, gender, name from EMPS where name = 'John'",
-        "EMPNO=110; GENDER=M; NAME=John");
+    final String sql =
+        "select empno, gender, name from EMPS where name = 'John'";
+    sql("filterable-model", sql)
+        .returns("EMPNO=110; GENDER=M; NAME=John").ok();
   }
 
   /** Filter that can be partly handled by CsvFilterableTable. */
   @Test public void testFilterableWhere2() throws SQLException {
-    checkSql("filterable-model",
-        "select empno, gender, name from EMPS where gender = 'F' and empno > 125",
-        "EMPNO=130; GENDER=F; NAME=Alice");
+    final String sql = "select empno, gender, name from EMPS\n"
+        + " where gender = 'F' and empno > 125";
+    sql("filterable-model", sql)
+        .returns("EMPNO=130; GENDER=F; NAME=Alice").ok();
   }
 
   @Test public void testJson() throws SQLException {
-    checkSql("bug", "select _MAP['id'] as id,\n"
-            + " _MAP['title'] as title,\n"
-            + " CHAR_LENGTH(CAST(_MAP['title'] AS VARCHAR(30))) as len\n"
-            + " from \"archers\"\n",
-        "ID=19990101; TITLE=Tractor trouble.; LEN=16",
-        "ID=19990103; TITLE=Charlie's surprise.; LEN=19");
+    final String sql = "select _MAP['id'] as id,\n"
+        + " _MAP['title'] as title,\n"
+        + " CHAR_LENGTH(CAST(_MAP['title'] AS VARCHAR(30))) as len\n"
+        + " from \"archers\"\n";
+    sql("bug", sql)
+        .returns("ID=19990101; TITLE=Tractor trouble.; LEN=16",
+            "ID=19990103; TITLE=Charlie's surprise.; LEN=19")
+        .ok();
   }
 
-  private void checkSql(String model, String sql) throws SQLException {
-    checkSql(sql, model, output());
+  private Fluent sql(String model, String sql) {
+    return new Fluent(model, sql, output());
   }
 
-  private Function1<ResultSet, Void> output() {
-    return new Function1<ResultSet, Void>() {
+  private Function<ResultSet, Void> output() {
+    return new Function<ResultSet, Void>() {
       public Void apply(ResultSet resultSet) {
         try {
           output(resultSet, System.out);
@@ -280,15 +289,10 @@ public class CsvTest {
     };
   }
 
-  private void checkSql(String model, String sql, final String... expected)
-      throws SQLException {
-    checkSql(sql, model, expect(expected));
-  }
-
   /** Returns a function that checks the contents of a result set against an
    * expected string. */
-  private static Function1<ResultSet, Void> expect(final String... expected) {
-    return new Function1<ResultSet, Void>() {
+  private static Function<ResultSet, Void> expect(final String... expected) {
+    return new Function<ResultSet, Void>() {
       public Void apply(ResultSet resultSet) {
         try {
           final List<String> lines = new ArrayList<>();
@@ -302,7 +306,7 @@ public class CsvTest {
     };
   }
 
-  private void checkSql(String sql, String model, Function1<ResultSet, Void> fn)
+  private void checkSql(String sql, String model, Function<ResultSet, Void> fn)
       throws SQLException {
     Connection connection = null;
     Statement statement = null;
@@ -370,34 +374,36 @@ public class CsvTest {
   }
 
   @Test public void testJoinOnString() throws SQLException {
-    checkSql("smart",
-        "select * from emps join depts on emps.name = depts.name");
+    final String sql = "select * from emps\n"
+        + "join depts on emps.name = depts.name";
+    sql("smart", sql).ok();
   }
 
   @Test public void testWackyColumns() throws SQLException {
-    checkSql("select * from wacky_column_names where false", "bug",
-        expect());
-    checkSql(
-        "select \"joined at\", \"naME\" from wacky_column_names where \"2gender\" = 'F'",
-        "bug",
-        expect(
-            "joined at=2005-09-07; naME=Wilma",
-            "joined at=2007-01-01; naME=Alice"));
+    final String sql = "select * from wacky_column_names where false";
+    sql("bug", sql).returns().ok();
+
+    final String sql2 = "select \"joined at\", \"naME\"\n"
+        + "from wacky_column_names\n"
+        + "where \"2gender\" = 'F'";
+    sql("bug", sql2)
+        .returns("joined at=2005-09-07; naME=Wilma",
+            "joined at=2007-01-01; naME=Alice")
+        .ok();
   }
 
   @Test public void testBoolean() throws SQLException {
-    checkSql("smart",
-        "select empno, slacker from emps where slacker",
-        "EMPNO=100; SLACKER=true");
+    sql("smart", "select empno, slacker from emps where slacker")
+        .returns("EMPNO=100; SLACKER=true").ok();
   }
 
   @Test public void testReadme() throws SQLException {
-    checkSql("SELECT d.name, COUNT(*) cnt"
-            + " FROM emps AS e"
-            + " JOIN depts AS d ON e.deptno = d.deptno"
-            + " GROUP BY d.name",
-        "smart",
-        expect("NAME=Sales; CNT=1", "NAME=Marketing; CNT=2"));
+    final String sql = "SELECT d.name, COUNT(*) cnt"
+        + " FROM emps AS e"
+        + " JOIN depts AS d ON e.deptno = d.deptno"
+        + " GROUP BY d.name";
+    sql("smart", sql)
+        .returns("NAME=Sales; CNT=1", "NAME=Marketing; CNT=2").ok();
   }
 
   /** Test case for
@@ -410,10 +416,12 @@ public class CsvTest {
         + "FROM emps AS e\n"
         + "WHERE cast(e.empno as bigint) in ";
     final int threshold = SqlToRelConverter.DEFAULT_IN_SUB_QUERY_THRESHOLD;
-    checkSql(sql + range(130, threshold - 5), "smart", expect("NAME=Alice"));
-    checkSql(sql + range(130, threshold), "smart", expect("NAME=Alice"));
-    checkSql(sql + range(130, threshold + 1000), "smart",
-        expect("NAME=Alice"));
+    sql("smart", sql + range(130, threshold - 5))
+        .returns("NAME=Alice").ok();
+    sql("smart", sql + range(130, threshold))
+        .returns("NAME=Alice").ok();
+    sql("smart", sql + range(130, threshold + 1000))
+        .returns("NAME=Alice").ok();
   }
 
   /** Test case for
@@ -424,7 +432,7 @@ public class CsvTest {
         + "FROM emps AS e\n"
         + "WHERE e.empno in "
         + range(130, SqlToRelConverter.DEFAULT_IN_SUB_QUERY_THRESHOLD);
-    checkSql(sql, "smart", expect("NAME=Alice"));
+    sql("smart", sql).returns("NAME=Alice").ok();
   }
 
   private String range(int first, int count) {
@@ -604,7 +612,7 @@ public class CsvTest {
 
       statement2.setString(1, "Sales");
       final ResultSet resultSet1 = statement2.executeQuery();
-      Function1<ResultSet, Void> expect = expect("DEPTNO=10; NAME=Sales");
+      Function<ResultSet, Void> expect = expect("DEPTNO=10; NAME=Sales");
       expect.apply(resultSet1);
     }
   }
@@ -761,6 +769,39 @@ public class CsvTest {
       }
     }
   }
+
+  /** Fluent API to perform test actions. */
+  private class Fluent {
+    private final String model;
+    private final String sql;
+    private final Function<ResultSet, Void> expect;
+
+    Fluent(String model, String sql, Function<ResultSet, Void> expect) {
+      this.model = model;
+      this.sql = sql;
+      this.expect = expect;
+    }
+
+    /** Runs the test. */
+    Fluent ok() {
+      try {
+        checkSql(sql, model, expect);
+        return this;
+      } catch (SQLException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    /** Assigns a function to call to test whether output is correct. */
+    Fluent checking(Function<ResultSet, Void> expect) {
+      return new Fluent(model, sql, expect);
+    }
+
+    /** Sets the rows that are expected to be returned from the SQL query. */
+    Fluent returns(String... expectedLines) {
+      return checking(expect(expectedLines));
+    }
+  }
 }
 
 // End CsvTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8b69e4da/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
----------------------------------------------------------------------
diff --git a/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java b/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
index 37d3bfe..ad39b64 100644
--- a/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
+++ b/file/src/test/java/org/apache/calcite/adapter/file/SqlTest.java
@@ -40,14 +40,21 @@ import static org.junit.Assert.assertThat;
 public class SqlTest {
   // helper functions
 
-  private void checkSql(String model, String sql, String... expectedLines)
-      throws SQLException {
+  private Fluent sql(String model, String sql) {
+    return new Fluent(model, sql, new Function<ResultSet, Void>() {
+      public Void apply(ResultSet input) {
+        throw new AssertionError();
+      }
+    });
+  }
+
+  private Function<ResultSet, Void> expect(String... expectedLines) {
     final StringBuilder b = new StringBuilder();
     for (String s : expectedLines) {
       b.append(s).append('\n');
     }
     final String expected = b.toString();
-    checkSql(sql, model, new Function<ResultSet, Void>() {
+    return new Function<ResultSet, Void>() {
       public Void apply(ResultSet resultSet) {
         try {
           String actual = SqlTest.toString(resultSet);
@@ -62,7 +69,7 @@ public class SqlTest {
         }
         return null;
       }
-    });
+    };
   }
 
   private void checkSql(String sql, String model, Function<ResultSet, Void> fn)
@@ -121,7 +128,7 @@ public class SqlTest {
   /** Reads from a local file and checks the result. */
   @Test public void testFileSelect() throws SQLException {
     final String sql = "select H1 from T1 where H0 = 'R1C0'";
-    checkSql("testModel", sql, "H1=R1C1");
+    sql("testModel", sql).returns("H1=R1C1").ok();
   }
 
   /** Reads from a local file without table headers &lt;TH&gt; and checks the
@@ -129,14 +136,14 @@ public class SqlTest {
   @Test public void testNoThSelect() throws SQLException {
     Assume.assumeTrue(FileSuite.hazNetwork());
     final String sql = "select \"col1\" from T1_NO_TH where \"col0\" like 'R0%'";
-    checkSql("testModel", sql, "col1=R0C1");
+    sql("testModel", sql).returns("col1=R0C1").ok();
   }
 
   /** Reads from a local file - finds larger table even without &lt;TH&gt;
    * elements. */
   @Test public void testFindBiggerNoTh() throws SQLException {
     final String sql = "select \"col4\" from TABLEX2 where \"col0\" like 'R1%'";
-    checkSql("testModel", sql, "col4=R1C4");
+    sql("testModel", sql).returns("col4=R1C4").ok();
   }
 
   /** Reads from a URL and checks the result. */
@@ -144,54 +151,59 @@ public class SqlTest {
     Assume.assumeTrue(FileSuite.hazNetwork());
     final String sql = "select \"State\", \"Statehood\" from \"States_as_of\"\n"
         + "where \"State\" = 'California'";
-    checkSql("wiki", sql, "State=California; Statehood=1850-09-09");
+    sql("wiki", sql).returns("State=California; Statehood=1850-09-09").ok();
   }
 
   /** Reads the EMPS table. */
   @Test public void testSalesEmps() throws SQLException {
     final String sql = "select * from sales.emps";
-    checkSql("sales", sql,
-        "EMPNO=100; NAME=Fred; DEPTNO=30",
-        "EMPNO=110; NAME=Eric; DEPTNO=20",
-        "EMPNO=110; NAME=John; DEPTNO=40",
-        "EMPNO=120; NAME=Wilma; DEPTNO=20",
-        "EMPNO=130; NAME=Alice; DEPTNO=40");
+    sql("sales", sql)
+        .returns("EMPNO=100; NAME=Fred; DEPTNO=30",
+            "EMPNO=110; NAME=Eric; DEPTNO=20",
+            "EMPNO=110; NAME=John; DEPTNO=40",
+            "EMPNO=120; NAME=Wilma; DEPTNO=20",
+            "EMPNO=130; NAME=Alice; DEPTNO=40")
+        .ok();
   }
 
   /** Reads the DEPTS table. */
   @Test public void testSalesDepts() throws SQLException {
     final String sql = "select * from sales.depts";
-    checkSql("sales", sql,
-        "DEPTNO=10; NAME=Sales",
-        "DEPTNO=20; NAME=Marketing",
-        "DEPTNO=30; NAME=Accounts");
+    sql("sales", sql)
+        .returns("DEPTNO=10; NAME=Sales",
+            "DEPTNO=20; NAME=Marketing",
+            "DEPTNO=30; NAME=Accounts")
+        .ok();
   }
 
   /** Reads the DEPTS table from the CSV schema. */
   @Test public void testCsvSalesDepts() throws SQLException {
     final String sql = "select * from sales.depts";
-    checkSql("sales-csv", sql,
-        "DEPTNO=10; NAME=Sales",
-        "DEPTNO=20; NAME=Marketing",
-        "DEPTNO=30; NAME=Accounts");
+    sql("sales-csv", sql)
+        .returns("DEPTNO=10; NAME=Sales",
+            "DEPTNO=20; NAME=Marketing",
+            "DEPTNO=30; NAME=Accounts")
+        .ok();
   }
 
   /** Reads the EMPS table from the CSV schema. */
   @Test public void testCsvSalesEmps() throws SQLException {
     final String sql = "select * from sales.emps";
-    checkSql("sales-csv", sql,
-        "EMPNO=100; NAME=Fred; DEPTNO=10; GENDER=; CITY=; EMPID=30; AGE=25; SLACKER=true; MANAGER=false; JOINEDAT=1996-08-03",
-        "EMPNO=110; NAME=Eric; DEPTNO=20; GENDER=M; CITY=San Francisco; EMPID=3; AGE=80; SLACKER=null; MANAGER=false; JOINEDAT=2001-01-01",
-        "EMPNO=110; NAME=John; DEPTNO=40; GENDER=M; CITY=Vancouver; EMPID=2; AGE=null; SLACKER=false; MANAGER=true; JOINEDAT=2002-05-03",
-        "EMPNO=120; NAME=Wilma; DEPTNO=20; GENDER=F; CITY=; EMPID=1; AGE=5; SLACKER=null; MANAGER=true; JOINEDAT=2005-09-07",
-        "EMPNO=130; NAME=Alice; DEPTNO=40; GENDER=F; CITY=Vancouver; EMPID=2; AGE=null; SLACKER=false; MANAGER=true; JOINEDAT=2007-01-01");
+    final String[] lines = {
+      "EMPNO=100; NAME=Fred; DEPTNO=10; GENDER=; CITY=; EMPID=30; AGE=25; SLACKER=true; MANAGER=false; JOINEDAT=1996-08-03",
+      "EMPNO=110; NAME=Eric; DEPTNO=20; GENDER=M; CITY=San Francisco; EMPID=3; AGE=80; SLACKER=null; MANAGER=false; JOINEDAT=2001-01-01",
+      "EMPNO=110; NAME=John; DEPTNO=40; GENDER=M; CITY=Vancouver; EMPID=2; AGE=null; SLACKER=false; MANAGER=true; JOINEDAT=2002-05-03",
+      "EMPNO=120; NAME=Wilma; DEPTNO=20; GENDER=F; CITY=; EMPID=1; AGE=5; SLACKER=null; MANAGER=true; JOINEDAT=2005-09-07",
+      "EMPNO=130; NAME=Alice; DEPTNO=40; GENDER=F; CITY=Vancouver; EMPID=2; AGE=null; SLACKER=false; MANAGER=true; JOINEDAT=2007-01-01",
+    };
+    sql("sales-csv", sql).returns(lines).ok();
   }
 
   /** Reads the HEADER_ONLY table from the CSV schema. The CSV file has one
    * line - the column headers - but no rows of data. */
   @Test public void testCsvSalesHeaderOnly() throws SQLException {
     final String sql = "select * from sales.header_only";
-    checkSql("sales-csv", sql);
+    sql("sales-csv", sql).returns().ok();
   }
 
   /** Reads the EMPTY table from the CSV schema. The CSV file has no lines,
@@ -217,6 +229,38 @@ public class SqlTest {
     });
   }
 
+  /** Fluent API to perform test actions. */
+  private class Fluent {
+    private final String model;
+    private final String sql;
+    private final Function<ResultSet, Void> expect;
+
+    Fluent(String model, String sql, Function<ResultSet, Void> expect) {
+      this.model = model;
+      this.sql = sql;
+      this.expect = expect;
+    }
+
+    /** Runs the test. */
+    Fluent ok() {
+      try {
+        checkSql(sql, model, expect);
+        return this;
+      } catch (SQLException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    /** Assigns a function to call to test whether output is correct. */
+    Fluent checking(Function<ResultSet, Void> expect) {
+      return new Fluent(model, sql, expect);
+    }
+
+    /** Sets the rows that are expected to be returned from the SQL query. */
+    Fluent returns(String... expectedLines) {
+      return checking(expect(expectedLines));
+    }
+  }
 }
 
 // End SqlTest.java


[3/3] calcite git commit: [CALCITE-1639] TIMESTAMPADD(MONTH, ...) should return last day of month if the day overflows (Hongbin Ma)

Posted by jh...@apache.org.
[CALCITE-1639] TIMESTAMPADD(MONTH, ...) should return last day of month if the day overflows (Hongbin Ma)

Close apache/calcite#431


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

Branch: refs/heads/master
Commit: 0d2d11f3b8ab4a11279b4f73d0cd913902872880
Parents: cd13698
Author: Julian Hyde <jh...@apache.org>
Authored: Tue Apr 18 13:40:16 2017 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Apr 19 10:39:17 2017 -0700

----------------------------------------------------------------------
 .../main/java/org/apache/calcite/runtime/SqlFunctions.java   | 6 +-----
 .../org/apache/calcite/sql/test/SqlOperatorBaseTest.java     | 8 ++++++++
 .../test/java/org/apache/calcite/test/SqlFunctionsTest.java  | 8 ++++----
 3 files changed, 13 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/0d2d11f3/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index baa2c6f..fec73c6 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -2089,11 +2089,7 @@ public class SqlFunctions {
     m0 += m - y * 12;
     int last = lastDay(y0, m0);
     if (d0 > last) {
-      d0 = 1;
-      if (++m0 > 12) {
-        m0 = 1;
-        ++y0;
-      }
+      d0 = last;
     }
     return DateTimeUtils.ymdToUnixDate(y0, m0, d0);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/0d2d11f3/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 3e8cbd2..2ba92ac 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -5709,6 +5709,14 @@ public abstract class SqlOperatorBaseTest {
         null, "TIMESTAMP(0)");
     tester.checkScalar("timestampadd(DAY, 1, cast(null as date))",
         null, "DATE");
+
+    // Round to the last day of previous month
+    tester.checkScalar("timestampadd(MONTH, 1, date '2016-05-31')",
+        "2016-06-30", "DATE NOT NULL");
+    tester.checkScalar("timestampadd(MONTH, 5, date '2016-01-31')",
+        "2016-06-30", "DATE NOT NULL");
+    tester.checkScalar("timestampadd(MONTH, -1, date '2016-03-31')",
+        "2016-02-29", "DATE NOT NULL");
   }
 
   @Test public void testTimestampDiff() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/0d2d11f3/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
index 12ef35b..4293654 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
@@ -365,10 +365,10 @@ public class SqlFunctionsTest {
     checkAddMonths(2016, 1, 1, 2017, 2, 1, 13);
     checkAddMonths(2016, 1, 1, 2015, 1, 1, -12);
     checkAddMonths(2016, 1, 1, 2018, 10, 1, 33);
-    checkAddMonths(2016, 1, 31, 2016, 5, 1, 3); // roll up
-    checkAddMonths(2016, 4, 30, 2016, 7, 30, 3); // roll up
-    checkAddMonths(2016, 1, 31, 2016, 3, 1, 1);
-    checkAddMonths(2016, 3, 31, 2016, 3, 1, -1);
+    checkAddMonths(2016, 1, 31, 2016, 4, 30, 3);
+    checkAddMonths(2016, 4, 30, 2016, 7, 30, 3);
+    checkAddMonths(2016, 1, 31, 2016, 2, 29, 1);
+    checkAddMonths(2016, 3, 31, 2016, 2, 29, -1);
     checkAddMonths(2016, 3, 31, 2116, 3, 31, 1200);
     checkAddMonths(2016, 2, 28, 2116, 2, 28, 1200);
   }