This is an automated email from the ASF dual-hosted git repository. liyafan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit 8916c413b0b794cb6074a71d9d524ada8de668d0 Author: Marco Jorge <[email protected]> AuthorDate: Thu Dec 23 13:27:22 2021 -0500 [CALCITE-4963] Make it easier to implement interface SqlDialectFactory To create a SqlDialect, you need an instance of SqlDialect.Context, but the code to create this was until now locked in class SqlDialectFactoryImpl. This change moves that code into a public method SqlDialects.createContext(DatabaseMetaData). It is now easier to sub-class SqlDialectFactoryImpl or to directly implement SqlDialectFactory. Close apache/calcite#2658 --- .../apache/calcite/sql/SqlDialectFactoryImpl.java | 99 +-------------- .../java/org/apache/calcite/sql/SqlDialects.java | 137 +++++++++++++++++++++ .../org/apache/calcite/sql/SqlDialectsTest.java | 46 +++++++ 3 files changed, 189 insertions(+), 93 deletions(-) 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 b7d025a..d527e4d 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java @@ -16,8 +16,6 @@ */ 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; import org.apache.calcite.sql.dialect.BigQuerySqlDialect; @@ -68,35 +66,18 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory { JethroDataSqlDialect.createCache(); @Override public SqlDialect create(DatabaseMetaData databaseMetaData) { - String databaseProductName; - int databaseMajorVersion; - int databaseMinorVersion; - String databaseVersion; + SqlDialect.Context c = SqlDialects.createContext(databaseMetaData); + + String databaseProductName = c.databaseProductName(); try { - databaseProductName = databaseMetaData.getDatabaseProductName(); - databaseMajorVersion = databaseMetaData.getDatabaseMajorVersion(); - databaseMinorVersion = databaseMetaData.getDatabaseMinorVersion(); - databaseVersion = databaseMetaData.getDatabaseProductVersion(); + if (databaseProductName == null) { + databaseProductName = databaseMetaData.getDatabaseProductName(); + } } catch (SQLException e) { throw new RuntimeException("while detecting database product", e); } final String upperProductName = 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": return new AccessSqlDialect(c); @@ -175,73 +156,6 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory { } } - private static 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 static boolean isCaseSensitive(DatabaseMetaData databaseMetaData) { - try { - return databaseMetaData.supportsMixedCaseIdentifiers() - || databaseMetaData.supportsMixedCaseQuotedIdentifiers(); - } catch (SQLException e) { - throw new IllegalArgumentException("cannot deduce case-sensitivity", e); - } - } - - private static NullCollation getNullCollation(DatabaseMetaData databaseMetaData) { - try { - if (databaseMetaData.nullsAreSortedAtEnd()) { - return NullCollation.LAST; - } else if (databaseMetaData.nullsAreSortedAtStart()) { - return NullCollation.FIRST; - } else if (databaseMetaData.nullsAreSortedLow()) { - return NullCollation.LOW; - } else if (databaseMetaData.nullsAreSortedHigh()) { - return NullCollation.HIGH; - } else if (isBigQuery(databaseMetaData)) { - return NullCollation.LOW; - } else { - throw new IllegalArgumentException("cannot deduce null collation"); - } - } catch (SQLException e) { - throw new IllegalArgumentException("cannot deduce null collation", e); - } - } - - private static boolean isBigQuery(DatabaseMetaData databaseMetaData) - throws SQLException { - return databaseMetaData.getDatabaseProductName() - .equals("Google Big Query"); - } - - private static String getIdentifierQuoteString(DatabaseMetaData databaseMetaData) { - try { - return databaseMetaData.getIdentifierQuoteString(); - } catch (SQLException e) { - throw new IllegalArgumentException("cannot deduce identifier quote string", e); - } - } - /** Returns a basic dialect for a given product, or null if none is known. */ static @Nullable SqlDialect simple(SqlDialect.DatabaseProduct databaseProduct) { switch (databaseProduct) { @@ -313,5 +227,4 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory { return null; } } - } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialects.java b/core/src/main/java/org/apache/calcite/sql/SqlDialects.java new file mode 100644 index 0000000..7fa7696 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/SqlDialects.java @@ -0,0 +1,137 @@ +/* + * 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; + +import org.apache.calcite.avatica.util.Casing; +import org.apache.calcite.config.NullCollation; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +/** + * Utilities related to {@link SqlDialect}. + */ +public final class SqlDialects { + private SqlDialects() { + // utility class + } + + /** + * Extracts information from {@link DatabaseMetaData} into {@link SqlDialect.Context}. + * + * @param databaseMetaData the database metadata + * @return a context with information populated from the database metadata + */ + public static SqlDialect.Context createContext(DatabaseMetaData databaseMetaData) { + String databaseProductName; + int databaseMajorVersion; + int databaseMinorVersion; + String databaseVersion; + try { + databaseProductName = databaseMetaData.getDatabaseProductName(); + databaseMajorVersion = databaseMetaData.getDatabaseMajorVersion(); + databaseMinorVersion = databaseMetaData.getDatabaseMinorVersion(); + databaseVersion = databaseMetaData.getDatabaseProductVersion(); + } catch (SQLException e) { + throw new RuntimeException("while detecting database product", e); + } + 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); + + return c; + } + + private static String getIdentifierQuoteString(DatabaseMetaData databaseMetaData) { + try { + return databaseMetaData.getIdentifierQuoteString(); + } catch (SQLException e) { + throw new IllegalArgumentException("cannot deduce identifier quote string", e); + } + } + + private static 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 static boolean isCaseSensitive(DatabaseMetaData databaseMetaData) { + try { + return databaseMetaData.supportsMixedCaseIdentifiers() + || databaseMetaData.supportsMixedCaseQuotedIdentifiers(); + } catch (SQLException e) { + throw new IllegalArgumentException("cannot deduce case-sensitivity", e); + } + } + + private static NullCollation getNullCollation(DatabaseMetaData databaseMetaData) { + try { + if (databaseMetaData.nullsAreSortedAtEnd()) { + return NullCollation.LAST; + } else if (databaseMetaData.nullsAreSortedAtStart()) { + return NullCollation.FIRST; + } else if (databaseMetaData.nullsAreSortedLow()) { + return NullCollation.LOW; + } else if (databaseMetaData.nullsAreSortedHigh()) { + return NullCollation.HIGH; + } else if (isBigQuery(databaseMetaData)) { + return NullCollation.LOW; + } else { + throw new IllegalArgumentException("cannot deduce null collation"); + } + } catch (SQLException e) { + throw new IllegalArgumentException("cannot deduce null collation", e); + } + } + + private static boolean isBigQuery(DatabaseMetaData databaseMetaData) + throws SQLException { + return databaseMetaData.getDatabaseProductName() + .equals("Google Big Query"); + } +} diff --git a/core/src/test/java/org/apache/calcite/sql/SqlDialectsTest.java b/core/src/test/java/org/apache/calcite/sql/SqlDialectsTest.java new file mode 100644 index 0000000..d77f02d --- /dev/null +++ b/core/src/test/java/org/apache/calcite/sql/SqlDialectsTest.java @@ -0,0 +1,46 @@ +/* + * 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; + +import org.apache.calcite.jdbc.Driver; + +import org.junit.jupiter.api.Test; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.SQLException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for {@link SqlDialects}. + */ +public class SqlDialectsTest { + @Test void testCreateContextFromCalciteMetaData() throws SQLException { + Connection connection = + DriverManager.getConnection(Driver.CONNECT_STRING_PREFIX); + DatabaseMetaData metaData = connection.getMetaData(); + + SqlDialect.Context context = SqlDialects.createContext(metaData); + assertThat(context.databaseProductName(), + is(metaData.getDatabaseProductName())); + assertThat(context.databaseMajorVersion(), + is(metaData.getDatabaseMajorVersion())); + } +}
