HHH-18602 Expose `determineDatabaseVersion` in `Dialect`
This commit is contained in:
parent
4bd164222f
commit
4b33d0d067
|
@ -344,7 +344,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
}
|
||||
|
||||
protected Dialect(DialectResolutionInfo info) {
|
||||
this.version = info.makeCopyOrDefault( getMinimumSupportedVersion() );
|
||||
this.version = determineDatabaseVersion( info );
|
||||
checkVersion();
|
||||
registerDefaultKeywords();
|
||||
registerKeywords(info);
|
||||
|
@ -363,6 +363,17 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the database version, as precise as possible and using Dialect-specific techniques,
|
||||
* from a {@link DialectResolutionInfo} object.
|
||||
* @param info The dialect resolution info that would be passed by Hibernate ORM
|
||||
* to the constructor of a Dialect of the same type.
|
||||
* @return The corresponding database version.
|
||||
*/
|
||||
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
|
||||
return info.makeCopyOrDefault( getMinimumSupportedVersion() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set appropriate default values for configuration properties.
|
||||
* <p>
|
||||
|
|
|
@ -126,7 +126,7 @@ public class H2Dialect extends Dialect {
|
|||
private final OptionalTableUpdateStrategy optionalTableUpdateStrategy;
|
||||
|
||||
public H2Dialect(DialectResolutionInfo info) {
|
||||
this( parseVersion( info ) );
|
||||
this( staticDetermineDatabaseVersion( info ) );
|
||||
registerKeywords( info );
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,13 @@ public class H2Dialect extends Dialect {
|
|||
this.optionalTableUpdateStrategy = H2Dialect::usingMerge;
|
||||
}
|
||||
|
||||
private static DatabaseVersion parseVersion(DialectResolutionInfo info) {
|
||||
@Override
|
||||
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
|
||||
return staticDetermineDatabaseVersion(info);
|
||||
}
|
||||
|
||||
// Static version necessary to call from constructor
|
||||
private static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
|
||||
DatabaseVersion version = info.makeCopyOrDefault( MINIMUM_VERSION );
|
||||
if ( info.getDatabaseVersion() != null ) {
|
||||
version = DatabaseVersion.make( version.getMajor(), version.getMinor(), parseBuildId( info ) );
|
||||
|
|
|
@ -202,6 +202,10 @@ public class HANADialect extends Dialect {
|
|||
return MINIMUM_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
|
||||
return HANAServerConfiguration.staticDetermineDatabaseVersion( info );
|
||||
}
|
||||
|
||||
// Use column or row tables by default
|
||||
public static final String USE_DEFAULT_TABLE_TYPE_COLUMN = "hibernate.dialect.hana.use_default_table_type_column";
|
||||
|
|
|
@ -75,10 +75,10 @@ public class HANAServerConfiguration {
|
|||
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE
|
||||
);
|
||||
}
|
||||
return new HANAServerConfiguration( createVersion( info ), maxLobPrefetchSize );
|
||||
return new HANAServerConfiguration( staticDetermineDatabaseVersion( info ), maxLobPrefetchSize );
|
||||
}
|
||||
|
||||
private static DatabaseVersion createVersion(DialectResolutionInfo info) {
|
||||
static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
|
||||
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
|
||||
final String versionString = info.getDatabaseVersion();
|
||||
int majorVersion = 1;
|
||||
|
|
|
@ -174,11 +174,17 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
}
|
||||
|
||||
public SQLServerDialect(DialectResolutionInfo info) {
|
||||
this( determineDatabaseVersion( info ) );
|
||||
this( staticDetermineDatabaseVersion( info ) );
|
||||
registerKeywords( info );
|
||||
}
|
||||
|
||||
private static DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
|
||||
@Override
|
||||
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
|
||||
return staticDetermineDatabaseVersion(info);
|
||||
}
|
||||
|
||||
// Static version necessary to call from constructor
|
||||
private static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
|
||||
final Integer compatibilityLevel = getCompatibilityLevel( info );
|
||||
if ( compatibilityLevel != null ) {
|
||||
final int majorVersion = compatibilityLevel / 10;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Simple version of DatabaseVersion
|
||||
*/
|
||||
|
@ -101,4 +103,21 @@ public class SimpleDatabaseVersion implements DatabaseVersion {
|
|||
}
|
||||
return version.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
SimpleDatabaseVersion that = (SimpleDatabaseVersion) o;
|
||||
return major == that.major && minor == that.minor && micro == that.micro;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( major, minor, micro );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,11 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.boot.registry.StandardServiceInitiator;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
@ -33,12 +35,18 @@ import org.hibernate.dialect.PostgresPlusDialect;
|
|||
import org.hibernate.dialect.SQLServerDialect;
|
||||
import org.hibernate.dialect.SpannerDialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectFactory;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.service.spi.ServiceException;
|
||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||
|
||||
import org.hibernate.testing.env.TestingDatabaseInfo;
|
||||
import org.hibernate.testing.logger.Triggerable;
|
||||
import org.hibernate.testing.orm.junit.DialectContext;
|
||||
import org.hibernate.testing.orm.junit.Jira;
|
||||
import org.hibernate.testing.orm.logger.LoggerInspectionExtension;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -89,8 +97,7 @@ public class MetadataAccessTests {
|
|||
.doesNotContainKeys( JdbcSettings.DIALECT, JdbcSettings.JAKARTA_HBM2DDL_DB_NAME );
|
||||
|
||||
try (StandardServiceRegistry registry = registryBuilder.build()) {
|
||||
final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
|
||||
final Dialect dialect = jdbcEnvironment.getDialect();
|
||||
final Dialect dialect = getDialect( registry );
|
||||
assertThat( dialect ).isNotNull();
|
||||
assertThat( dialect ).isInstanceOf( H2Dialect.class );
|
||||
}
|
||||
|
@ -134,7 +141,20 @@ public class MetadataAccessTests {
|
|||
|
||||
@ParameterizedTest
|
||||
@MethodSource("dialects")
|
||||
void testAccessDisabledExplicitDialect(String productName, Class<?> dialectClass, DatabaseVersion expectedDatabaseVersion) {
|
||||
void testAccessDisabledExplicitDialect(String productName, Class<?> dialectClass,
|
||||
DatabaseVersion expectedDatabaseVersion) {
|
||||
try ( StandardServiceRegistry registry = createRegistryWithMetadataAccessDisabledAndDialect( dialectClass ) ) {
|
||||
final Dialect dialect = getDialect( registry );
|
||||
assertThat( dialect ).isInstanceOf( dialectClass );
|
||||
assertThat( dialect.getVersion() ).isEqualTo( expectedDatabaseVersion );
|
||||
}
|
||||
|
||||
assertThat( triggerable.triggerMessages() )
|
||||
.as( triggerable.toString() )
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
private StandardServiceRegistry createRegistryWithMetadataAccessDisabledAndDialect(Class<?> dialectClass) {
|
||||
final StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
|
||||
registryBuilder.clearSettings();
|
||||
|
||||
|
@ -143,16 +163,7 @@ public class MetadataAccessTests {
|
|||
assertThat( registryBuilder.getSettings() )
|
||||
.doesNotContainKeys( JdbcSettings.JAKARTA_HBM2DDL_DB_NAME );
|
||||
|
||||
try (StandardServiceRegistry registry = registryBuilder.build()) {
|
||||
final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
|
||||
final Dialect dialect = jdbcEnvironment.getDialect();
|
||||
assertThat( dialect ).isInstanceOf( dialectClass );
|
||||
assertThat( dialect.getVersion() ).isEqualTo( expectedDatabaseVersion );
|
||||
}
|
||||
|
||||
assertThat( triggerable.triggerMessages() )
|
||||
.as( triggerable.toString() )
|
||||
.isEmpty();
|
||||
return registryBuilder.build();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
@ -190,8 +201,7 @@ public class MetadataAccessTests {
|
|||
|
||||
registryBuilder.applySetting( JdbcSettings.ALLOW_METADATA_ON_BOOT, false );
|
||||
try (StandardServiceRegistry registry = registryBuilder.build()) {
|
||||
final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
|
||||
final Dialect dialect = jdbcEnvironment.getDialect();
|
||||
final Dialect dialect = getDialect( registry );
|
||||
fail( "Should fail to boot - " + dialect );
|
||||
}
|
||||
catch (ServiceException expected) {
|
||||
|
@ -201,6 +211,50 @@ public class MetadataAccessTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDetermineDatabaseVersion() {
|
||||
final Dialect metadataAccessDisabledDialect;
|
||||
try ( StandardServiceRegistry registry =
|
||||
createRegistryWithMetadataAccessDisabledAndDialect( DialectContext.getDialectClass() ) ) {
|
||||
// The version on this dialect may be anything, but most likely will be the minimum version.
|
||||
// We're not interested in that, but in how determineDatabaseVersion() behaves for this dialect,
|
||||
// when passed actual resolution info -- which Quarkus may do.
|
||||
metadataAccessDisabledDialect = getDialect( registry );
|
||||
}
|
||||
|
||||
try ( StandardServiceRegistry registry = createRegistryWithTestedDatabaseAndMetadataAccessAllowed() ) {
|
||||
final Dialect metadataAccessAllowedDialect = getDialect( registry );
|
||||
|
||||
// We expect determineDatabaseVersion(), when called on metadataAccessDisabledDialect,
|
||||
// to return the version that would have been returned,
|
||||
// had we booted up with auto-detection of version (metadata access allowed).
|
||||
assertThat( metadataAccessDisabledDialect.determineDatabaseVersion( getDialectResolutionInfo( registry ) ) )
|
||||
.isEqualTo( metadataAccessAllowedDialect.getVersion() );
|
||||
}
|
||||
}
|
||||
|
||||
private StandardServiceRegistry createRegistryWithTestedDatabaseAndMetadataAccessAllowed() {
|
||||
final StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
|
||||
|
||||
registryBuilder.addInitiator( new CapturingDialectFactory.Initiator() );
|
||||
|
||||
// allow access to the jdbc metadata
|
||||
registryBuilder.applySetting( JdbcSettings.ALLOW_METADATA_ON_BOOT, true );
|
||||
|
||||
// leave connection info as defined in global test configuration (most likely system properties)
|
||||
|
||||
return registryBuilder.build();
|
||||
}
|
||||
|
||||
private static Dialect getDialect(StandardServiceRegistry registry) {
|
||||
return registry.getService( JdbcEnvironment.class ).getDialect();
|
||||
}
|
||||
|
||||
private static DialectResolutionInfo getDialectResolutionInfo(StandardServiceRegistry registry) {
|
||||
return ( (CapturingDialectFactory) registry.getService( DialectFactory.class ) )
|
||||
.capturedDialectResolutionInfoSource.getDialectResolutionInfo();
|
||||
}
|
||||
|
||||
// Ugly hack because neither MINIMUM_VERSION nor getMinimumSupportedVersion()
|
||||
// can be accessed from this test.
|
||||
private static DatabaseVersion getVersionConstant(Class<? extends Dialect> dialectClass, String versionConstantName) {
|
||||
|
@ -213,4 +267,29 @@ public class MetadataAccessTests {
|
|||
throw new RuntimeException( "Error extracting '" + versionConstantName + "' from '" + dialectClass + "'", e );
|
||||
}
|
||||
}
|
||||
|
||||
// A hack to easily retrieve DialectResolutionInfo exactly as it would be constructed by Hibernate ORM
|
||||
private static class CapturingDialectFactory extends DialectFactoryImpl {
|
||||
static class Initiator implements StandardServiceInitiator<DialectFactory> {
|
||||
@Override
|
||||
public Class<DialectFactory> getServiceInitiated() {
|
||||
return DialectFactory.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectFactory initiateService(Map<String, Object> configurationValues,
|
||||
ServiceRegistryImplementor registry) {
|
||||
return new CapturingDialectFactory();
|
||||
}
|
||||
}
|
||||
|
||||
DialectResolutionInfoSource capturedDialectResolutionInfoSource;
|
||||
|
||||
@Override
|
||||
public Dialect buildDialect(Map<String, Object> configValues, DialectResolutionInfoSource resolutionInfoSource)
|
||||
throws HibernateException {
|
||||
this.capturedDialectResolutionInfoSource = resolutionInfoSource;
|
||||
return super.buildDialect( configValues, resolutionInfoSource );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,40 +25,47 @@ import org.hibernate.internal.util.ReflectHelper;
|
|||
*/
|
||||
public final class DialectContext {
|
||||
|
||||
private static Class<? extends Dialect> dialectClass;
|
||||
private static Dialect dialect;
|
||||
|
||||
static void init() {
|
||||
static void initDialectClass() {
|
||||
final Properties properties = Environment.getProperties();
|
||||
final String diverClassName = properties.getProperty( Environment.DRIVER );
|
||||
final String dialectName = properties.getProperty( Environment.DIALECT );
|
||||
final String jdbcUrl = properties.getProperty( Environment.URL );
|
||||
final Properties props = new Properties();
|
||||
props.setProperty( "user", properties.getProperty( Environment.USER ) );
|
||||
props.setProperty( "password", properties.getProperty( Environment.PASS ) );
|
||||
if ( dialectName == null ) {
|
||||
throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
|
||||
}
|
||||
final Constructor<? extends Dialect> constructor;
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<? extends Dialect> dialectClass = (Class<? extends Dialect>) ReflectHelper.classForName( dialectName );
|
||||
constructor = dialectClass.getConstructor( DialectResolutionInfo.class );
|
||||
dialectClass = (Class<? extends Dialect>) ReflectHelper.classForName( dialectName );
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
throw new HibernateException( "Dialect class not found: " + dialectName, cnfe );
|
||||
}
|
||||
}
|
||||
|
||||
static void init() {
|
||||
final Properties properties = Environment.getProperties();
|
||||
final String driverClassName = properties.getProperty( Environment.DRIVER );
|
||||
final String jdbcUrl = properties.getProperty( Environment.URL );
|
||||
final Properties props = new Properties();
|
||||
props.setProperty( "user", properties.getProperty( Environment.USER ) );
|
||||
props.setProperty( "password", properties.getProperty( Environment.PASS ) );
|
||||
final Class<? extends Dialect> dialectClass = getDialectClass();
|
||||
final Constructor<? extends Dialect> constructor;
|
||||
try {
|
||||
constructor = dialectClass.getConstructor( DialectResolutionInfo.class );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e );
|
||||
throw new HibernateException( "Could not instantiate given dialect class: " + dialectClass, e );
|
||||
}
|
||||
final Driver driver;
|
||||
try {
|
||||
driver = (Driver) Class.forName( diverClassName ).newInstance();
|
||||
driver = (Driver) Class.forName( driverClassName ).newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
throw new HibernateException( "JDBC Driver class not found: " + dialectName, cnfe );
|
||||
throw new HibernateException( "JDBC Driver class not found: " + driverClassName, cnfe );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new HibernateException( "Could not instantiate given JDBC driver class: " + dialectName, e );
|
||||
throw new HibernateException( "Could not instantiate given JDBC driver class: " + driverClassName, e );
|
||||
}
|
||||
try ( Connection connection = driver.connect( jdbcUrl, props ) ) {
|
||||
// if ( jdbcUrl.startsWith( "jdbc:derby:" ) ) {
|
||||
|
@ -77,13 +84,20 @@ public final class DialectContext {
|
|||
+ jdbcUrl + "' [" + sqle.getMessage() + "]", sqle );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e );
|
||||
throw new HibernateException( "Could not connect to database with dialect class: " + dialectClass, e );
|
||||
}
|
||||
}
|
||||
|
||||
private DialectContext() {
|
||||
}
|
||||
|
||||
public static synchronized Class<? extends Dialect> getDialectClass() {
|
||||
if ( dialectClass == null ) {
|
||||
initDialectClass();
|
||||
}
|
||||
return dialectClass;
|
||||
}
|
||||
|
||||
public static synchronized Dialect getDialect() {
|
||||
if (dialect == null) {
|
||||
init();
|
||||
|
|
Loading…
Reference in New Issue