/* * Copyright 2010-2017 Boxfuse GmbH * * Licensed 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.flywaydb.core.internal.dbsupport; import org.flywaydb.core.api.FlywayException; import org.flywaydb.core.internal.dbsupport.db2.DB2DbSupport; import org.flywaydb.core.internal.dbsupport.db2zos.DB2zosDbSupport; import org.flywaydb.core.internal.dbsupport.derby.DerbyDbSupport; import org.flywaydb.core.internal.dbsupport.greenplum.GreenPlumDbSupport; import org.flywaydb.core.internal.dbsupport.enterprisedb.EnterpriseDBDbSupport; import org.flywaydb.core.internal.dbsupport.h2.H2DbSupport; import org.flywaydb.core.internal.dbsupport.hsql.HsqlDbSupport; import org.flywaydb.core.internal.dbsupport.mysql.MySQLDbSupport; import org.flywaydb.core.internal.dbsupport.oracle.OracleDbSupport; import org.flywaydb.core.internal.dbsupport.phoenix.PhoenixDbSupport; import org.flywaydb.core.internal.dbsupport.postgresql.PostgreSQLDbSupport; import org.flywaydb.core.internal.dbsupport.redshift.RedshfitDbSupportViaPostgreSQLDriver; import org.flywaydb.core.internal.dbsupport.redshift.RedshfitDbSupportViaRedshiftDriver; import org.flywaydb.core.internal.dbsupport.redshift.RedshiftDbSupport; import org.flywaydb.core.internal.dbsupport.saphana.SapHanaDbSupport; import org.flywaydb.core.internal.dbsupport.solid.SolidDbSupport; import org.flywaydb.core.internal.dbsupport.sqlite.SQLiteDbSupport; import org.flywaydb.core.internal.dbsupport.sqlserver.SQLServerDbSupport; import org.flywaydb.core.internal.dbsupport.sybase.ase.SybaseASEDbSupport; import org.flywaydb.core.internal.dbsupport.vertica.VerticaDbSupport; import org.flywaydb.core.internal.util.logging.Log; import org.flywaydb.core.internal.util.logging.LogFactory; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; /** * Factory for obtaining the correct DbSupport instance for the current connection. */ public class DbSupportFactory { private static final Log LOG = LogFactory.getLog(DbSupportFactory.class); /** * Prevent instantiation. */ private DbSupportFactory() { //Do nothing } /** * Initializes the appropriate DbSupport class for the database product used by the data source. * * @param connection The Jdbc connection to use to query the database. * @param printInfo Where the DB info should be printed in the logs. * @return The appropriate DbSupport class. */ public static DbSupport createDbSupport(Connection connection, boolean printInfo) { String databaseProductName = getDatabaseProductName(connection); if (printInfo) { LOG.info("Database: " + getJdbcUrl(connection) + " (" + databaseProductName + ")"); } if (databaseProductName.startsWith("Apache Derby")) { return new DerbyDbSupport(connection); } if (databaseProductName.startsWith("SQLite")) { return new SQLiteDbSupport(connection); } if (databaseProductName.startsWith("H2")) { return new H2DbSupport(connection); } if (databaseProductName.contains("HSQL Database Engine")) { // For regular Hsql and the Google Cloud SQL local default DB. return new HsqlDbSupport(connection); } if (databaseProductName.startsWith("Microsoft SQL Server")) { return new SQLServerDbSupport(connection); } if (databaseProductName.contains("MySQL")) { // For regular MySQL, MariaDB and Google Cloud SQL. // Google Cloud SQL returns different names depending on the environment and the SDK version. // ex.: Google SQL Service/MySQL return new MySQLDbSupport(connection); } if (databaseProductName.startsWith("Oracle")) { return new OracleDbSupport(connection); } if (databaseProductName.startsWith("EnterpriseDB")) { return new EnterpriseDBDbSupport(connection); } if (databaseProductName.startsWith("PostgreSQL 8")) { // Redshift reports a databaseProductName of "PostgreSQL 8.0", and it uses the same JDBC driver, // but only supports a subset of features. Therefore, we need to execute a query in order to // distinguish it from the real PostgreSQL 8: RedshiftDbSupport redshift; if ("RedshiftJDBC".equals(getDriverName(connection))) { redshift = new RedshfitDbSupportViaRedshiftDriver(connection); } else { redshift = new RedshfitDbSupportViaPostgreSQLDriver(connection); } if (redshift.detect()) { return redshift; } } if (databaseProductName.startsWith("PostgreSQL")) { return new PostgreSQLDbSupport(connection); } if (databaseProductName.startsWith("DB2")) { if (getDatabaseProductVersion(connection).startsWith("DSN")) { return new DB2zosDbSupport(connection); } else { return new DB2DbSupport(connection); } } if (databaseProductName.startsWith("Vertica")) { return new VerticaDbSupport(connection); } if (databaseProductName.contains("solidDB")) { // SolidDB was originally developed by a company named Solid and was sold afterwards to IBM. // In the meanwhile IBM also sold solidDB to Unicom Systems. // Therefore no vendor string in search criteria return new SolidDbSupport(connection); } if (databaseProductName.startsWith("Phoenix")) { return new PhoenixDbSupport(connection); } if (databaseProductName.startsWith("ASE") || databaseProductName.startsWith("Adaptive") //Newer Sybase ASE versions || databaseProductName.startsWith("sql server") // Older Sybase ASE 12.5 installations ) { return new SybaseASEDbSupport(connection); } if (databaseProductName.startsWith("HDB")) { return new SapHanaDbSupport(connection); } if (databaseProductName.startsWith("Greenplum")) { return new GreenPlumDbSupport(connection); } throw new FlywayException("Unsupported Database: " + databaseProductName); } /** * Retrieves the Jdbc Url for this connection. * * @param connection The Jdbc connection. * @return The Jdbc Url. */ private static String getJdbcUrl(Connection connection) { try { return connection.getMetaData().getURL(); } catch (SQLException e) { throw new FlywaySqlException("Unable to retrieve the Jdbc connection Url!", e); } } /** * Retrieves the name of the database product. * * @param connection The connection to use to query the database. * @return The name of the database product. Ex.: Oracle, MySQL, ... */ private static String getDatabaseProductName(Connection connection) { try { DatabaseMetaData databaseMetaData = connection.getMetaData(); if (databaseMetaData == null) { throw new FlywayException("Unable to read database metadata while it is null!"); } String databaseProductName = databaseMetaData.getDatabaseProductName(); if (databaseProductName == null) { throw new FlywayException("Unable to determine database. Product name is null."); } int databaseMajorVersion = databaseMetaData.getDatabaseMajorVersion(); int databaseMinorVersion = databaseMetaData.getDatabaseMinorVersion(); return databaseProductName + " " + databaseMajorVersion + "." + databaseMinorVersion; } catch (SQLException e) { throw new FlywaySqlException("Error while determining database product name", e); } } /** * Retrieves the database version. * * @param connection The connection to use to query the database. * @return The version of the database product. * Ex.: DSN11015 DB2 for z/OS Version 11 * SQL10050 DB" for Linux, UNIX and Windows Version 10.5 */ private static String getDatabaseProductVersion(Connection connection) { try { DatabaseMetaData databaseMetaData = connection.getMetaData(); if (databaseMetaData == null) { throw new FlywayException("Unable to read database metadata while it is null!"); } String databaseProductVersion = databaseMetaData.getDatabaseProductVersion(); if (databaseProductVersion == null) { throw new FlywayException("Unable to determine database. Product version is null."); } return databaseProductVersion; } catch (SQLException e) { throw new FlywaySqlException("Error while determining database product version", e); } } /** * Retrieves the name of the JDBC driver * * @param connection The connection to use to query the database. * @return The name of the driver. Ex: RedshiftJDBC */ private static String getDriverName(Connection connection) { try { DatabaseMetaData databaseMetaData = connection.getMetaData(); if (databaseMetaData == null) { throw new FlywayException("Unable to read database metadata while it is null!"); } String driverName = databaseMetaData.getDriverName(); if (driverName == null) { throw new FlywayException("Unable to determine JDBC driver name. JDBC driver name is null."); } return driverName; } catch (SQLException e) { throw new FlywaySqlException("Error while determining JDBC driver name", e); } } }