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 -------------------------------------------------------
/**