This is an automated email from the ASF dual-hosted git repository.

jhyde pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/master by this push:
     new 170d522  [CALCITE-3050] Integrate SqlDialect and SqlParser.Config
170d522 is described below

commit 170d5225fb65fd49df83d0234d8a69d11a9469c4
Author: Julian Hyde <[email protected]>
AuthorDate: Tue May 14 00:18:21 2019 -0700

    [CALCITE-3050] Integrate SqlDialect and SqlParser.Config
    
    Add a method SqlDialect.configureParser(SqlParser.ConfigBuilder),
    which allows a dialect to pass on its settings to a SQL parser.
    
    Also add SqlDialect methods getUnquotedCasing(), getQuotedCasing(),
    getConformance(), isCaseSensitive().
    
    Add Snowflake dialect.
---
 .../java/org/apache/calcite/sql/SqlDialect.java    | 209 +++++++++++++++++++--
 .../apache/calcite/sql/SqlDialectFactoryImpl.java  |  45 +++++
 .../calcite/sql/dialect/MssqlSqlDialect.java       |   3 +-
 .../calcite/sql/dialect/MysqlSqlDialect.java       |   2 +
 .../calcite/sql/dialect/PostgresqlSqlDialect.java  |   2 +
 .../calcite/sql/dialect/RedshiftSqlDialect.java    |   6 +-
 ...icaSqlDialect.java => SnowflakeSqlDialect.java} |  22 +--
 .../calcite/sql/dialect/VerticaSqlDialect.java     |   4 +-
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |  43 ++++-
 .../apache/calcite/sql/parser/SqlParserTest.java   |  41 ++++
 10 files changed, 337 insertions(+), 40 deletions(-)

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 8d78386..5a1b14f 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
@@ -16,7 +16,9 @@
  */
 package org.apache.calcite.sql;
 
+import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.avatica.util.Quoting;
 import org.apache.calcite.avatica.util.TimeUnit;
 import org.apache.calcite.config.NullCollation;
 import org.apache.calcite.linq4j.function.Experimental;
@@ -28,9 +30,12 @@ import org.apache.calcite.sql.dialect.AnsiSqlDialect;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
 import org.apache.calcite.sql.dialect.JethroDataSqlDialect;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.BasicSqlType;
 import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.sql.validate.SqlConformance;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Suppliers;
@@ -135,6 +140,9 @@ public class SqlDialect {
   private final DatabaseProduct databaseProduct;
   protected final NullCollation nullCollation;
   private final RelDataTypeSystem dataTypeSystem;
+  private final Casing unquotedCasing;
+  private final Casing quotedCasing;
+  private final boolean caseSensitive;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -210,6 +218,9 @@ public class SqlDialect {
     this.identifierEscapedQuote =
         identifierQuoteString == null ? null
             : this.identifierEndQuoteString + this.identifierEndQuoteString;
+    this.unquotedCasing = Objects.requireNonNull(context.unquotedCasing());
+    this.quotedCasing = Objects.requireNonNull(context.quotedCasing());
+    this.caseSensitive = context.caseSensitive();
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -217,7 +228,9 @@ public class SqlDialect {
   /** Creates an empty context. Use {@link #EMPTY_CONTEXT} if possible. */
   protected static Context emptyContext() {
     return new ContextImpl(DatabaseProduct.UNKNOWN, null, null, -1, -1, null,
-        NullCollation.HIGH, RelDataTypeSystemImpl.DEFAULT, 
JethroDataSqlDialect.JethroInfo.EMPTY);
+        Casing.UNCHANGED, Casing.TO_UPPER, true, SqlConformanceEnum.DEFAULT,
+        NullCollation.HIGH, RelDataTypeSystemImpl.DEFAULT,
+        JethroDataSqlDialect.JethroInfo.EMPTY);
   }
 
   /**
@@ -238,7 +251,6 @@ public class SqlDialect {
     case "ACCESS":
       return DatabaseProduct.ACCESS;
     case "APACHE DERBY":
-      return DatabaseProduct.DERBY;
     case "DBMS:CLOUDSCAPE":
       return DatabaseProduct.DERBY;
     case "HIVE":
@@ -922,6 +934,97 @@ public class SqlDialect {
   }
 
   /**
+   * Copies settings from this dialect into a parser configuration.
+   *
+   * <p>{@code SqlDialect}, {@link SqlParser.Config} and {@link SqlConformance}
+   * cover different aspects of the same thing - the dialect of SQL spoken by a
+   * database - and this method helps to bridge between them. (The aspects are,
+   * respectively, generating SQL to send to a source database, parsing SQL
+   * sent to Calcite, and validating queries sent to Calcite. It makes sense to
+   * keep them as separate interfaces because they are used by different
+   * modules.)
+   *
+   * <p>The settings copied may differ among dialects, and may change over 
time,
+   * but currently include the following:
+   *
+   * <ul>
+   *   <li>{@link #getQuoting()}
+   *   <li>{@link #getQuotedCasing()}
+   *   <li>{@link #getUnquotedCasing()}
+   *   <li>{@link #isCaseSensitive()}
+   *   <li>{@link #getConformance()}
+   * </ul>
+   *
+   * @param configBuilder Parser configuration builder
+   *
+   * @return The configuration builder
+   */
+  public @Nonnull SqlParser.ConfigBuilder configureParser(
+      SqlParser.ConfigBuilder configBuilder) {
+    final Quoting quoting = getQuoting();
+    if (quoting != null) {
+      configBuilder.setQuoting(quoting);
+    }
+    configBuilder.setQuotedCasing(getQuotedCasing());
+    configBuilder.setUnquotedCasing(getUnquotedCasing());
+    configBuilder.setCaseSensitive(isCaseSensitive());
+    configBuilder.setConformance(getConformance());
+    return configBuilder;
+  }
+
+  /** Returns the {@link SqlConformance} that matches this dialect.
+   *
+   * <p>The base implementation returns its best guess, based upon
+   * {@link #databaseProduct}; sub-classes may override. */
+  @Nonnull public SqlConformance getConformance() {
+    switch (databaseProduct) {
+    case CALCITE:
+      return SqlConformanceEnum.DEFAULT;
+    case MYSQL:
+      return SqlConformanceEnum.MYSQL_5;
+    case ORACLE:
+      return SqlConformanceEnum.ORACLE_10;
+    case MSSQL:
+      return SqlConformanceEnum.SQL_SERVER_2008;
+    default:
+      return SqlConformanceEnum.PRAGMATIC_2003;
+    }
+  }
+
+  /** Returns the quoting scheme, or null if the combination of
+   * {@link #identifierQuoteString} and {@link #identifierEndQuoteString}
+   * does not correspond to any known quoting scheme. */
+  protected Quoting getQuoting() {
+    if ("\"".equals(identifierQuoteString)
+        && "\"".equals(identifierEndQuoteString)) {
+      return Quoting.DOUBLE_QUOTE;
+    } else if ("`".equals(identifierQuoteString)
+        && "`".equals(identifierEndQuoteString)) {
+      return Quoting.BACK_TICK;
+    } else if ("[".equals(identifierQuoteString)
+        && "]".equals(identifierEndQuoteString)) {
+      return Quoting.BRACKET;
+    } else {
+      return null;
+    }
+  }
+
+  /** Returns how unquoted identifiers are stored. */
+  public Casing getUnquotedCasing() {
+    return unquotedCasing;
+  }
+
+  /** Returns how quoted identifiers are stored. */
+  public Casing getQuotedCasing() {
+    return quotedCasing;
+  }
+
+  /** Returns whether matching of identifiers is case-sensitive. */
+  public boolean isCaseSensitive() {
+    return caseSensitive;
+  }
+
+  /**
    * A few utility functions copied from org.apache.calcite.util.Util. We have
    * copied them because we wish to keep SqlDialect's dependencies to a
    * minimum.
@@ -1023,6 +1126,7 @@ public class SqlDialect {
      * presumably the dialect capabilities are similar. */
     PARACCEL("Paraccel", "\"", NullCollation.HIGH),
     REDSHIFT("Redshift", "\"", NullCollation.HIGH),
+    SNOWFLAKE("Snowflake", "\"", NullCollation.HIGH),
 
     /**
      * Placeholder for the unknown database.
@@ -1085,7 +1189,15 @@ public class SqlDialect {
     int databaseMinorVersion();
     Context withDatabaseMinorVersion(int databaseMinorVersion);
     String identifierQuoteString();
-    Context withIdentifierQuoteString(String identifierQuoteString);
+    @Nonnull Context withIdentifierQuoteString(String identifierQuoteString);
+    @Nonnull Casing unquotedCasing();
+    @Nonnull Context withUnquotedCasing(Casing unquotedCasing);
+    @Nonnull Casing quotedCasing();
+    @Nonnull Context withQuotedCasing(Casing unquotedCasing);
+    boolean caseSensitive();
+    @Nonnull Context withCaseSensitive(boolean caseSensitive);
+    @Nonnull SqlConformance conformance();
+    @Nonnull Context withConformance(SqlConformance conformance);
     @Nonnull NullCollation nullCollation();
     @Nonnull Context withNullCollation(@Nonnull NullCollation nullCollation);
     @Nonnull RelDataTypeSystem dataTypeSystem();
@@ -1102,6 +1214,10 @@ public class SqlDialect {
     private final int databaseMajorVersion;
     private final int databaseMinorVersion;
     private final String identifierQuoteString;
+    private final Casing unquotedCasing;
+    private final Casing quotedCasing;
+    private final boolean caseSensitive;
+    private final SqlConformance conformance;
     private final NullCollation nullCollation;
     private final RelDataTypeSystem dataTypeSystem;
     private final JethroDataSqlDialect.JethroInfo jethroInfo;
@@ -1109,7 +1225,9 @@ public class SqlDialect {
     private ContextImpl(DatabaseProduct databaseProduct,
         String databaseProductName, String databaseVersion,
         int databaseMajorVersion, int databaseMinorVersion,
-        String identifierQuoteString, NullCollation nullCollation,
+        String identifierQuoteString, Casing quotedCasing,
+        Casing unquotedCasing, boolean caseSensitive,
+        SqlConformance conformance, NullCollation nullCollation,
         RelDataTypeSystem dataTypeSystem,
         JethroDataSqlDialect.JethroInfo jethroInfo) {
       this.databaseProduct = Objects.requireNonNull(databaseProduct);
@@ -1118,6 +1236,10 @@ public class SqlDialect {
       this.databaseMajorVersion = databaseMajorVersion;
       this.databaseMinorVersion = databaseMinorVersion;
       this.identifierQuoteString = identifierQuoteString;
+      this.quotedCasing = Objects.requireNonNull(quotedCasing);
+      this.unquotedCasing = Objects.requireNonNull(unquotedCasing);
+      this.caseSensitive = caseSensitive;
+      this.conformance = Objects.requireNonNull(conformance);
       this.nullCollation = Objects.requireNonNull(nullCollation);
       this.dataTypeSystem = Objects.requireNonNull(dataTypeSystem);
       this.jethroInfo = Objects.requireNonNull(jethroInfo);
@@ -1131,7 +1253,10 @@ public class SqlDialect {
         @Nonnull DatabaseProduct databaseProduct) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing,
+          caseSensitive,
+          conformance, nullCollation,
+          dataTypeSystem, jethroInfo);
     }
 
     public String databaseProductName() {
@@ -1141,7 +1266,8 @@ public class SqlDialect {
     public Context withDatabaseProductName(String databaseProductName) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
 
     public String databaseVersion() {
@@ -1151,7 +1277,8 @@ public class SqlDialect {
     public Context withDatabaseVersion(String databaseVersion) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
 
     public int databaseMajorVersion() {
@@ -1161,7 +1288,8 @@ public class SqlDialect {
     public Context withDatabaseMajorVersion(int databaseMajorVersion) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
 
     public int databaseMinorVersion() {
@@ -1171,27 +1299,76 @@ public class SqlDialect {
     public Context withDatabaseMinorVersion(int databaseMinorVersion) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
 
     public String identifierQuoteString() {
       return identifierQuoteString;
     }
 
-    public Context withIdentifierQuoteString(String identifierQuoteString) {
+    @Nonnull public Context withIdentifierQuoteString(
+        String identifierQuoteString) {
+      return new ContextImpl(databaseProduct, databaseProductName,
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
+    }
+
+    @Nonnull public Casing unquotedCasing() {
+      return unquotedCasing;
+    }
+
+    @Nonnull public Context withUnquotedCasing(Casing unquotedCasing) {
+      return new ContextImpl(databaseProduct, databaseProductName,
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
+    }
+
+    @Nonnull public Casing quotedCasing() {
+      return quotedCasing;
+    }
+
+    @Nonnull public Context withQuotedCasing(Casing quotedCasing) {
+      return new ContextImpl(databaseProduct, databaseProductName,
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
+    }
+
+    public boolean caseSensitive() {
+      return caseSensitive;
+    }
+
+    @Nonnull public Context withCaseSensitive(boolean caseSensitive) {
+      return new ContextImpl(databaseProduct, databaseProductName,
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
+    }
+
+    @Nonnull public SqlConformance conformance() {
+      return conformance;
+    }
+
+    @Nonnull public Context withConformance(SqlConformance conformance) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
 
     @Nonnull public NullCollation nullCollation() {
       return nullCollation;
     }
 
-    public Context withNullCollation(@Nonnull NullCollation nullCollation) {
+    @Nonnull public Context withNullCollation(
+        @Nonnull NullCollation nullCollation) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
 
     @Nonnull public RelDataTypeSystem dataTypeSystem() {
@@ -1201,7 +1378,8 @@ public class SqlDialect {
     public Context withDataTypeSystem(@Nonnull RelDataTypeSystem 
dataTypeSystem) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
 
     @Nonnull public JethroDataSqlDialect.JethroInfo jethroInfo() {
@@ -1211,7 +1389,8 @@ public class SqlDialect {
     public Context withJethroInfo(JethroDataSqlDialect.JethroInfo jethroInfo) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation, dataTypeSystem, jethroInfo);
+          identifierQuoteString, quotedCasing, unquotedCasing, caseSensitive,
+          conformance, nullCollation, dataTypeSystem, jethroInfo);
     }
   }
 }
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 f26f940..280c5fa 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql;
 
+import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.config.NullCollation;
 import org.apache.calcite.sql.dialect.AccessSqlDialect;
 import org.apache.calcite.sql.dialect.AnsiSqlDialect;
@@ -42,6 +43,7 @@ import org.apache.calcite.sql.dialect.ParaccelSqlDialect;
 import org.apache.calcite.sql.dialect.PhoenixSqlDialect;
 import org.apache.calcite.sql.dialect.PostgresqlSqlDialect;
 import org.apache.calcite.sql.dialect.RedshiftSqlDialect;
+import org.apache.calcite.sql.dialect.SnowflakeSqlDialect;
 import org.apache.calcite.sql.dialect.SparkSqlDialect;
 import org.apache.calcite.sql.dialect.SybaseSqlDialect;
 import org.apache.calcite.sql.dialect.TeradataSqlDialect;
@@ -82,12 +84,18 @@ public class SqlDialectFactoryImpl implements 
SqlDialectFactory {
         databaseProductName.toUpperCase(Locale.ROOT).trim();
     final String quoteString = getIdentifierQuoteString(databaseMetaData);
     final NullCollation nullCollation = getNullCollation(databaseMetaData);
+    final Casing unquotedCasing = getCasing(databaseMetaData, false);
+    final Casing quotedCasing = getCasing(databaseMetaData, true);
+    final boolean caseSensitive = isCaseSensitive(databaseMetaData);
     final SqlDialect.Context c = SqlDialect.EMPTY_CONTEXT
         .withDatabaseProductName(databaseProductName)
         .withDatabaseMajorVersion(databaseMajorVersion)
         .withDatabaseMinorVersion(databaseMinorVersion)
         .withDatabaseVersion(databaseVersion)
         .withIdentifierQuoteString(quoteString)
+        .withUnquotedCasing(unquotedCasing)
+        .withQuotedCasing(quotedCasing)
+        .withCaseSensitive(caseSensitive)
         .withNullCollation(nullCollation);
     switch (upperProductName) {
     case "ACCESS":
@@ -117,6 +125,8 @@ public class SqlDialectFactoryImpl implements 
SqlDialectFactory {
       return new MysqlSqlDialect(c);
     case "REDSHIFT":
       return new RedshiftSqlDialect(c);
+    case "SNOWFLAKE":
+      return new SnowflakeSqlDialect(c);
     case "SPARK":
       return new SparkSqlDialect(c);
     }
@@ -147,6 +157,8 @@ public class SqlDialectFactoryImpl implements 
SqlDialectFactory {
       return new H2SqlDialect(c);
     } else if (upperProductName.contains("VERTICA")) {
       return new VerticaSqlDialect(c);
+    } else if (upperProductName.contains("SNOWFLAKE")) {
+      return new SnowflakeSqlDialect(c);
     } else if (upperProductName.contains("SPARK")) {
       return new SparkSqlDialect(c);
     } else {
@@ -154,6 +166,39 @@ public class SqlDialectFactoryImpl implements 
SqlDialectFactory {
     }
   }
 
+  private Casing getCasing(DatabaseMetaData databaseMetaData, boolean quoted) {
+    try {
+      if (quoted
+          ? databaseMetaData.storesUpperCaseQuotedIdentifiers()
+          : databaseMetaData.storesUpperCaseIdentifiers()) {
+        return Casing.TO_UPPER;
+      } else if (quoted
+          ? databaseMetaData.storesLowerCaseQuotedIdentifiers()
+          : databaseMetaData.storesLowerCaseIdentifiers()) {
+        return Casing.TO_LOWER;
+      } else if (quoted
+          ? (databaseMetaData.storesMixedCaseQuotedIdentifiers()
+              || databaseMetaData.supportsMixedCaseQuotedIdentifiers())
+          : (databaseMetaData.storesMixedCaseIdentifiers()
+              || databaseMetaData.supportsMixedCaseIdentifiers())) {
+        return Casing.UNCHANGED;
+      } else {
+        return Casing.UNCHANGED;
+      }
+    } catch (SQLException e) {
+      throw new IllegalArgumentException("cannot deduce casing", e);
+    }
+  }
+
+  private boolean isCaseSensitive(DatabaseMetaData databaseMetaData) {
+    try {
+      return databaseMetaData.supportsMixedCaseIdentifiers()
+          || databaseMetaData.supportsMixedCaseQuotedIdentifiers();
+    } catch (SQLException e) {
+      throw new IllegalArgumentException("cannot deduce case-sensitivity", e);
+    }
+  }
+
   private NullCollation getNullCollation(DatabaseMetaData databaseMetaData) {
     try {
       if (databaseMetaData.nullsAreSortedAtEnd()) {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java
index 89de617..47b647b 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java
@@ -41,7 +41,8 @@ public class MssqlSqlDialect extends SqlDialect {
   public static final SqlDialect DEFAULT =
       new MssqlSqlDialect(EMPTY_CONTEXT
           .withDatabaseProduct(DatabaseProduct.MSSQL)
-          .withIdentifierQuoteString("["));
+          .withIdentifierQuoteString("[")
+          .withCaseSensitive(false));
 
   private static final SqlFunction MSSQL_SUBSTRING =
       new SqlFunction("SUBSTRING", SqlKind.OTHER_FUNCTION,
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 0ac80f3..742bd02 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
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql.dialect;
 
+import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.avatica.util.TimeUnit;
 import org.apache.calcite.avatica.util.TimeUnitRange;
 import org.apache.calcite.config.NullCollation;
@@ -50,6 +51,7 @@ public class MysqlSqlDialect extends SqlDialect {
       new MysqlSqlDialect(EMPTY_CONTEXT
           .withDatabaseProduct(DatabaseProduct.MYSQL)
           .withIdentifierQuoteString("`")
+          .withUnquotedCasing(Casing.UNCHANGED)
           .withNullCollation(NullCollation.LOW));
 
   /** MySQL specific function. */
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java
index 5f1b8d0..987c944 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql.dialect;
 
+import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.avatica.util.TimeUnitRange;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
@@ -59,6 +60,7 @@ public class PostgresqlSqlDialect extends SqlDialect {
       new PostgresqlSqlDialect(EMPTY_CONTEXT
           .withDatabaseProduct(DatabaseProduct.POSTGRESQL)
           .withIdentifierQuoteString("\"")
+          .withUnquotedCasing(Casing.TO_LOWER)
           .withDataTypeSystem(POSTGRESQL_TYPE_SYSTEM));
 
   /** Creates a PostgresqlSqlDialect. */
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java
index 8f0dc1d..ebe2d98 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql.dialect;
 
+import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlWriter;
@@ -27,7 +28,10 @@ public class RedshiftSqlDialect extends SqlDialect {
   public static final SqlDialect DEFAULT =
       new RedshiftSqlDialect(EMPTY_CONTEXT
           .withDatabaseProduct(DatabaseProduct.REDSHIFT)
-          .withIdentifierQuoteString("\""));
+          .withIdentifierQuoteString("\"")
+          .withQuotedCasing(Casing.TO_LOWER)
+          .withUnquotedCasing(Casing.TO_LOWER)
+          .withCaseSensitive(false));
 
   /** Creates a RedshiftSqlDialect. */
   public RedshiftSqlDialect(Context context) {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/VerticaSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/SnowflakeSqlDialect.java
similarity index 66%
copy from 
core/src/main/java/org/apache/calcite/sql/dialect/VerticaSqlDialect.java
copy to 
core/src/main/java/org/apache/calcite/sql/dialect/SnowflakeSqlDialect.java
index 5781feb..a6ab502 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/VerticaSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/SnowflakeSqlDialect.java
@@ -16,25 +16,23 @@
  */
 package org.apache.calcite.sql.dialect;
 
+import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.sql.SqlDialect;
 
 /**
- * A <code>SqlDialect</code> implementation for the Vertica database.
+ * A <code>SqlDialect</code> implementation for the Snowflake database.
  */
-public class VerticaSqlDialect extends SqlDialect {
+public class SnowflakeSqlDialect extends SqlDialect {
   public static final SqlDialect DEFAULT =
-      new VerticaSqlDialect(EMPTY_CONTEXT
-          .withDatabaseProduct(DatabaseProduct.VERTICA)
-          .withIdentifierQuoteString("\""));
+      new SnowflakeSqlDialect(EMPTY_CONTEXT
+          .withDatabaseProduct(DatabaseProduct.SNOWFLAKE)
+          .withIdentifierQuoteString("\"")
+          .withUnquotedCasing(Casing.TO_UPPER));
 
-  /** Creates a VerticaSqlDialect. */
-  public VerticaSqlDialect(Context context) {
+  /** Creates a SnowflakeSqlDialect. */
+  public SnowflakeSqlDialect(Context context) {
     super(context);
   }
-
-  @Override public boolean supportsNestedAggregations() {
-    return false;
-  }
 }
 
-// End VerticaSqlDialect.java
+// End SnowflakeSqlDialect.java
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/VerticaSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/VerticaSqlDialect.java
index 5781feb..08dac88 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/VerticaSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/VerticaSqlDialect.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql.dialect;
 
+import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.sql.SqlDialect;
 
 /**
@@ -25,7 +26,8 @@ public class VerticaSqlDialect extends SqlDialect {
   public static final SqlDialect DEFAULT =
       new VerticaSqlDialect(EMPTY_CONTEXT
           .withDatabaseProduct(DatabaseProduct.VERTICA)
-          .withIdentifierQuoteString("\""));
+          .withIdentifierQuoteString("\"")
+          .withUnquotedCasing(Casing.UNCHANGED));
 
   /** Creates a VerticaSqlDialect. */
   public VerticaSqlDialect(Context context) {
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 693c3cc..da7dc67 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
@@ -516,14 +516,11 @@ public class RelToSqlConverterTest {
         + "FROM (SELECT SUM(`net_weight`) AS `net_weight1`\n"
         + "FROM `foodmart`.`product`\n"
         + "GROUP BY `product_id`) AS `t1`";
-    final String expectedVertica = "SELECT SUM(\"net_weight1\") AS 
\"net_weight_converted\"\n"
-        + "FROM (SELECT SUM(\"net_weight\") AS \"net_weight1\"\n"
-        + "FROM \"foodmart\".\"product\"\n"
-        + "GROUP BY \"product_id\") AS \"t1\"";
     final String expectedPostgresql = "SELECT SUM(\"net_weight1\") AS 
\"net_weight_converted\"\n"
         + "FROM (SELECT SUM(\"net_weight\") AS \"net_weight1\"\n"
         + "FROM \"foodmart\".\"product\"\n"
         + "GROUP BY \"product_id\") AS \"t1\"";
+    final String expectedVertica = expectedPostgresql;
     sql(query)
         .withOracle()
         .ok(expectedOracle)
@@ -781,7 +778,7 @@ public class RelToSqlConverterTest {
     final String query = "select position('A' IN 'ABC') from \"product\"";
     final String expected = "SELECT STRPOS('ABC', 'A')\n"
         + "FROM foodmart.product";
-    sql(query).withBigquery().ok(expected);
+    sql(query).withBigQuery().ok(expected);
   }
 
   @Test public void testModFunctionForHive() {
@@ -797,7 +794,7 @@ public class RelToSqlConverterTest {
     final String expected = "SELECT MOD(11, 3)\n"
         + "FROM foodmart.product\n"
         + "UNION DISTINCT\nSELECT 1\nFROM foodmart.product";
-    sql(query).withBigquery().ok(expected);
+    sql(query).withBigQuery().ok(expected);
   }
 
   @Test public void testIntersectOperatorForBigQuery() {
@@ -806,7 +803,7 @@ public class RelToSqlConverterTest {
     final String expected = "SELECT MOD(11, 3)\n"
         + "FROM foodmart.product\n"
         + "INTERSECT DISTINCT\nSELECT 1\nFROM foodmart.product";
-    sql(query).withBigquery().ok(expected);
+    sql(query).withBigQuery().ok(expected);
   }
 
   @Test public void testExceptOperatorForBigQuery() {
@@ -815,7 +812,7 @@ public class RelToSqlConverterTest {
     final String expected = "SELECT MOD(11, 3)\n"
         + "FROM foodmart.product\n"
         + "EXCEPT DISTINCT\nSELECT 1\nFROM foodmart.product";
-    sql(query).withBigquery().ok(expected);
+    sql(query).withBigQuery().ok(expected);
   }
 
   @Test public void 
testHiveSelectQueryWithOrderByDescAndNullsFirstShouldBeEmulated() {
@@ -1867,6 +1864,8 @@ public class RelToSqlConverterTest {
         + "FROM \"foodmart\".\"product\"";
     final String expectedPostgresql = "SELECT SUBSTRING(\"brand_name\" FROM 
2)\n"
         + "FROM \"foodmart\".\"product\"";
+    final String expectedSnowflake = expectedPostgresql;
+    final String expectedRedshift = expectedPostgresql;
     final String expectedMysql = "SELECT SUBSTRING(`brand_name` FROM 2)\n"
         + "FROM `foodmart`.`product`";
     sql(query)
@@ -1874,6 +1873,10 @@ public class RelToSqlConverterTest {
         .ok(expectedOracle)
         .withPostgresql()
         .ok(expectedPostgresql)
+        .withSnowflake()
+        .ok(expectedSnowflake)
+        .withRedshift()
+        .ok(expectedRedshift)
         .withMysql()
         .ok(expectedMysql)
         .withMssql()
@@ -1888,6 +1891,8 @@ public class RelToSqlConverterTest {
         + "FROM \"foodmart\".\"product\"";
     final String expectedPostgresql = "SELECT SUBSTRING(\"brand_name\" FROM 2 
FOR 3)\n"
         + "FROM \"foodmart\".\"product\"";
+    final String expectedSnowflake = expectedPostgresql;
+    final String expectedRedshift = expectedPostgresql;
     final String expectedMysql = "SELECT SUBSTRING(`brand_name` FROM 2 FOR 
3)\n"
         + "FROM `foodmart`.`product`";
     final String expectedMssql = "SELECT SUBSTRING([brand_name], 2, 3)\n"
@@ -1897,6 +1902,10 @@ public class RelToSqlConverterTest {
         .ok(expectedOracle)
         .withPostgresql()
         .ok(expectedPostgresql)
+        .withSnowflake()
+        .ok(expectedSnowflake)
+        .withRedshift()
+        .ok(expectedRedshift)
         .withMysql()
         .ok(expectedMysql)
         .withMssql()
@@ -3029,13 +3038,19 @@ public class RelToSqlConverterTest {
         + "UNION ALL\n"
         + "SELECT 2 \"a\", 'yy' \"b\"\n"
         + "FROM \"DUAL\")";
+    final String expectedSnowflake = expectedPostgresql;
+    final String expectedRedshift = expectedPostgresql;
     sql(sql)
         .withHsqldb()
         .ok(expectedHsqldb)
         .withPostgresql()
         .ok(expectedPostgresql)
         .withOracle()
-        .ok(expectedOracle);
+        .ok(expectedOracle)
+        .withSnowflake()
+        .ok(expectedSnowflake)
+        .withRedshift()
+        .ok(expectedRedshift);
   }
 
   /** Test case for
@@ -3408,11 +3423,19 @@ public class RelToSqlConverterTest {
       return dialect(SqlDialect.DatabaseProduct.POSTGRESQL.getDialect());
     }
 
+    Sql withRedshift() {
+      return dialect(DatabaseProduct.REDSHIFT.getDialect());
+    }
+
+    Sql withSnowflake() {
+      return dialect(DatabaseProduct.SNOWFLAKE.getDialect());
+    }
+
     Sql withVertica() {
       return dialect(SqlDialect.DatabaseProduct.VERTICA.getDialect());
     }
 
-    Sql withBigquery() {
+    Sql withBigQuery() {
       return dialect(SqlDialect.DatabaseProduct.BIG_QUERY.getDialect());
     }
 
diff --git 
a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java 
b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index f2af797..72950c8 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -19,6 +19,7 @@ package org.apache.calcite.sql.parser;
 import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.avatica.util.Quoting;
 import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.SqlIdentifier;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlNode;
@@ -618,6 +619,11 @@ public class SqlParserTest {
             .build());
   }
 
+  protected SqlParser getDialectSqlParser(String sql, SqlDialect dialect) {
+    return SqlParser.create(new SourceStringReader(sql),
+        dialect.configureParser(SqlParser.configBuilder()).build());
+  }
+
   protected void checkExp(
       String sql,
       String expected) {
@@ -8727,6 +8733,41 @@ public class SqlParserTest {
     assertEquals(node2.toString(), node1.toString());
   }
 
+  @Test public void testConfigureFromDialect() throws SqlParseException {
+    // Calcite's default converts unquoted identifiers to upper case
+    checkDialect(SqlDialect.DatabaseProduct.CALCITE.getDialect(),
+        "select unquotedColumn from \"doubleQuotedTable\"",
+        is("SELECT \"UNQUOTEDCOLUMN\"\n"
+            + "FROM \"doubleQuotedTable\""));
+    // MySQL leaves unquoted identifiers unchanged
+    checkDialect(SqlDialect.DatabaseProduct.MYSQL.getDialect(),
+        "select unquotedColumn from `doubleQuotedTable`",
+        is("SELECT `unquotedColumn`\n"
+            + "FROM `doubleQuotedTable`"));
+    // Oracle converts unquoted identifiers to upper case
+    checkDialect(SqlDialect.DatabaseProduct.ORACLE.getDialect(),
+        "select unquotedColumn from \"doubleQuotedTable\"",
+        is("SELECT \"UNQUOTEDCOLUMN\"\n"
+            + "FROM \"doubleQuotedTable\""));
+    // PostgreSQL converts unquoted identifiers to lower case
+    checkDialect(SqlDialect.DatabaseProduct.POSTGRESQL.getDialect(),
+        "select unquotedColumn from \"doubleQuotedTable\"",
+        is("SELECT \"unquotedcolumn\"\n"
+            + "FROM \"doubleQuotedTable\""));
+    // Redshift converts all identifiers to lower case
+    checkDialect(SqlDialect.DatabaseProduct.REDSHIFT.getDialect(),
+        "select unquotedColumn from \"doubleQuotedTable\"",
+        is("SELECT \"unquotedcolumn\"\n"
+            + "FROM \"doublequotedtable\""));
+  }
+
+  protected void checkDialect(SqlDialect dialect, String sql,
+      Matcher<String> matcher) throws SqlParseException {
+    final SqlParser parser = getDialectSqlParser(sql, dialect);
+    final SqlNode node = parser.parseStmt();
+    assertThat(node.toSqlString(dialect).getSql(), matcher);
+  }
+
   //~ Inner Interfaces -------------------------------------------------------
 
   /**

Reply via email to