package io.ebeaninternal.server.core;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mariadb.MariaDbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.Oracle11Platform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.Postgres9Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform;
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.DbOffline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
Create a DatabasePlatform from the configuration.
Will used platform name or use the meta data from the JDBC driver to
determine the platform automatically.
/**
* Create a DatabasePlatform from the configuration.
* <p>
* Will used platform name or use the meta data from the JDBC driver to
* determine the platform automatically.
* </p>
*/
public class DatabasePlatformFactory {
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatformFactory.class);
Create the appropriate database specific platform.
/**
* Create the appropriate database specific platform.
*/
public DatabasePlatform create(DatabaseConfig config) {
try {
String offlinePlatform = DbOffline.getPlatform();
if (offlinePlatform != null) {
logger.info("offline platform [{}]", offlinePlatform);
return byDatabaseName(offlinePlatform);
}
if (config.getDatabasePlatformName() != null) {
// choose based on dbName
return byDatabaseName(config.getDatabasePlatformName());
}
if (config.getDataSourceConfig().isOffline()) {
throw new PersistenceException("You must specify a DatabasePlatformName when you are offline");
}
// guess using meta data from driver
return byDataSource(config.getDataSource());
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
Lookup the platform by name.
/**
* Lookup the platform by name.
*/
private DatabasePlatform byDatabaseName(String dbName) {
dbName = dbName.toLowerCase();
if (dbName.equals("h2")) {
return new H2Platform();
}
if (dbName.equals("mariadb")) {
return new MariaDbPlatform();
}
if (dbName.equals("mysql")) {
return new MySqlPlatform();
}
if (dbName.equals("mysql55")) {
return new MySql55Platform();
}
if (dbName.equals("postgres") || dbName.equals("postgres9")) {
return new PostgresPlatform();
}
if (dbName.equals("oracle11") || dbName.equals("oracle10") || dbName.equals("oracle9")) {
return new Oracle11Platform();
}
if (dbName.equals("oracle")) {
return new OraclePlatform();
}
if (dbName.equals("sqlserver16")) {
return new SqlServer16Platform();
}
if (dbName.equals("sqlserver17")) {
return new SqlServer17Platform();
}
if (dbName.equals("sqlserver")) {
throw new IllegalArgumentException("Please choose the more specific sqlserver16 or sqlserver17 platform. Refer to issue #1340 for details");
}
if (dbName.equals("sqlanywhere")) {
return new SqlAnywherePlatform();
}
if (dbName.equals("db2")) {
return new DB2Platform();
}
if (dbName.equals("clickhouse")) {
return new ClickHousePlatform();
}
if (dbName.equals("nuodb")) {
return new NuoDbPlatform();
}
if (dbName.equals("sqlite")) {
return new SQLitePlatform();
}
if (dbName.equals("hana")) {
return new HanaPlatform();
}
throw new RuntimeException("database platform " + dbName + " is not known?");
}
Use JDBC DatabaseMetaData to determine the platform.
/**
* Use JDBC DatabaseMetaData to determine the platform.
*/
private DatabasePlatform byDataSource(DataSource dataSource) {
Connection connection = null;
try {
connection = dataSource.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
return byDatabaseMeta(metaData, connection);
} catch (SQLException ex) {
throw new PersistenceException(ex);
} finally {
JdbcClose.close(connection);
}
}
Find the platform by the metaData.getDatabaseProductName().
/**
* Find the platform by the metaData.getDatabaseProductName().
*/
private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData, Connection connection) throws SQLException {
String dbProductName = metaData.getDatabaseProductName().toLowerCase();
final int majorVersion = metaData.getDatabaseMajorVersion();
final int minorVersion = metaData.getDatabaseMinorVersion();
if (dbProductName.contains("oracle")) {
return new OraclePlatform();
} else if (dbProductName.contains("microsoft")) {
throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via DatabaseConfig.setDatabasePlatformName. Refer to issue #1340 for more details");
} else if (dbProductName.contains("h2")) {
return new H2Platform();
} else if (dbProductName.contains("hsql database engine")) {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
return readPostgres(connection, majorVersion);
} else if (dbProductName.contains("mariadb")) {
return new MariaDbPlatform();
} else if (dbProductName.contains("mysql")) {
return mysqlVersion(majorVersion, minorVersion);
} else if (dbProductName.contains("nuo")) {
return new NuoDbPlatform();
} else if (dbProductName.contains("sqlite")) {
return new SQLitePlatform();
} else if (dbProductName.contains("db2")) {
return new DB2Platform();
} else if (dbProductName.contains("sql anywhere")) {
return new SqlAnywherePlatform();
} else if (dbProductName.contains("hdb")) {
return new HanaPlatform();
} else if (dbProductName.contains("clickhouse")) {
return new ClickHousePlatform();
}
// use the standard one
return new DatabasePlatform();
}
private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) {
if (majorVersion <= 5 && minorVersion <= 5) {
return new MySql55Platform();
}
return new MySqlPlatform();
}
Use a select version() query as it could be Postgres or CockroachDB.
/**
* Use a select version() query as it could be Postgres or CockroachDB.
*/
private static DatabasePlatform readPostgres(Connection connection, int majorVersion) {
try (PreparedStatement statement = connection.prepareStatement("select version() as \"version\"")) {
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
String productVersion = resultSet.getString("version").toLowerCase();
if (productVersion.contains("cockroach")) {
return new CockroachPlatform();
}
}
}
} catch (SQLException e) {
logger.warn("Error running detection query on Postgres", e);
}
if (majorVersion <= 9) {
return new Postgres9Platform();
}
return new PostgresPlatform();
}
}