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/11/02 23:25:37 UTC

[1/5] calcite git commit: [CALCITE-1998] Hive ORDER BY null values (Abbas Gadhia)

Repository: calcite
Updated Branches:
  refs/heads/master 17cd76af7 -> 2f0eecb3c


[CALCITE-1998] Hive ORDER BY null values (Abbas Gadhia)

Make HiveSqlDialect aware of the version so that it may switch NULLS
FIRST/NULLS LAST behavior based on version.  Use DatabaseMetadata
major and minor version integer values instead of relying on strings
to compare versions.

Add dialect for Google BigQuery.

Change the default NullCollation for MySQL to reflect the correct collation.

Optimize the code generated for NULLS FIRST/LAST based on the order of
the sort. If the default sort would return nulls in the same order as
the NULLS FIRST/LAST flag, then we are dropping the emulation of the
NULLS FIRST/LAST clause.  Change the emulateNulls method to support
all types of NullCollations

Fix javadoc, restore MySQL calls to ISNULL. (Julian Hyde)

Close apache/calcite#545


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

Branch: refs/heads/master
Commit: 45425103c0be0604bb3c6c650f8f13ecb47de519
Parents: 17cd76a
Author: Abbas Gadhia <ab...@yahoo.com>
Authored: Thu Oct 5 16:33:31 2017 +0530
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 2 11:30:26 2017 -0700

----------------------------------------------------------------------
 .../apache/calcite/config/NullCollation.java    |  20 ++
 .../apache/calcite/rel/RelFieldCollation.java   |  12 +
 .../calcite/rel/rel2sql/RelToSqlConverter.java  |   5 +-
 .../calcite/rel/rel2sql/SqlImplementor.java     |   5 +-
 .../java/org/apache/calcite/sql/SqlDialect.java |  79 ++++++-
 .../calcite/sql/SqlDialectFactoryImpl.java      |   9 +-
 .../calcite/sql/dialect/BigQuerySqlDialect.java |  39 ++++
 .../calcite/sql/dialect/HiveSqlDialect.java     |  23 +-
 .../calcite/sql/dialect/MysqlSqlDialect.java    |  13 +-
 .../rel/rel2sql/RelToSqlConverterTest.java      | 226 +++++++++++++++++++
 .../java/org/apache/calcite/test/JdbcTest.java  |  42 +++-
 11 files changed, 434 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/config/NullCollation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/config/NullCollation.java b/core/src/main/java/org/apache/calcite/config/NullCollation.java
index 8fc17f2..f26191b 100644
--- a/core/src/main/java/org/apache/calcite/config/NullCollation.java
+++ b/core/src/main/java/org/apache/calcite/config/NullCollation.java
@@ -45,6 +45,26 @@ public enum NullCollation {
       return !desc;
     }
   }
+
+  /** Returns whether a given combination of null direction and sort order is
+   * the default order of nulls returned in the ORDER BY clause. */
+  public boolean isDefaultOrder(boolean nullsFirst, boolean desc) {
+    final boolean asc = !desc;
+    final boolean nullsLast = !nullsFirst;
+
+    switch (this) {
+    case FIRST:
+      return nullsFirst;
+    case LAST:
+      return nullsLast;
+    case LOW:
+      return (asc && nullsFirst) || (desc && nullsLast);
+    case HIGH:
+      return (asc && nullsLast) || (desc && nullsFirst);
+    default:
+      throw new IllegalArgumentException("Unrecognized Null Collation");
+    }
+  }
 }
 
 // End NullCollation.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java b/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
index c216912..660b3cd 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
@@ -139,6 +139,18 @@ public class RelFieldCollation {
         return NullDirection.UNSPECIFIED;
       }
     }
+
+    /** Returns whether this is {@link #DESCENDING} or
+     * {@link #STRICTLY_DESCENDING}. */
+    public boolean isDescending() {
+      switch (this) {
+      case DESCENDING:
+      case STRICTLY_DESCENDING:
+        return true;
+      default:
+        return false;
+      }
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
index 1e60c10..5e9cfc2 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
@@ -457,8 +457,9 @@ public class RelToSqlConverter extends SqlImplementor
       for (RelFieldCollation fc : e.getOrderKeys().getFieldCollations()) {
         if (fc.nullDirection != RelFieldCollation.NullDirection.UNSPECIFIED) {
           boolean first = fc.nullDirection == RelFieldCollation.NullDirection.FIRST;
-          SqlNode nullDirectionNode = dialect.emulateNullDirection(
-              context.field(fc.getFieldIndex()), first);
+          SqlNode nullDirectionNode =
+              dialect.emulateNullDirection(context.field(fc.getFieldIndex()),
+                  first, fc.direction.isDescending());
           if (nullDirectionNode != null) {
             orderBySqlList.add(nullDirectionNode);
             fc = new RelFieldCollation(fc.getFieldIndex(), fc.getDirection(),

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 0304743..0faacf5 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -1122,8 +1122,9 @@ public abstract class SqlImplementor {
         RelFieldCollation field) {
       if (field.nullDirection != RelFieldCollation.NullDirection.UNSPECIFIED) {
         boolean first = field.nullDirection == RelFieldCollation.NullDirection.FIRST;
-        SqlNode nullDirectionNode = dialect.emulateNullDirection(
-            context.field(field.getFieldIndex()), first);
+        SqlNode nullDirectionNode =
+            dialect.emulateNullDirection(context.field(field.getFieldIndex()),
+                first, field.direction.isDescending());
         if (nullDirectionNode != null) {
           orderByList.add(nullDirectionNode);
           field = new RelFieldCollation(field.getFieldIndex(),

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
index 6023089..e2fe25a 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
@@ -23,6 +23,7 @@ import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.dialect.AnsiSqlDialect;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.BasicSqlType;
 import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -74,7 +75,7 @@ public class SqlDialect {
   private final String identifierEndQuoteString;
   private final String identifierEscapedQuote;
   private final DatabaseProduct databaseProduct;
-  private final NullCollation nullCollation;
+  protected final NullCollation nullCollation;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -155,7 +156,7 @@ public class SqlDialect {
 
   /** Creates an empty context. Use {@link #EMPTY_CONTEXT} if possible. */
   protected static Context emptyContext() {
-    return new ContextImpl(DatabaseProduct.UNKNOWN, null, null, null,
+    return new ContextImpl(DatabaseProduct.UNKNOWN, null, null, -1, -1, null,
         NullCollation.HIGH);
   }
 
@@ -226,6 +227,8 @@ public class SqlDialect {
       return DatabaseProduct.H2;
     } else if (upperProductName.contains("VERTICA")) {
       return DatabaseProduct.VERTICA;
+    } else if (upperProductName.contains("GOOGLE BIGQUERY")) {
+      return DatabaseProduct.BIG_QUERY;
     } else {
       return DatabaseProduct.UNKNOWN;
     }
@@ -532,13 +535,32 @@ public class SqlDialect {
    * or <code>null</code> if no emulation needs to be done.
    *
    * @param node The SqlNode representing the expression
-   * @param nullsFirst <code>true</code> if nulls should come first, <code>false</code> otherwise
+   * @param nullsFirst Whether nulls should come first
+   * @param desc Whether the sort direction is
+   * {@link org.apache.calcite.rel.RelFieldCollation.Direction#DESCENDING} or
+   * {@link org.apache.calcite.rel.RelFieldCollation.Direction#STRICTLY_DESCENDING}
    * @return A SqlNode for null direction emulation or <code>null</code> if not required
    */
-  public SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst) {
+  public SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst,
+      boolean desc) {
     return null;
   }
 
+  protected SqlNode emulateNullDirectionWithIsNull(SqlNode node,
+      boolean nullsFirst, boolean desc) {
+    // No need for emulation if the nulls will anyways come out the way we want
+    // them based on "nullsFirst" and "desc".
+    if (nullCollation.isDefaultOrder(nullsFirst, desc)) {
+      return null;
+    }
+
+    node = SqlStdOperatorTable.IS_NULL.createCall(SqlParserPos.ZERO, node);
+    if (nullsFirst) {
+      node = SqlStdOperatorTable.DESC.createCall(SqlParserPos.ZERO, node);
+    }
+    return node;
+  }
+
   /**
    * Returns whether the dialect supports OFFSET/FETCH clauses
    * introduced by SQL:2008, for instance
@@ -667,15 +689,16 @@ public class SqlDialect {
    */
   public enum DatabaseProduct {
     ACCESS("Access", "\"", NullCollation.HIGH),
+    BIG_QUERY("Google BigQuery", "`", NullCollation.LOW),
     CALCITE("Apache Calcite", "\"", NullCollation.HIGH),
     MSSQL("Microsoft SQL Server", "[", NullCollation.HIGH),
-    MYSQL("MySQL", "`", NullCollation.HIGH),
+    MYSQL("MySQL", "`", NullCollation.LOW),
     ORACLE("Oracle", "\"", NullCollation.HIGH),
     DERBY("Apache Derby", null, NullCollation.HIGH),
     DB2("IBM DB2", null, NullCollation.HIGH),
     FIREBIRD("Firebird", null, NullCollation.HIGH),
     H2("H2", "\"", NullCollation.HIGH),
-    HIVE("Apache Hive", null, NullCollation.HIGH),
+    HIVE("Apache Hive", null, NullCollation.LOW),
     INFORMIX("Informix", null, NullCollation.HIGH),
     INGRES("Ingres", null, NullCollation.HIGH),
     LUCIDDB("LucidDB", "\"", NullCollation.HIGH),
@@ -760,6 +783,10 @@ public class SqlDialect {
     Context withDatabaseProductName(String databaseProductName);
     String databaseVersion();
     Context withDatabaseVersion(String databaseVersion);
+    int databaseMajorVersion();
+    Context withDatabaseMajorVersion(int databaseMajorVersion);
+    int databaseMinorVersion();
+    Context withDatabaseMinorVersion(int databaseMinorVersion);
     String identifierQuoteString();
     Context withIdentifierQuoteString(String identifierQuoteString);
     @Nonnull NullCollation nullCollation();
@@ -771,15 +798,20 @@ public class SqlDialect {
     private final DatabaseProduct databaseProduct;
     private final String databaseProductName;
     private final String databaseVersion;
+    private final int databaseMajorVersion;
+    private final int databaseMinorVersion;
     private final String identifierQuoteString;
     private final NullCollation nullCollation;
 
     private ContextImpl(DatabaseProduct databaseProduct,
         String databaseProductName, String databaseVersion,
+        int databaseMajorVersion, int databaseMinorVersion,
         String identifierQuoteString, NullCollation nullCollation) {
       this.databaseProduct = Preconditions.checkNotNull(databaseProduct);
       this.databaseProductName = databaseProductName;
       this.databaseVersion = databaseVersion;
+      this.databaseMajorVersion = databaseMajorVersion;
+      this.databaseMinorVersion = databaseMinorVersion;
       this.identifierQuoteString = identifierQuoteString;
       this.nullCollation = Preconditions.checkNotNull(nullCollation);
     }
@@ -791,7 +823,8 @@ public class SqlDialect {
     public Context withDatabaseProduct(
         @Nonnull DatabaseProduct databaseProduct) {
       return new ContextImpl(databaseProduct, databaseProductName,
-          databaseVersion, identifierQuoteString, nullCollation);
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation);
     }
 
     public String databaseProductName() {
@@ -800,7 +833,8 @@ public class SqlDialect {
 
     public Context withDatabaseProductName(String databaseProductName) {
       return new ContextImpl(databaseProduct, databaseProductName,
-          databaseVersion, identifierQuoteString, nullCollation);
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation);
     }
 
     public String databaseVersion() {
@@ -809,7 +843,28 @@ public class SqlDialect {
 
     public Context withDatabaseVersion(String databaseVersion) {
       return new ContextImpl(databaseProduct, databaseProductName,
-          databaseVersion, identifierQuoteString, nullCollation);
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation);
+    }
+
+    public int databaseMajorVersion() {
+      return databaseMajorVersion;
+    }
+
+    public Context withDatabaseMajorVersion(int databaseMajorVersion) {
+      return new ContextImpl(databaseProduct, databaseProductName,
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation);
+    }
+
+    public int databaseMinorVersion() {
+      return databaseMinorVersion;
+    }
+
+    public Context withDatabaseMinorVersion(int databaseMinorVersion) {
+      return new ContextImpl(databaseProduct, databaseProductName,
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation);
     }
 
     public String identifierQuoteString() {
@@ -818,7 +873,8 @@ public class SqlDialect {
 
     public Context withIdentifierQuoteString(String identifierQuoteString) {
       return new ContextImpl(databaseProduct, databaseProductName,
-          databaseVersion, identifierQuoteString, nullCollation);
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation);
     }
 
     @Nonnull public NullCollation nullCollation() {
@@ -827,7 +883,8 @@ public class SqlDialect {
 
     public Context withNullCollation(@Nonnull NullCollation nullCollation) {
       return new ContextImpl(databaseProduct, databaseProductName,
-          databaseVersion, identifierQuoteString, nullCollation);
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
index 0457735..c2bf4e3 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
@@ -54,10 +54,12 @@ import java.util.Locale;
 public class SqlDialectFactoryImpl implements SqlDialectFactory {
   public SqlDialect create(DatabaseMetaData databaseMetaData) {
     String databaseProductName;
-    String databaseVersion;
+    int databaseMajorVersion;
+    int databaseMinorVersion;
     try {
       databaseProductName = databaseMetaData.getDatabaseProductName();
-      databaseVersion = databaseMetaData.getDatabaseProductVersion();
+      databaseMajorVersion = databaseMetaData.getDatabaseMajorVersion();
+      databaseMinorVersion = databaseMetaData.getDatabaseMinorVersion();
     } catch (SQLException e) {
       throw new RuntimeException("while detecting database product", e);
     }
@@ -67,7 +69,8 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory {
     final NullCollation nullCollation = getNullCollation(databaseMetaData);
     final SqlDialect.Context c = SqlDialect.EMPTY_CONTEXT
         .withDatabaseProductName(databaseProductName)
-        .withDatabaseVersion(databaseVersion)
+        .withDatabaseMajorVersion(databaseMajorVersion)
+        .withDatabaseMinorVersion(databaseMinorVersion)
         .withIdentifierQuoteString(quoteString)
         .withNullCollation(nullCollation);
     switch (upperProductName) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java
new file mode 100644
index 0000000..b983005
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java
@@ -0,0 +1,39 @@
+/*
+ * 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.calcite.sql.dialect;
+
+import org.apache.calcite.config.NullCollation;
+import org.apache.calcite.sql.SqlDialect;
+
+/**
+ * A <code>SqlDialect</code> implementation for Google BigQuery's "Standard SQL"
+ * dialect.
+ */
+public class BigQuerySqlDialect extends SqlDialect {
+  public static final SqlDialect DEFAULT =
+      new BigQuerySqlDialect(
+          EMPTY_CONTEXT
+              .withDatabaseProduct(SqlDialect.DatabaseProduct.BIG_QUERY)
+              .withNullCollation(NullCollation.LOW));
+
+  /** Creates a BigQuerySqlDialect. */
+  public BigQuerySqlDialect(SqlDialect.Context context) {
+    super(context);
+  }
+}
+
+// End BigQuerySqlDialect.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java
index 52f0a81..506c40b 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java
@@ -16,19 +16,29 @@
  */
 package org.apache.calcite.sql.dialect;
 
+import org.apache.calcite.config.NullCollation;
 import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlNode;
 
 /**
  * A <code>SqlDialect</code> implementation for the Apache Hive database.
  */
 public class HiveSqlDialect extends SqlDialect {
   public static final SqlDialect DEFAULT =
-      new HiveSqlDialect(
-          EMPTY_CONTEXT.withDatabaseProduct(DatabaseProduct.HIVE));
+      new HiveSqlDialect(EMPTY_CONTEXT
+          .withDatabaseProduct(DatabaseProduct.HIVE)
+          .withNullCollation(NullCollation.LOW));
+
+  private final boolean emulateNullDirection;
 
   /** Creates a HiveSqlDialect. */
   public HiveSqlDialect(Context context) {
     super(context);
+    // Since 2.1.0, Hive natively supports "NULLS FIRST" and "NULLS LAST".
+    // See https://issues.apache.org/jira/browse/HIVE-12994.
+    emulateNullDirection = (context.databaseMajorVersion() < 2)
+        || (context.databaseMajorVersion() == 2
+            && context.databaseMinorVersion() < 1);
   }
 
   @Override protected boolean allowsAs() {
@@ -38,6 +48,15 @@ public class HiveSqlDialect extends SqlDialect {
   @Override public boolean supportsOffsetFetch() {
     return false;
   }
+
+  @Override public SqlNode emulateNullDirection(SqlNode node,
+      boolean nullsFirst, boolean desc) {
+    if (emulateNullDirection) {
+      return emulateNullDirectionWithIsNull(node, nullsFirst, desc);
+    }
+
+    return null;
+  }
 }
 
 // End HiveSqlDialect.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java
index e9ae479..4038f9e 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql.dialect;
 
 import org.apache.calcite.avatica.util.TimeUnitRange;
+import org.apache.calcite.config.NullCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlBasicCall;
 import org.apache.calcite.sql.SqlCall;
@@ -45,7 +46,8 @@ public class MysqlSqlDialect extends SqlDialect {
   public static final SqlDialect DEFAULT =
       new MysqlSqlDialect(EMPTY_CONTEXT
           .withDatabaseProduct(DatabaseProduct.MYSQL)
-          .withIdentifierQuoteString("`"));
+          .withIdentifierQuoteString("`")
+          .withNullCollation(NullCollation.LOW));
 
   /** MySQL specific function. */
   public static final SqlFunction ISNULL_FUNCTION =
@@ -66,12 +68,9 @@ public class MysqlSqlDialect extends SqlDialect {
     return false;
   }
 
-  @Override public SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst) {
-    node = ISNULL_FUNCTION.createCall(SqlParserPos.ZERO, node);
-    if (nullsFirst) {
-      node = SqlStdOperatorTable.DESC.createCall(SqlParserPos.ZERO, node);
-    }
-    return node;
+  @Override public SqlNode emulateNullDirection(SqlNode node,
+      boolean nullsFirst, boolean desc) {
+    return emulateNullDirectionWithIsNull(node, nullsFirst, desc);
   }
 
   @Override public boolean supportsAggregateFunction(SqlKind kind) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index a8682f8..d89df0b 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.rel.rel2sql;
 
+import org.apache.calcite.config.NullCollation;
 import org.apache.calcite.plan.RelOptLattice;
 import org.apache.calcite.plan.RelOptMaterialization;
 import org.apache.calcite.plan.RelOptPlanner;
@@ -30,6 +31,8 @@ import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.dialect.HiveSqlDialect;
+import org.apache.calcite.sql.dialect.MysqlSqlDialect;
 import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.calcite.test.CalciteAssert;
@@ -92,6 +95,13 @@ public class RelToSqlConverterTest {
     return Frameworks.getPlanner(config);
   }
 
+  private static MysqlSqlDialect mySqlDialect(NullCollation nullCollation) {
+    return new MysqlSqlDialect(SqlDialect.EMPTY_CONTEXT
+        .withDatabaseProduct(SqlDialect.DatabaseProduct.MYSQL)
+        .withIdentifierQuoteString("`")
+        .withNullCollation(nullCollation));
+  }
+
   @Test public void testSimpleSelectStarFromProductTable() {
     String query = "select * from \"product\"";
     sql(query).ok("SELECT *\nFROM \"foodmart\".\"product\"");
@@ -340,6 +350,222 @@ public class RelToSqlConverterTest {
     sql(query).withHive().ok(expected);
   }
 
+  @Test public void testHiveSelectQueryWithOrderByDescAndNullsFirstShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+    final String expected = "SELECT product_id\n"
+        + "FROM foodmart.product\n"
+        + "ORDER BY product_id IS NULL DESC, product_id DESC";
+    sql(query).dialect(HiveSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testHiveSelectQueryWithOrderByAscAndNullsLastShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls last";
+    final String expected = "SELECT product_id\n"
+        + "FROM foodmart.product\n"
+        + "ORDER BY product_id IS NULL, product_id";
+    sql(query).dialect(HiveSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testHiveSelectQueryWithOrderByAscNullsFirstShouldNotAddNullEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls first";
+    final String expected = "SELECT product_id\n"
+        + "FROM foodmart.product\n"
+        + "ORDER BY product_id";
+    sql(query).dialect(HiveSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testHiveSelectQueryWithOrderByDescNullsLastShouldNotAddNullEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls last";
+    final String expected = "SELECT product_id\n"
+        + "FROM foodmart.product\n"
+        + "ORDER BY product_id DESC";
+    sql(query).dialect(HiveSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testHiveSelectQueryWithOrderByDescAndHighNullsWithVersionGreaterThanOrEq21() {
+    final HiveSqlDialect hive2_1Dialect =
+        new HiveSqlDialect(SqlDialect.EMPTY_CONTEXT
+            .withDatabaseMajorVersion(2)
+            .withDatabaseMinorVersion(1)
+            .withNullCollation(NullCollation.LOW));
+
+    final HiveSqlDialect hive2_2_Dialect =
+        new HiveSqlDialect(SqlDialect.EMPTY_CONTEXT
+            .withDatabaseMajorVersion(2)
+            .withDatabaseMinorVersion(2)
+            .withNullCollation(NullCollation.LOW));
+
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+    final String expected = "SELECT product_id\n"
+        + "FROM foodmart.product\n"
+        + "ORDER BY product_id DESC NULLS FIRST";
+    sql(query).dialect(hive2_1Dialect).ok(expected);
+    sql(query).dialect(hive2_2_Dialect).ok(expected);
+  }
+
+  @Test public void testHiveSelectQueryWithOrderByDescAndHighNullsWithVersion20() {
+    final HiveSqlDialect hive2_1_0_Dialect =
+        new HiveSqlDialect(SqlDialect.EMPTY_CONTEXT
+            .withDatabaseMajorVersion(2)
+            .withDatabaseMinorVersion(0)
+            .withNullCollation(NullCollation.LOW));
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+    final String expected = "SELECT product_id\n"
+        + "FROM foodmart.product\n"
+        + "ORDER BY product_id IS NULL DESC, product_id DESC";
+    sql(query).dialect(hive2_1_0_Dialect).ok(expected);
+  }
+
+  @Test public void testMySqlSelectQueryWithOrderByDescAndNullsFirstShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL DESC, `product_id` DESC";
+    sql(query).dialect(MysqlSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testMySqlSelectQueryWithOrderByAscAndNullsLastShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL, `product_id`";
+    sql(query).dialect(MysqlSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testMySqlSelectQueryWithOrderByAscNullsFirstShouldNotAddNullEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id`";
+    sql(query).dialect(MysqlSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testMySqlSelectQueryWithOrderByDescNullsLastShouldNotAddNullEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` DESC";
+    sql(query).dialect(MysqlSqlDialect.DEFAULT).ok(expected);
+  }
+
+  @Test public void testMySqlWithHighNullsSelectWithOrderByAscNullsLastAndNoEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id`";
+    sql(query).dialect(mySqlDialect(NullCollation.HIGH)).ok(expected);
+  }
+
+  @Test public void testMySqlWithHighNullsSelectWithOrderByAscNullsFirstAndNullEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL DESC, `product_id`";
+    sql(query).dialect(mySqlDialect(NullCollation.HIGH)).ok(expected);
+  }
+
+  @Test public void testMySqlWithHighNullsSelectWithOrderByDescNullsFirstAndNoEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` DESC";
+    sql(query).dialect(mySqlDialect(NullCollation.HIGH)).ok(expected);
+  }
+
+  @Test public void testMySqlWithHighNullsSelectWithOrderByDescNullsLastAndNullEmulation() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL, `product_id` DESC";
+    sql(query).dialect(mySqlDialect(NullCollation.HIGH)).ok(expected);
+  }
+
+  @Test public void testMySqlWithFirstNullsSelectWithOrderByDescAndNullsFirstShouldNotBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` DESC";
+    sql(query).dialect(mySqlDialect(NullCollation.FIRST)).ok(expected);
+  }
+
+  @Test public void testMySqlWithFirstNullsSelectWithOrderByAscAndNullsFirstShouldNotBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id`";
+    sql(query).dialect(mySqlDialect(NullCollation.FIRST)).ok(expected);
+  }
+
+  @Test public void testMySqlWithFirstNullsSelectWithOrderByDescAndNullsLastShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL, `product_id` DESC";
+    sql(query).dialect(mySqlDialect(NullCollation.FIRST)).ok(expected);
+  }
+
+  @Test public void testMySqlWithFirstNullsSelectWithOrderByAscAndNullsLastShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL, `product_id`";
+    sql(query).dialect(mySqlDialect(NullCollation.FIRST)).ok(expected);
+  }
+
+  @Test public void testMySqlWithLastNullsSelectWithOrderByDescAndNullsFirstShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL DESC, `product_id` DESC";
+    sql(query).dialect(mySqlDialect(NullCollation.LAST)).ok(expected);
+  }
+
+  @Test public void testMySqlWithLastNullsSelectWithOrderByAscAndNullsFirstShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls first";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` IS NULL DESC, `product_id`";
+    sql(query).dialect(mySqlDialect(NullCollation.LAST)).ok(expected);
+  }
+
+  @Test public void testMySqlWithLastNullsSelectWithOrderByDescAndNullsLastShouldNotBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id` DESC";
+    sql(query).dialect(mySqlDialect(NullCollation.LAST)).ok(expected);
+  }
+
+  @Test public void testMySqlWithLastNullsSelectWithOrderByAscAndNullsLastShouldNotBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" nulls last";
+    final String expected = "SELECT `product_id`\n"
+        + "FROM `foodmart`.`product`\n"
+        + "ORDER BY `product_id`";
+    sql(query).dialect(mySqlDialect(NullCollation.LAST)).ok(expected);
+  }
+
   @Test public void testSelectQueryWithLimitClauseWithoutOrder() {
     String query = "select \"product_id\"  from \"product\" limit 100 offset 10";
     final String expected = "SELECT \"product_id\"\n"

http://git-wip-us.apache.org/repos/asf/calcite/blob/45425103/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 4c0975c..af69242 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -2853,11 +2853,26 @@ public class JdbcTest {
             + "customer_id=1; postal_code=15057\n");
   }
 
+  /** Tests ORDER BY with all combinations of ASC, DESC, NULLS FIRST,
+   * NULLS LAST. */
+  @Test public void testOrderByNulls() {
+    checkOrderByNulls(CalciteAssert.Config.FOODMART_CLONE);
+    checkOrderByNulls(CalciteAssert.Config.JDBC_FOODMART);
+  }
+
+  private void checkOrderByNulls(CalciteAssert.Config clone) {
+    checkOrderByDescNullsFirst(clone);
+    checkOrderByNullsFirst(clone);
+    checkOrderByDescNullsLast(clone);
+    checkOrderByNullsLast(clone);
+  }
+
   /** Tests ORDER BY ... DESC NULLS FIRST. */
-  @Test public void testOrderByDescNullsFirst() {
+  private void checkOrderByDescNullsFirst(CalciteAssert.Config config) {
     CalciteAssert.that()
-        .with(CalciteAssert.Config.FOODMART_CLONE)
-        .query("select \"store_id\", \"grocery_sqft\" from \"store\"\n"
+        .with(config)
+        .query("select \"store_id\", \"grocery_sqft\"\n"
+            + "from \"foodmart\".\"store\"\n"
             + "where \"store_id\" < 3 order by 2 desc nulls first")
         .returns("store_id=0; grocery_sqft=null\n"
             + "store_id=2; grocery_sqft=22271\n"
@@ -2865,10 +2880,11 @@ public class JdbcTest {
   }
 
   /** Tests ORDER BY ... NULLS FIRST. */
-  @Test public void testOrderByNullsFirst() {
+  private void checkOrderByNullsFirst(CalciteAssert.Config config) {
     CalciteAssert.that()
-        .with(CalciteAssert.Config.FOODMART_CLONE)
-        .query("select \"store_id\", \"grocery_sqft\" from \"store\"\n"
+        .with(config)
+        .query("select \"store_id\", \"grocery_sqft\"\n"
+            + "from \"foodmart\".\"store\"\n"
             + "where \"store_id\" < 3 order by 2 nulls first")
         .returns("store_id=0; grocery_sqft=null\n"
             + "store_id=1; grocery_sqft=17475\n"
@@ -2876,10 +2892,11 @@ public class JdbcTest {
   }
 
   /** Tests ORDER BY ... DESC NULLS LAST. */
-  @Test public void testOrderByDescNullsLast() {
+  private void checkOrderByDescNullsLast(CalciteAssert.Config config) {
     CalciteAssert.that()
-        .with(CalciteAssert.Config.FOODMART_CLONE)
-        .query("select \"store_id\", \"grocery_sqft\" from \"store\"\n"
+        .with(config)
+        .query("select \"store_id\", \"grocery_sqft\"\n"
+            + "from \"foodmart\".\"store\"\n"
             + "where \"store_id\" < 3 order by 2 desc nulls last")
         .returns("store_id=2; grocery_sqft=22271\n"
             + "store_id=1; grocery_sqft=17475\n"
@@ -2887,10 +2904,11 @@ public class JdbcTest {
   }
 
   /** Tests ORDER BY ... NULLS LAST. */
-  @Test public void testOrderByNullsLast() {
+  private void checkOrderByNullsLast(CalciteAssert.Config config) {
     CalciteAssert.that()
-        .with(CalciteAssert.Config.FOODMART_CLONE)
-        .query("select \"store_id\", \"grocery_sqft\" from \"store\"\n"
+        .with(config)
+        .query("select \"store_id\", \"grocery_sqft\"\n"
+            + "from \"foodmart\".\"store\"\n"
             + "where \"store_id\" < 3 order by 2 nulls last")
         .returns("store_id=1; grocery_sqft=17475\n"
             + "store_id=2; grocery_sqft=22271\n"


[4/5] calcite git commit: [CALCITE-1867] Allow user-defined grouped window functions (Timo Walther)

Posted by jh...@apache.org.
[CALCITE-1867] Allow user-defined grouped window functions (Timo Walther)

Rename SqlGroupFunction to SqlGroupedWindowFunction.

Close apache/calcite#549


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

Branch: refs/heads/master
Commit: 814de2327d3255888a07e1940dbf7dd7bd340b19
Parents: 61f1258
Author: twalthr <tw...@apache.org>
Authored: Tue Oct 17 11:30:10 2017 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 2 15:04:59 2017 -0700

----------------------------------------------------------------------
 .../calcite/sql/fun/SqlGroupFunction.java       |  99 ---------------
 .../sql/fun/SqlGroupedWindowFunction.java       | 124 +++++++++++++++++++
 .../calcite/sql/fun/SqlStdOperatorTable.java    |  42 +++----
 3 files changed, 145 insertions(+), 120 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/814de232/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java
deleted file mode 100644
index d44267a..0000000
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.calcite.sql.fun;
-
-import org.apache.calcite.sql.SqlFunction;
-import org.apache.calcite.sql.SqlFunctionCategory;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlOperatorBinding;
-import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlOperandTypeChecker;
-import org.apache.calcite.sql.validate.SqlMonotonicity;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-
-/**
- * SQL function that computes keys by which rows can be partitioned and
- * aggregated.
- *
- * <p>Grouped window functions always occur in the GROUP BY clause. They often
- * have auxiliary functions that access information about the group. For
- * example, {@code HOP} is a group function, and its auxiliary functions are
- * {@code HOP_START} and {@code HOP_END}. Here they are used in a streaming
- * query:
- *
- * <blockquote><pre>
- * SELECT STREAM HOP_START(rowtime, INTERVAL '1' HOUR),
- *   HOP_END(rowtime, INTERVAL '1' HOUR),
- *   MIN(unitPrice)
- * FROM Orders
- * GROUP BY HOP(rowtime, INTERVAL '1' HOUR), productId
- * </pre></blockquote>
- */
-class SqlGroupFunction extends SqlFunction {
-  /** The grouped function, if this an auxiliary function; null otherwise. */
-  final SqlGroupFunction groupFunction;
-
-  /** Creates a SqlGroupFunction.
-   *
-   * @param kind Kind; also determines function name
-   * @param groupFunction Group function, if this is an auxiliary;
-   *                      null, if this is a group function
-   * @param operandTypeChecker Operand type checker
-   */
-  SqlGroupFunction(SqlKind kind, SqlGroupFunction groupFunction,
-      SqlOperandTypeChecker operandTypeChecker) {
-    super(kind.name(), kind, ReturnTypes.ARG0, null,
-        operandTypeChecker, SqlFunctionCategory.SYSTEM);
-    this.groupFunction = groupFunction;
-    if (groupFunction != null) {
-      assert groupFunction.groupFunction == null;
-    }
-  }
-
-  /** Creates an auxiliary function from this grouped window function. */
-  SqlGroupFunction auxiliary(SqlKind kind) {
-    return new SqlGroupFunction(kind, this, getOperandTypeChecker());
-  }
-
-  /** Returns a list of this grouped window function's auxiliary functions. */
-  List<SqlGroupFunction> getAuxiliaryFunctions() {
-    return ImmutableList.of();
-  }
-
-  @Override public boolean isGroup() {
-    // Auxiliary functions are not group functions
-    return groupFunction == null;
-  }
-
-  @Override public boolean isGroupAuxiliary() {
-    return groupFunction != null;
-  }
-
-  @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
-    // Monotonic iff its first argument is, but not strict.
-    //
-    // Note: This strategy happens to works for all current group functions
-    // (HOP, TUMBLE, SESSION). When there are exceptions to this rule, we'll
-    // make the method abstract.
-    return call.getOperandMonotonicity(0).unstrict();
-  }
-}
-
-// End SqlGroupFunction.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/814de232/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupedWindowFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupedWindowFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupedWindowFunction.java
new file mode 100644
index 0000000..8226e95
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlGroupedWindowFunction.java
@@ -0,0 +1,124 @@
+/*
+ * 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.calcite.sql.fun;
+
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperatorBinding;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.validate.SqlMonotonicity;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * SQL function that computes keys by which rows can be partitioned and
+ * aggregated.
+ *
+ * <p>Grouped window functions always occur in the GROUP BY clause. They often
+ * have auxiliary functions that access information about the group. For
+ * example, {@code HOP} is a group function, and its auxiliary functions are
+ * {@code HOP_START} and {@code HOP_END}. Here they are used in a streaming
+ * query:
+ *
+ * <blockquote><pre>
+ * SELECT STREAM HOP_START(rowtime, INTERVAL '1' HOUR),
+ *   HOP_END(rowtime, INTERVAL '1' HOUR),
+ *   MIN(unitPrice)
+ * FROM Orders
+ * GROUP BY HOP(rowtime, INTERVAL '1' HOUR), productId
+ * </pre></blockquote>
+ */
+public class SqlGroupedWindowFunction extends SqlFunction {
+  /** The grouped function, if this an auxiliary function; null otherwise. */
+  final SqlGroupedWindowFunction groupFunction;
+
+  /** Creates a SqlGroupedWindowFunction.
+   *
+   * @param name Function name
+   * @param kind Kind
+   * @param groupFunction Group function, if this is an auxiliary;
+   *                      null, if this is a group function
+   * @param operandTypeChecker Operand type checker
+   */
+  public SqlGroupedWindowFunction(String name, SqlKind kind, SqlGroupedWindowFunction groupFunction,
+      SqlOperandTypeChecker operandTypeChecker) {
+    super(name, kind, ReturnTypes.ARG0, null,
+        operandTypeChecker, SqlFunctionCategory.SYSTEM);
+    this.groupFunction = groupFunction;
+    if (groupFunction != null) {
+      assert groupFunction.groupFunction == null;
+    }
+  }
+
+  /** Creates a SqlGroupedWindowFunction.
+   *
+   * @param kind Kind; also determines function name
+   * @param groupFunction Group function, if this is an auxiliary;
+   *                      null, if this is a group function
+   * @param operandTypeChecker Operand type checker
+   */
+  public SqlGroupedWindowFunction(SqlKind kind, SqlGroupedWindowFunction groupFunction,
+      SqlOperandTypeChecker operandTypeChecker) {
+    this(kind.name(), kind, groupFunction, operandTypeChecker);
+  }
+
+  /** Creates an auxiliary function from this grouped window function.
+   *
+   * @param kind Kind; also determines function name
+   */
+  public SqlGroupedWindowFunction auxiliary(SqlKind kind) {
+    return auxiliary(kind.name(), kind);
+  }
+
+  /** Creates an auxiliary function from this grouped window function.
+   *
+   * @param name Function name
+   * @param kind Kind
+   */
+  public SqlGroupedWindowFunction auxiliary(String name, SqlKind kind) {
+    return new SqlGroupedWindowFunction(name, kind, this, getOperandTypeChecker());
+  }
+
+  /** Returns a list of this grouped window function's auxiliary functions. */
+  public List<SqlGroupedWindowFunction> getAuxiliaryFunctions() {
+    return ImmutableList.of();
+  }
+
+  @Override public boolean isGroup() {
+    // Auxiliary functions are not group functions
+    return groupFunction == null;
+  }
+
+  @Override public boolean isGroupAuxiliary() {
+    return groupFunction != null;
+  }
+
+  @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
+    // Monotonic iff its first argument is, but not strict.
+    //
+    // Note: This strategy happens to works for all current group functions
+    // (HOP, TUMBLE, SESSION). When there are exceptions to this rule, we'll
+    // make the method abstract.
+    return call.getOperandMonotonicity(0).unstrict();
+  }
+}
+
+// End SqlGroupedWindowFunction.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/814de232/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index c8adf5e..aafc943 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -2006,63 +2006,63 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
       };
 
   /** The {@code TUMBLE} group function. */
-  public static final SqlGroupFunction TUMBLE =
-      new SqlGroupFunction(SqlKind.TUMBLE, null,
+  public static final SqlGroupedWindowFunction TUMBLE =
+      new SqlGroupedWindowFunction(SqlKind.TUMBLE, null,
           OperandTypes.or(OperandTypes.DATETIME_INTERVAL,
               OperandTypes.DATETIME_INTERVAL_TIME)) {
-        @Override List<SqlGroupFunction> getAuxiliaryFunctions() {
+        @Override public List<SqlGroupedWindowFunction> getAuxiliaryFunctions() {
           return ImmutableList.of(TUMBLE_START, TUMBLE_END);
         }
       };
 
   /** The {@code TUMBLE_START} auxiliary function of
    * the {@code TUMBLE} group function. */
-  public static final SqlGroupFunction TUMBLE_START =
+  public static final SqlGroupedWindowFunction TUMBLE_START =
       TUMBLE.auxiliary(SqlKind.TUMBLE_START);
 
   /** The {@code TUMBLE_END} auxiliary function of
    * the {@code TUMBLE} group function. */
-  public static final SqlGroupFunction TUMBLE_END =
+  public static final SqlGroupedWindowFunction TUMBLE_END =
       TUMBLE.auxiliary(SqlKind.TUMBLE_END);
 
   /** The {@code HOP} group function. */
-  public static final SqlGroupFunction HOP =
-      new SqlGroupFunction(SqlKind.HOP, null,
+  public static final SqlGroupedWindowFunction HOP =
+      new SqlGroupedWindowFunction(SqlKind.HOP, null,
           OperandTypes.or(OperandTypes.DATETIME_INTERVAL_INTERVAL,
               OperandTypes.DATETIME_INTERVAL_INTERVAL_TIME)) {
-        @Override List<SqlGroupFunction> getAuxiliaryFunctions() {
+        @Override public List<SqlGroupedWindowFunction> getAuxiliaryFunctions() {
           return ImmutableList.of(HOP_START, HOP_END);
         }
       };
 
   /** The {@code HOP_START} auxiliary function of
    * the {@code HOP} group function. */
-  public static final SqlGroupFunction HOP_START =
+  public static final SqlGroupedWindowFunction HOP_START =
       HOP.auxiliary(SqlKind.HOP_START);
 
   /** The {@code HOP_END} auxiliary function of
    * the {@code HOP} group function. */
-  public static final SqlGroupFunction HOP_END =
+  public static final SqlGroupedWindowFunction HOP_END =
       HOP.auxiliary(SqlKind.HOP_END);
 
   /** The {@code SESSION} group function. */
-  public static final SqlGroupFunction SESSION =
-      new SqlGroupFunction(SqlKind.SESSION, null,
+  public static final SqlGroupedWindowFunction SESSION =
+      new SqlGroupedWindowFunction(SqlKind.SESSION, null,
           OperandTypes.or(OperandTypes.DATETIME_INTERVAL,
               OperandTypes.DATETIME_INTERVAL_TIME)) {
-        @Override List<SqlGroupFunction> getAuxiliaryFunctions() {
+        @Override public List<SqlGroupedWindowFunction> getAuxiliaryFunctions() {
           return ImmutableList.of(SESSION_START, SESSION_END);
         }
       };
 
   /** The {@code SESSION_START} auxiliary function of
    * the {@code SESSION} group function. */
-  public static final SqlGroupFunction SESSION_START =
+  public static final SqlGroupedWindowFunction SESSION_START =
       SESSION.auxiliary(SqlKind.SESSION_START);
 
   /** The {@code SESSION_END} auxiliary function of
    * the {@code SESSION} group function. */
-  public static final SqlGroupFunction SESSION_END =
+  public static final SqlGroupedWindowFunction SESSION_END =
       SESSION.auxiliary(SqlKind.SESSION_END);
 
   /** {@code |} operator to create alternate patterns
@@ -2178,7 +2178,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
 
   /** Returns the group function for which a given kind is an auxiliary
    * function, or null if it is not an auxiliary function. */
-  public static SqlGroupFunction auxiliaryToGroup(SqlKind kind) {
+  public static SqlGroupedWindowFunction auxiliaryToGroup(SqlKind kind) {
     switch (kind) {
     case TUMBLE_START:
     case TUMBLE_END:
@@ -2201,9 +2201,9 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
    * to {@code TUMBLE(rowtime, INTERVAL '1' HOUR))}. */
   public static SqlCall convertAuxiliaryToGroupCall(SqlCall call) {
     final SqlOperator op = call.getOperator();
-    if (op instanceof SqlGroupFunction
+    if (op instanceof SqlGroupedWindowFunction
         && op.isGroupAuxiliary()) {
-      return copy(call, ((SqlGroupFunction) op).groupFunction);
+      return copy(call, ((SqlGroupedWindowFunction) op).groupFunction);
     }
     return null;
   }
@@ -2216,12 +2216,12 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
   public static List<Pair<SqlNode, AuxiliaryConverter>> convertGroupToAuxiliaryCalls(
       SqlCall call) {
     final SqlOperator op = call.getOperator();
-    if (op instanceof SqlGroupFunction
+    if (op instanceof SqlGroupedWindowFunction
         && op.isGroup()) {
       ImmutableList.Builder<Pair<SqlNode, AuxiliaryConverter>> builder =
           ImmutableList.builder();
-      for (final SqlGroupFunction f
-          : ((SqlGroupFunction) op).getAuxiliaryFunctions()) {
+      for (final SqlGroupedWindowFunction f
+          : ((SqlGroupedWindowFunction) op).getAuxiliaryFunctions()) {
         builder.add(
             Pair.<SqlNode, AuxiliaryConverter>of(copy(call, f),
                 new AuxiliaryConverter.Impl(f)));


[3/5] calcite git commit: [CALCITE-2029] Query with IS DISTINCT FROM condition in WHERE or JOIN clause fails with AssertionError, "Cast for just nullability not allowed" (Volodymyr Vysotskyi)

Posted by jh...@apache.org.
[CALCITE-2029] Query with IS DISTINCT FROM condition in WHERE or JOIN clause fails with AssertionError, "Cast for just nullability not allowed" (Volodymyr Vysotskyi)

Move fix from Filter constructor to IS DISTINCT FROM convertlet (Julian Hyde)

Close apache/calcite#554


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

Branch: refs/heads/master
Commit: 61f1258c678d40d1041d89bdfec58b665a36fb6b
Parents: 67bd544
Author: Volodymyr Vysotskyi <vv...@gmail.com>
Authored: Thu Nov 2 14:08:39 2017 +0000
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 2 15:01:51 2017 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/plan/RelOptUtil.java     | 12 +++++----
 .../java/org/apache/calcite/rex/RexBuilder.java | 14 ++++++++++
 .../java/org/apache/calcite/test/JdbcTest.java  | 28 ++++++++++++++++++++
 .../calcite/test/SqlToRelConverterTest.java     |  8 ++++--
 .../org/apache/calcite/test/RelOptRulesTest.xml |  2 +-
 .../calcite/test/SqlToRelConverterTest.xml      | 24 ++++++++++++-----
 6 files changed, 74 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/61f1258c/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index 238cf3c..625fc73 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -1920,10 +1920,8 @@ public abstract class RelOptUtil {
 
     // The result of IS DISTINCT FROM is NOT NULL because it can
     // only return TRUE or FALSE.
-    ret =
-        rexBuilder.makeCast(
-            rexBuilder.getTypeFactory().createSqlType(SqlTypeName.BOOLEAN),
-            ret);
+    assert ret != null;
+    assert !ret.getType().isNullable();
 
     return ret;
   }
@@ -1942,6 +1940,8 @@ public abstract class RelOptUtil {
       nullOp = SqlStdOperatorTable.IS_NOT_NULL;
       eqOp = SqlStdOperatorTable.NOT_EQUALS;
     }
+    // By the time the ELSE is reached, x and y are known to be not null;
+    // therefore the whole CASE is not null.
     RexNode[] whenThenElse = {
         // when x is null
         rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, x),
@@ -1956,7 +1956,9 @@ public abstract class RelOptUtil {
         rexBuilder.makeCall(nullOp, x),
 
         // else return x compared to y
-        rexBuilder.makeCall(eqOp, x, y)
+        rexBuilder.makeCall(eqOp,
+            rexBuilder.makeNotNull(x),
+            rexBuilder.makeNotNull(y))
     };
     return rexBuilder.makeCall(
         SqlStdOperatorTable.CASE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/61f1258c/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index bd6579d..a04cbf0 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -748,6 +748,20 @@ public class RexBuilder {
   }
 
   /**
+   * Makes a cast of a value to NOT NULL;
+   * no-op if the type already has NOT NULL.
+   */
+  public RexNode makeNotNull(RexNode exp) {
+    final RelDataType type = exp.getType();
+    if (!type.isNullable()) {
+      return exp;
+    }
+    final RelDataType notNullType =
+        typeFactory.createTypeWithNullability(type, false);
+    return makeAbstractCast(notNullType, exp);
+  }
+
+  /**
    * Creates a reference to all the fields in the row. That is, the whole row
    * as a single record object.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/61f1258c/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index af69242..8760d82 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -1589,6 +1589,34 @@ public class JdbcTest {
             + "full_name=Terry Anderson\n");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-2029">[CALCITE-2029]
+   * Query with "is distinct from" condition in where or join clause fails
+   * with AssertionError: Cast for just nullability not allowed</a>. */
+  @Test public void testIsNotDistinctInFilter() {
+    CalciteAssert.that()
+      .with(CalciteAssert.Config.JDBC_FOODMART)
+      .query("select *\n"
+          + "  from \"foodmart\".\"employee\" as e1\n"
+          + "  where e1.\"last_name\" is distinct from e1.\"last_name\"")
+      .runs();
+  }
+
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-2029">[CALCITE-2029]
+   * Query with "is distinct from" condition in where or join clause fails
+   * with AssertionError: Cast for just nullability not allowed</a>. */
+  @Test public void testMixedEqualAndIsNotDistinctJoin() {
+    CalciteAssert.that()
+      .with(CalciteAssert.Config.JDBC_FOODMART)
+      .query("select *\n"
+          + "  from \"foodmart\".\"employee\" as e1\n"
+          + "  join \"foodmart\".\"employee\" as e2 on\n"
+          + "  e1.\"first_name\" = e1.\"first_name\"\n"
+          + "  and e1.\"last_name\" is distinct from e2.\"last_name\"")
+      .runs();
+  }
+
   /** A join that has both equi and non-equi conditions.
    *
    * <p>Test case for

http://git-wip-us.apache.org/repos/asf/calcite/blob/61f1258c/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index f0276f4..aebcece 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -1442,12 +1442,16 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
   }
 
   @Test public void testIsDistinctFrom() {
-    final String sql = "select 1 is distinct from 2 from (values(true))";
+    final String sql = "select empno is distinct from deptno\n"
+        + "from (values (cast(null as int), 1),\n"
+        + "             (2, cast(null as int))) as emp(empno, deptno)";
     sql(sql).ok();
   }
 
   @Test public void testIsNotDistinctFrom() {
-    final String sql = "select 1 is not distinct from 2 from (values(true))";
+    final String sql = "select empno is not distinct from deptno\n"
+        + "from (values (cast(null as int), 1),\n"
+        + "             (2, cast(null as int))) as emp(empno, deptno)";
     sql(sql).ok();
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/61f1258c/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 596f730..cccedb0 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -2036,7 +2036,7 @@ LogicalCalc(expr#0=[{inputs}], expr#1=['TABLE        '], expr#2=['t'], U=[$t1],
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
-LogicalProject(EXPR$0=[CAST(CASE(IS NULL($1), IS NULL($0), IS NULL($0), IS NULL($1), =($1, $0))):BOOLEAN NOT NULL])
+LogicalProject(EXPR$0=[CASE(IS NULL($1), IS NULL($0), =(CAST($1):INTEGER NOT NULL, $0))])
   LogicalProject(EXPR$0=[2], EXPR$1=[null])
     LogicalValues(tuples=[[{ 0 }]])
 ]]>

http://git-wip-us.apache.org/repos/asf/calcite/blob/61f1258c/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index dd944d1..6818e63 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -626,23 +626,35 @@ LogicalProject(DEPTNO=[$7])
     <TestCase name="testIsDistinctFrom">
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EXPR$0=[CAST(CASE(IS NULL(1), IS NOT NULL(2), IS NULL(2), IS NOT NULL(1), <>(1, 2))):BOOLEAN NOT NULL])
-  LogicalValues(tuples=[[{ true }]])
+LogicalProject(EXPR$0=[CASE(IS NULL($0), IS NOT NULL($1), IS NULL($1), IS NOT NULL($0), <>(CAST($0):INTEGER NOT NULL, CAST($1):INTEGER NOT NULL))])
+  LogicalUnion(all=[true])
+    LogicalProject(EXPR$0=[null], EXPR$1=[1])
+      LogicalValues(tuples=[[{ 0 }]])
+    LogicalProject(EXPR$0=[2], EXPR$1=[null])
+      LogicalValues(tuples=[[{ 0 }]])
 ]]>
         </Resource>
         <Resource name="sql">
-            <![CDATA[select 1 is distinct from 2 from (values(true))]]>
+            <![CDATA[select empno is distinct from deptno
+from (values (cast(null as int), 1),
+              (2, cast(null as int))) as emp(empno, deptno)]]>
         </Resource>
     </TestCase>
     <TestCase name="testIsNotDistinctFrom">
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EXPR$0=[CAST(CASE(IS NULL(1), IS NULL(2), IS NULL(2), IS NULL(1), =(1, 2))):BOOLEAN NOT NULL])
-  LogicalValues(tuples=[[{ true }]])
+LogicalProject(EXPR$0=[CASE(IS NULL($0), IS NULL($1), IS NULL($1), IS NULL($0), =(CAST($0):INTEGER NOT NULL, CAST($1):INTEGER NOT NULL))])
+  LogicalUnion(all=[true])
+    LogicalProject(EXPR$0=[null], EXPR$1=[1])
+      LogicalValues(tuples=[[{ 0 }]])
+    LogicalProject(EXPR$0=[2], EXPR$1=[null])
+      LogicalValues(tuples=[[{ 0 }]])
 ]]>
         </Resource>
         <Resource name="sql">
-            <![CDATA[select 1 is not distinct from 2 from (values(true))]]>
+            <![CDATA[select empno is not distinct from deptno
+from (values (cast(null as int), 1),
+             (2, cast(null as int))) as emp(empno, deptno)]]>
         </Resource>
     </TestCase>
     <TestCase name="testNotLike">


[5/5] calcite git commit: [CALCITE-2021] Document the interfaces that you can use to extend Calcite

Posted by jh...@apache.org.
[CALCITE-2021] Document the interfaces that you can use to extend Calcite

Document the APIs that people can use to extend Calcite. Examples
include user-defined functions (UDFs), adapters (aka schema
factories), planner rules and metadata.

The tutorial currently tries to cover how to use those extensions, but
there are many incomplete sections. We should remove those sections and a one or
two paragraph description of each extension point in the adapters page. Later we
can add to the tutorial.

Include grouped window functions, per [CALCITE-1867].


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

Branch: refs/heads/master
Commit: 2f0eecb3cbb39caeaf333b16a4e2a8a594c26da3
Parents: 814de23
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Oct 23 18:24:28 2017 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 2 15:05:04 2017 -0700

----------------------------------------------------------------------
 site/_docs/adapter.md  | 407 +++++++++++++++++++++++++++++++++++++++++++-
 site/_docs/tutorial.md |  41 +----
 2 files changed, 406 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/2f0eecb3/site/_docs/adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/adapter.md b/site/_docs/adapter.md
index 2a4695b..52305f9 100644
--- a/site/_docs/adapter.md
+++ b/site/_docs/adapter.md
@@ -92,15 +92,15 @@ as implemented by Avatica's
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#LEX">lex</a> | Lexical policy. Values are ORACLE (default), MYSQL, MYSQL_ANSI, SQL_SERVER, JAVA.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#MATERIALIZATIONS_ENABLED">materializationsEnabled</a> | Whether Calcite should use materializations. Default false.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#MODEL">model</a> | URI of the JSON model file.
-| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#PARSER_FACTORY">parserFactory</a> | Parser factory. The name of a class that implements <a href="{{ site.apiRoot }}/org/apache/calcite/sql/parser/SqlParserImplFactory.html">SqlParserImplFactory</a> and has a public default constructor or an `INSTANCE` constant.
+| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#PARSER_FACTORY">parserFactory</a> | Parser factory. The name of a class that implements [<tt>interface SqlParserImplFactory</tt>]({{ site.apiRoot }}/org/apache/calcite/sql/parser/SqlParserImplFactory.html) and has a public default constructor or an `INSTANCE` constant.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#QUOTING">quoting</a> | How identifiers are quoted. Values are DOUBLE_QUOTE, BACK_QUOTE, BRACKET. If not specified, value from `lex` is used.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#QUOTED_CASING">quotedCasing</a> | How identifiers are stored if they are quoted. Values are UNCHANGED, TO_UPPER, TO_LOWER. If not specified, value from `lex` is used.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#SCHEMA">schema</a> | Name of initial schema.
-| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#SCHEMA_FACTORY">schemaFactory</a> | Schema factory. The name of a class that implements <a href="{{ site.apiRoot }}/org/apache/calcite/schema/SchemaFactory.html">SchemaFactory</a> and has a public default constructor or an `INSTANCE` constant. Ignored if `model` is specified.
+| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#SCHEMA_FACTORY">schemaFactory</a> | Schema factory. The name of a class that implements [<tt>interface SchemaFactory</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/SchemaFactory.html) and has a public default constructor or an `INSTANCE` constant. Ignored if `model` is specified.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#SCHEMA_TYPE">schemaType</a> | Schema type. Value must be "MAP" (the default), "JDBC", or "CUSTOM" (implicit if `schemaFactory` is specified). Ignored if `model` is specified.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#SPARK">spark</a> | Specifies whether Spark should be used as the engine for processing that cannot be pushed to the source system. If false (the default), Calcite generates code that implements the Enumerable interface.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#TIME_ZONE">timeZone</a> | Time zone, for example "gmt-3". Default is the JVM's time zone.
-| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#TYPE_SYSTEM">typeSystem</a> | Type system. The name of a class that implements <a href="{{ site.apiRoot }}/org/apache/calcite/rel/type/RelDataTypeSystem.html">RelDataTypeSystem</a> and has a public default constructor or an `INSTANCE` constant.
+| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#TYPE_SYSTEM">typeSystem</a> | Type system. The name of a class that implements [<tt>interface RelDataTypeSystem</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/type/RelDataTypeSystem.html) and has a public default constructor or an `INSTANCE` constant.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#UNQUOTED_CASING">unquotedCasing</a> | How identifiers are stored if they are not quoted. Values are UNCHANGED, TO_UPPER, TO_LOWER. If not specified, value from `lex` is used.
 
 To make a connection to a single schema based on a built-in schema type, you don't need to specify
@@ -136,3 +136,404 @@ makes a connection to the Cassandra adapter, equivalent to writing the following
 {% endhighlight %}
 
 Note how each key in the `operand` section appears with a `schema.` prefix in the connect string.
+
+## Extensibility
+
+There are many other APIs that allow you to extend Calcite's capabilities.
+
+In this section, we briefly describe those APIs, to give you an idea what is
+possible. To fully use these APIs you will need to read other documentation
+such as the javadoc for the interfaces, and possibly seek out the tests that
+we have written for them.
+
+### Functions and operators
+
+There are several ways to add operators or functions to Calcite.
+We'll describe the simplest (and least powerful) first.
+
+*User-defined functions* are the simplest (but least powerful).
+They are straightforward to write (you just write a Java class and register it
+in your schema) but do not offer much flexibility in the number and type of
+arguments, resolving overloaded functions, or deriving the return type.
+
+It you want that flexibility, you probably need to write you a
+*user-defined operator*
+(see [<tt>interface SqlOperator</tt>]({{ site.apiRoot }}/org/apache/calcite/sql/SqlOperator.html)).
+
+If your operator does not adhere to standard SQL function syntax,
+"`f(arg1, arg2, ...)`", then you need to
+[extend the parser](#extending-the-parser).
+
+There are many good examples in the tests:
+[<tt>class UdfTest</tt>]({{ site.sourceRoot }}/core/src/test/java/org/apache/calcite/test/UdfTest.java)
+tests user-defined functions and user-defined aggregate functions.
+
+### Aggregate functions
+
+*User-defined aggregate functions* are similar to user-defined functions,
+but each function has several corresponding Java methods, one for each
+stage in the life-cycle of an aggregate:
+
+* `init` creates an accumulator;
+* `add` adds one row's value to an accumulator;
+* `merge` combines two accumulators into one;
+* `result` finalizes an accumulator and converts it to a result.
+
+For example, the methods (in pseudo-code) for `SUM(int)` are as follows:
+
+{% highlight java %}
+struct Accumulator {
+  final int sum;
+}
+Accumulator init() {
+  return new Accumulator(0);
+}
+Accumulator add(Accumulator a, int x) {
+  return new Accumulator(a.sum + x);
+}
+Accumulator merge(Accumulator a, Accumulator a2) {
+  return new Accumulator(a.sum + a2.sum);
+}
+int result(Accumulator a) {
+  return new Accumulator(a.sum + x);
+}
+{% endhighlight %}
+
+Here is the sequence of calls to compute the sum of two rows with column values 4 and 7:
+
+{% highlight java %}
+a = init()    # a = {0}
+a = add(a, 4) # a = {4}
+a = add(a, 7) # a = {11}
+return result(a) # returns 11
+{% endhighlight %}
+
+### Window functions
+
+A window function is similar to an aggregate function but it is applied to a set
+of rows gathered by an `OVER` clause rather than by a `GROUP BY` clause.
+Every aggregate function can be used as a window function, but there are some
+key differences. The rows seen by a window function may be ordered, and
+window functions that rely upon order (`RANK`, for example) cannot be used as
+aggregate functions.
+
+Another difference is that windows are *non-disjoint*: a particular row can
+appear in more than one window. For example, 10:37 appears in both the
+9:00-10:00 hour and also the 9:15-9:45 hour.
+
+Window functions are computed incrementally: when the clock ticks from
+10:14 to 10:15, two rows might enter the window and three rows leave.
+For this, window functions have have an extra life-cycle operation:
+
+* `remove` removes a value from an accumulator.
+
+It pseudo-code for `SUM(int)` would be:
+
+{% highlight java %}
+Accumulator remove(Accumulator a, int x) {
+  return new Accumulator(a.sum - x);
+}
+{% endhighlight %}
+
+Here is the sequence of calls to compute the moving sum,
+over the previous 2 rows, of 4 rows with values 4, 7, 2 and 3:
+
+{% highlight java %}
+a = init()       # a = {0}
+a = add(a, 4)    # a = {4}
+emit result(a)   # emits 4
+a = add(a, 7)    # a = {11}
+emit result(a)   # emits 11
+a = remove(a, 4) # a = {7}
+a = add(a, 2)    # a = {9}
+emit result(a)   # emits 9
+a = remove(a, 7) # a = {2}
+a = add(a, 3)    # a = {5}
+emit result(a)   # emits 5
+{% endhighlight %}
+
+### Grouped window functions
+
+Grouped window functions are functions that operate the `GROUP BY` clause
+to gather together records into sets. The built-in grouped window functions
+are `HOP`, `TUMBLE` and `SESSION`.
+You can define additional functions by implementing
+[<tt>interface SqlGroupedWindowFunction</tt>]({{ site.apiRoot }}/org/apache/calcite/sql/fun/SqlGroupedWindowFunction.html).
+
+### Table functions and table macros
+
+*User-defined table functions*
+are defined in a similar way to regular "scalar" user-defined functions,
+but are used in the `FROM` clause of a query. The following query uses a table
+function called `Ramp`:
+
+{% highlight sql %}
+SELECT * FROM TABLE(Ramp(3, 4))
+{% endhighlight %}
+
+*User-defined table macros* use the same SQL syntax as table functions,
+but are defined differently. Rather than generating data, they generate an
+relational expression.
+Table macros are invoked during query preparation and the relational expression
+they produce can then be optimized.
+(Calcite's implementation of views uses table macros.)
+
+[<tt>class TableFunctionTest</tt>]({{ site.sourceRoot }}/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java)
+tests table functions and contains several useful examples.
+
+### Extending the parser
+
+Suppose you need to extend Calcite's SQL grammar in a way that will be
+compatible with future changes to the grammar. Making a copy of the grammar file
+`Parser.jj` in your project would be foolish, because the grammar is edited
+quite frequently.
+
+Fortunately, `Parser.jj` is actually an
+[Apache FreeMarker](http://freemarker.apache.org/)
+template that contains variables that can be substituted.
+The parser in `calcite-core` instantiates the template with default values of
+the variables, typically empty, but you can override.
+If your project would like a different parser, you can provide your
+own `config.fmpp` and `parserImpls.ftl` files and therefore generate an
+extended parser.
+
+The `calcite-server` module, which was created in
+[[CALCITE-707](https://issues.apache.org/jira/browse/CALCITE-707)] and
+adds DDL statements such as `CREATE TABLE`, is an example that you could follow.
+Also see
+[<tt>class ExtensionSqlParserTest</tt>]({{ site.sourceRoot }}/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/ExtensionSqlParserTest.java).
+
+### Customizing SQL dialect accepted and generated
+
+To customize what SQL extensions the parser should accept, implement
+[<tt>interface SqlConformance</tt>]({{ site.apiRoot }}/org/apache/calcite/sql/validate/SqlConformance.html)
+or use one of the built-in values in
+[<tt>enum SqlConformanceEnum</tt>]({{ site.apiRoot }}/org/apache/calcite/sql/validate/SqlConformanceEnum.html).
+
+To control how SQL is generated for an external database (usually via the JDBC
+adapter), use
+[<tt>class SqlDialect</tt>]({{ site.apiRoot }}/org/apache/calcite/sql/SqlDialect.html).
+The dialect also describes the engine's capabilities, such as whether it
+supports `OFFSET` and `FETCH` clauses.
+
+### Defining a custom schema
+
+To define a custom schema, you need to implement
+[<tt>interface SchemaFactory</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/SchemaFactory.html).
+
+During query preparation, Calcite will call this interface to find out
+what tables and sub-schemas your schema contains. When a table in your schema
+is referenced in a query, Calcite will ask your schema to create an instance of
+[<tt>interface Table</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/Table.html).
+
+That table will be wrapped in a
+[<tt>TableScan</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/TableScan.html)
+and will undergo the query optimization process.
+
+### Reflective schema
+
+A reflective schema
+([<tt>class ReflectiveSchema</tt>]({{ site.apiRoot }}/org/apache/calcite/adapter/java/ReflectiveSchema.html))
+is a way of wrapping a Java object so that it appears
+as a schema. Its collection-valued fields will appear as tables.
+
+It is not a schema factory but an actual schema; you have to create the object
+and wrap it in the schema by calling APIs.
+
+See
+[<tt>class ReflectiveSchemaTest</tt>]({{ site.sourceRoot }}/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java).
+
+### Defining a custom table
+
+To define a custom table, you need to implement
+[<tt>interface TableFactory</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/TableFactory.html).
+Whereas a schema factory a set of named tables, a table factory produces a
+single table when bound to a schema with a particular name (and optionally a
+set of extra operands).
+
+### Modifying data
+
+If your table is to support DML operations (INSERT, UPDATE, DELETE, MERGE),
+your implementation of `interface Table` must implement
+[<tt>interface ModifiableTable</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/ModifiableTable.html).
+
+### Streaming
+
+If your table is to support streaming queries,
+your implementation of `interface Table` must implement
+[<tt>interface StreamableTable</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/StreamableTable.html).
+
+See
+[<tt>class StreamTest</tt>]({{ site.sourceRoot }}/core/src/test/java/org/apache/calcite/test/StreamTest.java)
+for examples.
+
+### Pushing operations down to your table
+
+If you wish to push processing down to your custom table's source system,
+consider implementing either
+[<tt>interface FilterableTable</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/FilterableTable.html)
+or
+[<tt>interface ProjectableFilterableTable</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/ProjectableFilterableTable.html).
+
+If you want more control, you should write a [planner rule](#planner-rule).
+This will allow you to push down expressions, to make a cost-based decision
+about whether to push down processing, and push down more complex operations
+such as join, aggregation, and sort.
+
+### Type system
+
+You can customize some aspects of the type system by implementing
+[<tt>interface RelDataTypeSystem</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/type/RelDataTypeSystem.html).
+
+### Relational operators
+
+All relational operators implement
+[<tt>interface RelNode</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/RelNode.html)
+and most extend
+[<tt>class AbstractRelNode</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/AbstractRelNode.html).
+The core operators (used by
+[<tt>SqlToRelConverter</tt>]({{ site.apiRoot }}/org/apache/calcite/sql2rel/SqlToRelConverter.html)
+and covering conventional relational algebra) are
+[<tt>TableScan</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/TableScan.html),
+[<tt>TableModify</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/TableModify.html),
+[<tt>Values</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Values.html),
+[<tt>Project</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Project.html),
+[<tt>Filter</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Filter.html),
+[<tt>Aggregate</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Aggregate.html),
+[<tt>Join</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Join.html),
+[<tt>Sort</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Sort.html), 
+[<tt>Union</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Union.html),
+[<tt>Intersect</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Intersect.html),
+[<tt>Minus</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Minus.html),
+[<tt>Window</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Window.html) and
+[<tt>Match</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Match.html).
+
+Each of these has a "pure" logical sub-class, 
+[<tt>LogicalProject</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/logical/LogicalProject.html)
+and so forth. Any given adapter will have counterparts for the operations that
+its engine can implement efficiently; for example, the Cassandra adapter has
+[<tt>CassandraProject</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/cassandra/CassandraProject.html)
+but there is no `CassandraJoin`.
+
+You can define your own sub-class of `RelNode` to add a new operator, or
+an implementation of an existing operator in a particular engine.
+
+To make an operator useful and powerful, you will need
+[planner rules](#planner-rule) to combine it with existing operators.
+(And also provide metadata, see [below](#statistics-and-cost)).
+This being algebra, the effects are combinatorial: you write a few
+rules, but they combine to handle an exponential number of query patterns.
+
+If possible, make your operator a sub-class of an existing
+operator; then you may be able to re-use or adapt its rules.
+Even better, if your operator is a logical operation that you can rewrite
+(again, via a planner rule) in terms of existing operators, you should do that.
+You will be able to re-use the rules, metadata and implementations of those
+operators with no extra work.
+
+### Planner rule
+
+A planner rule
+([<tt>class RelOptRule</tt>]({{ site.apiRoot }}/org/apache/calcite/plan/RelOptRule.html))
+transforms a relational expression into an equivalent relational expression.
+
+A planner engine has many planner rules registered and fires them
+to transform the input query into something more efficient. Planner rules are
+therefore central to the optimization process, but surprisingly each planner
+rule does not concern itself with cost. The planner engine is responsible for
+firing rules in a sequence that produces an optimal plan, but each individual
+rules only concerns itself with correctness.
+
+Calcite has two built-in planner engines:
+[<tt>class VolcanoPlanner</tt>]({{ site.apiRoot }}/org/apache/calcite/plan/volcano/VolcanoPlanner.html)
+uses dynamic programming and is good for exhaustive search, whereas
+[<tt>class HepPlanner</tt>]({{ site.apiRoot }}/org/apache/calcite/plan/hep/HepPlanner.html)
+fires a sequence of rules in a more fixed order.
+
+### Calling conventions
+
+A calling convention is a protocol used by a particular data engine.
+For example, the Cassandra engine has a collection of relational operators,
+`CassandraProject`, `CassandraFilter` and so forth, and these operators can be
+connected to each other without the data having to be converted from one format
+to another.
+
+If data needs to be converted from one calling convention to another, Calcite
+uses a special sub-class of relational expression called a converter
+(see [<tt>class Converter</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/convert/Converter.html)).
+But of course converting data has a runtime cost.
+
+When planning a query that uses multiple engines, Calcite "colors" regions of
+the relational expression tree according to their calling convention. The
+planner pushes operations into data sources by firing rules. If the engine does
+not support a particular operation, the rule will not fire. Sometimes an
+operation can occur in more than one place, and ultimately the best plan is
+chosen according to cost.
+
+A calling convention is a class that implements
+[<tt>interface Convention</tt>]({{ site.apiRoot }}/org/apache/calcite/plan/Convention.html),
+an auxiliary interface (for instance
+[<tt>interface CassandraRel</tt>]({{ site.apiRoot }}/org/apache/calcite/adapter/cassandra/CassandraRel.html)),
+and a set of sub-classes of
+[<tt>class RelNode</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/RelNode.html)
+that implement that interface for the core relational operators
+([<tt>Project</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Project.html),
+[<tt>Filter</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Filter.html),
+[<tt>Aggregate</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/core/Aggregate.html),
+and so forth).
+
+### Built-in SQL implementation
+
+How does Calcite implement SQL, if an adapter does not implement all of the core
+relational operators?
+
+The answer is a particular built-in calling convention,
+[<tt>EnumerableConvention</tt>]({{ site.apiRoot }}/org/apache/calcite/adapter/EnumerableConvention.html).
+Relational expressions of enumerable convention are implemented as "built-ins":
+Calcite generates Java code, compiles it, and executes inside its own JVM.
+Enumerable convention is less efficient than, say, a distributed engine
+running over column-oriented data files, but it can implement all core
+relational operators and all built-in SQL functions and operators. If a data
+source cannot an implement a relational operator, enumerable convention is
+a fall-back.
+
+### Statistics and cost
+
+Calcite has a metadata system that allow you to define cost functions and
+statistics about relational operators, collectively referred to as *metadata*.
+Each kind of metadata has an interface with (usually) one method.
+For example, selectivity is defined by
+[<tt>interface RelMdSelectivity</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdSelectivity.html)
+and the method
+[<tt>getSelectivity(RelNode rel, RexNode predicate)</tt>]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMetadataQuery.html#getSelectivity-org.apache.calcite.rel.RelNode-org.apache.calcite.rex.RexNode-).
+
+There are many built-in kinds of metadata, including
+[collation]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdCollation.html),
+[column origins]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdColumnOrigins.html),
+[column uniqueness]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.html),
+[distinct row count]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.html),
+[distribution]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdDistribution.html),
+[explain visibility]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdExplainVisibility.html),
+[expression lineage]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdExpressionLineage.html),
+[max row count]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdMaxRowCount.html),
+[node types]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdNodeTypes.html),
+[parallelism]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdParallelism.html),
+[percentage original rows]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.html),
+[population size]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdPopulationSize.html),
+[predicates]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdPredicates.html),
+[row count]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdRowCount.html),
+[selectivity]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdSelectivity.html),
+[size]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdSize.html),
+[table references]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdTableReferences.html),
+[unique keys]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdUniqueKeys.html), and
+[selectivity]({{ site.apiRoot }}/org/apache/calcite/rel/metadata/RelMdSelectivity.html);
+you can also define your own.
+
+You can then supply a *metadata provider* that computes that kind of metadata
+for particular sub-classes of `RelNode`. Metadata providers can handle built-in
+and extended metadata types, and built-in and extended `RelNode` types.
+While preparing a query Calcite combines all of the applicable metadata
+providers and maintains a cache so that a given piece of metadata (for example
+the selectivity of the condition `x > 10` in a particular `Filter` operator)
+is computed only once.
+

http://git-wip-us.apache.org/repos/asf/calcite/blob/2f0eecb3/site/_docs/tutorial.md
----------------------------------------------------------------------
diff --git a/site/_docs/tutorial.md b/site/_docs/tutorial.md
index c907cef..2a6e91e 100644
--- a/site/_docs/tutorial.md
+++ b/site/_docs/tutorial.md
@@ -718,43 +718,6 @@ initial implementations.
 
 ## Further topics
 
-### Defining a custom schema
+There are many other ways to extend Calcite not yet described in this tutorial.
+The [adapter specification](adapter.html) describes the APIs involved.
 
-(To be written.)
-
-### Modifying data
-
-How to enable DML operations (INSERT, UPDATE and DELETE) on your schema.
-
-(To be written.)
-
-### Calling conventions
-
-(To be written.)
-
-### Statistics and cost
-
-(To be written.)
-
-### Defining and using user-defined functions
-
-(To be written.)
-
-###  Defining tables in a schema
-
-(To be written.)
-
-### Defining custom tables
-
-(To be written.)
-
-### Built-in SQL implementation
-
-How does Calcite implement SQL, if an adapter does not implement all of the core
-relational operators?
-
-(To be written.)
-
-### Table functions
-
-(To be written.)


[2/5] calcite git commit: Javadoc fixes (Alexey Roytman)

Posted by jh...@apache.org.
Javadoc fixes (Alexey Roytman)

Javadoc for 'filters' @param of ProjectableFilterableTable.scan()
stated the opposite of the truth, and was in conflict with the method
javadoc.

Javadoc for RelDataType.getFieldCount() had ()() in its description;
fixed.

Close apache/calcite#553


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

Branch: refs/heads/master
Commit: 67bd5446911bd8d1fe96b646affab844d85bb165
Parents: 4542510
Author: Alexey Roytman <al...@oracle.com>
Authored: Fri Oct 27 08:43:07 2017 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 2 12:56:03 2017 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/adapter/cassandra/CassandraFilter.java    | 2 +-
 core/src/main/java/org/apache/calcite/rel/type/RelDataType.java  | 4 ++--
 .../org/apache/calcite/schema/ProjectableFilterableTable.java    | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/67bd5446/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java
----------------------------------------------------------------------
diff --git a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java
index ba8aa9c..0bdc7f6 100644
--- a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java
+++ b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraFilter.java
@@ -174,7 +174,7 @@ public class CassandraFilter extends Filter implements CassandraRel {
       }
     }
 
-    /** Conver the value of a literal to a string.
+    /** Convert the value of a literal to a string.
      *
      * @param literal Literal to translate
      * @return String representation of the literal

http://git-wip-us.apache.org/repos/asf/calcite/blob/67bd5446/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
index 7b18d67..aa8d495 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
@@ -71,8 +71,8 @@ public interface RelDataType /*extends Type*/ {
   /**
    * Returns the number of fields in a struct type.
    *
-   * <p>This method is equivalent to <code>{@link #getFieldList}
-   * ().size()</code>.
+   * <p>This method is equivalent to
+   * <code>{@link #getFieldList}.size()</code>.
    */
   int getFieldCount();
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/67bd5446/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java b/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java
index 2f56865..f6227c4 100644
--- a/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java
+++ b/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java
@@ -48,7 +48,7 @@ public interface ProjectableFilterableTable extends Table {
    * <p>The projects are zero-based.</p>
    *
    * @param root Execution context
-   * @param filters Mutable list of filters. The method should remove from the
+   * @param filters Mutable list of filters. The method should keep in the
    *                list any filters that it cannot apply.
    * @param projects List of projects. Each is the 0-based ordinal of the column
    *                 to project.