Skip to content

Commit 4b33d0d

Browse files
yrodieresebersole
authored andcommitted
HHH-18602 Expose determineDatabaseVersion in Dialect
1 parent 4bd1642 commit 4b33d0d

File tree

8 files changed

+177
-38
lines changed

8 files changed

+177
-38
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ protected Dialect(DatabaseVersion version) {
344344
}
345345

346346
protected Dialect(DialectResolutionInfo info) {
347-
this.version = info.makeCopyOrDefault( getMinimumSupportedVersion() );
347+
this.version = determineDatabaseVersion( info );
348348
checkVersion();
349349
registerDefaultKeywords();
350350
registerKeywords(info);
@@ -363,6 +363,17 @@ protected void checkVersion() {
363363
}
364364
}
365365

366+
/**
367+
* Determine the database version, as precise as possible and using Dialect-specific techniques,
368+
* from a {@link DialectResolutionInfo} object.
369+
* @param info The dialect resolution info that would be passed by Hibernate ORM
370+
* to the constructor of a Dialect of the same type.
371+
* @return The corresponding database version.
372+
*/
373+
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
374+
return info.makeCopyOrDefault( getMinimumSupportedVersion() );
375+
}
376+
366377
/**
367378
* Set appropriate default values for configuration properties.
368379
* <p>

hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public class H2Dialect extends Dialect {
126126
private final OptionalTableUpdateStrategy optionalTableUpdateStrategy;
127127

128128
public H2Dialect(DialectResolutionInfo info) {
129-
this( parseVersion( info ) );
129+
this( staticDetermineDatabaseVersion( info ) );
130130
registerKeywords( info );
131131
}
132132

@@ -151,7 +151,13 @@ public H2Dialect(DatabaseVersion version) {
151151
this.optionalTableUpdateStrategy = H2Dialect::usingMerge;
152152
}
153153

154-
private static DatabaseVersion parseVersion(DialectResolutionInfo info) {
154+
@Override
155+
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
156+
return staticDetermineDatabaseVersion(info);
157+
}
158+
159+
// Static version necessary to call from constructor
160+
private static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
155161
DatabaseVersion version = info.makeCopyOrDefault( MINIMUM_VERSION );
156162
if ( info.getDatabaseVersion() != null ) {
157163
version = DatabaseVersion.make( version.getMajor(), version.getMinor(), parseBuildId( info ) );

hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ protected DatabaseVersion getMinimumSupportedVersion() {
202202
return MINIMUM_VERSION;
203203
}
204204

205+
@Override
206+
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
207+
return HANAServerConfiguration.staticDetermineDatabaseVersion( info );
208+
}
205209

206210
// Use column or row tables by default
207211
public static final String USE_DEFAULT_TABLE_TYPE_COLUMN = "hibernate.dialect.hana.use_default_table_type_column";

hibernate-core/src/main/java/org/hibernate/dialect/HANAServerConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ public static HANAServerConfiguration fromDialectResolutionInfo(DialectResolutio
7575
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE
7676
);
7777
}
78-
return new HANAServerConfiguration( createVersion( info ), maxLobPrefetchSize );
78+
return new HANAServerConfiguration( staticDetermineDatabaseVersion( info ), maxLobPrefetchSize );
7979
}
8080

81-
private static DatabaseVersion createVersion(DialectResolutionInfo info) {
81+
static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
8282
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
8383
final String versionString = info.getDatabaseVersion();
8484
int majorVersion = 1;

hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,17 @@ public SQLServerDialect(DatabaseVersion version) {
174174
}
175175

176176
public SQLServerDialect(DialectResolutionInfo info) {
177-
this( determineDatabaseVersion( info ) );
177+
this( staticDetermineDatabaseVersion( info ) );
178178
registerKeywords( info );
179179
}
180180

181-
private static DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
181+
@Override
182+
public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
183+
return staticDetermineDatabaseVersion(info);
184+
}
185+
186+
// Static version necessary to call from constructor
187+
private static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
182188
final Integer compatibilityLevel = getCompatibilityLevel( info );
183189
if ( compatibilityLevel != null ) {
184190
final int majorVersion = compatibilityLevel / 10;

hibernate-core/src/main/java/org/hibernate/dialect/SimpleDatabaseVersion.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.dialect;
88

9+
import java.util.Objects;
10+
911
/**
1012
* Simple version of DatabaseVersion
1113
*/
@@ -101,4 +103,21 @@ public String toString() {
101103
}
102104
return version.toString();
103105
}
106+
107+
@Override
108+
public boolean equals(Object o) {
109+
if ( this == o ) {
110+
return true;
111+
}
112+
if ( o == null || getClass() != o.getClass() ) {
113+
return false;
114+
}
115+
SimpleDatabaseVersion that = (SimpleDatabaseVersion) o;
116+
return major == that.major && minor == that.minor && micro == that.micro;
117+
}
118+
119+
@Override
120+
public int hashCode() {
121+
return Objects.hash( major, minor, micro );
122+
}
104123
}

hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212

1313
import java.lang.invoke.MethodHandles;
1414
import java.lang.reflect.Field;
15+
import java.util.Map;
1516
import java.util.stream.Stream;
1617

1718
import org.hibernate.HibernateException;
19+
import org.hibernate.boot.registry.StandardServiceInitiator;
1820
import org.hibernate.boot.registry.StandardServiceRegistry;
1921
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
2022
import org.hibernate.cfg.AvailableSettings;
@@ -33,12 +35,18 @@
3335
import org.hibernate.dialect.SQLServerDialect;
3436
import org.hibernate.dialect.SpannerDialect;
3537
import org.hibernate.dialect.SybaseDialect;
38+
import org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl;
39+
import org.hibernate.engine.jdbc.dialect.spi.DialectFactory;
40+
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
41+
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource;
3642
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
3743
import org.hibernate.internal.CoreMessageLogger;
3844
import org.hibernate.service.spi.ServiceException;
45+
import org.hibernate.service.spi.ServiceRegistryImplementor;
3946

4047
import org.hibernate.testing.env.TestingDatabaseInfo;
4148
import org.hibernate.testing.logger.Triggerable;
49+
import org.hibernate.testing.orm.junit.DialectContext;
4250
import org.hibernate.testing.orm.junit.Jira;
4351
import org.hibernate.testing.orm.logger.LoggerInspectionExtension;
4452
import org.junit.jupiter.api.BeforeEach;
@@ -89,8 +97,7 @@ void testAccessAllowed() {
8997
.doesNotContainKeys( JdbcSettings.DIALECT, JdbcSettings.JAKARTA_HBM2DDL_DB_NAME );
9098

9199
try (StandardServiceRegistry registry = registryBuilder.build()) {
92-
final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
93-
final Dialect dialect = jdbcEnvironment.getDialect();
100+
final Dialect dialect = getDialect( registry );
94101
assertThat( dialect ).isNotNull();
95102
assertThat( dialect ).isInstanceOf( H2Dialect.class );
96103
}
@@ -134,7 +141,20 @@ static Stream<Arguments> dialects() {
134141

135142
@ParameterizedTest
136143
@MethodSource("dialects")
137-
void testAccessDisabledExplicitDialect(String productName, Class<?> dialectClass, DatabaseVersion expectedDatabaseVersion) {
144+
void testAccessDisabledExplicitDialect(String productName, Class<?> dialectClass,
145+
DatabaseVersion expectedDatabaseVersion) {
146+
try ( StandardServiceRegistry registry = createRegistryWithMetadataAccessDisabledAndDialect( dialectClass ) ) {
147+
final Dialect dialect = getDialect( registry );
148+
assertThat( dialect ).isInstanceOf( dialectClass );
149+
assertThat( dialect.getVersion() ).isEqualTo( expectedDatabaseVersion );
150+
}
151+
152+
assertThat( triggerable.triggerMessages() )
153+
.as( triggerable.toString() )
154+
.isEmpty();
155+
}
156+
157+
private StandardServiceRegistry createRegistryWithMetadataAccessDisabledAndDialect(Class<?> dialectClass) {
138158
final StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
139159
registryBuilder.clearSettings();
140160

@@ -143,16 +163,7 @@ void testAccessDisabledExplicitDialect(String productName, Class<?> dialectClass
143163
assertThat( registryBuilder.getSettings() )
144164
.doesNotContainKeys( JdbcSettings.JAKARTA_HBM2DDL_DB_NAME );
145165

146-
try (StandardServiceRegistry registry = registryBuilder.build()) {
147-
final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
148-
final Dialect dialect = jdbcEnvironment.getDialect();
149-
assertThat( dialect ).isInstanceOf( dialectClass );
150-
assertThat( dialect.getVersion() ).isEqualTo( expectedDatabaseVersion );
151-
}
152-
153-
assertThat( triggerable.triggerMessages() )
154-
.as( triggerable.toString() )
155-
.isEmpty();
166+
return registryBuilder.build();
156167
}
157168

158169
@ParameterizedTest
@@ -190,8 +201,7 @@ void testAccessDisabledNoDialectNorProductName() {
190201

191202
registryBuilder.applySetting( JdbcSettings.ALLOW_METADATA_ON_BOOT, false );
192203
try (StandardServiceRegistry registry = registryBuilder.build()) {
193-
final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
194-
final Dialect dialect = jdbcEnvironment.getDialect();
204+
final Dialect dialect = getDialect( registry );
195205
fail( "Should fail to boot - " + dialect );
196206
}
197207
catch (ServiceException expected) {
@@ -201,6 +211,50 @@ void testAccessDisabledNoDialectNorProductName() {
201211
}
202212
}
203213

214+
@Test
215+
void testDetermineDatabaseVersion() {
216+
final Dialect metadataAccessDisabledDialect;
217+
try ( StandardServiceRegistry registry =
218+
createRegistryWithMetadataAccessDisabledAndDialect( DialectContext.getDialectClass() ) ) {
219+
// The version on this dialect may be anything, but most likely will be the minimum version.
220+
// We're not interested in that, but in how determineDatabaseVersion() behaves for this dialect,
221+
// when passed actual resolution info -- which Quarkus may do.
222+
metadataAccessDisabledDialect = getDialect( registry );
223+
}
224+
225+
try ( StandardServiceRegistry registry = createRegistryWithTestedDatabaseAndMetadataAccessAllowed() ) {
226+
final Dialect metadataAccessAllowedDialect = getDialect( registry );
227+
228+
// We expect determineDatabaseVersion(), when called on metadataAccessDisabledDialect,
229+
// to return the version that would have been returned,
230+
// had we booted up with auto-detection of version (metadata access allowed).
231+
assertThat( metadataAccessDisabledDialect.determineDatabaseVersion( getDialectResolutionInfo( registry ) ) )
232+
.isEqualTo( metadataAccessAllowedDialect.getVersion() );
233+
}
234+
}
235+
236+
private StandardServiceRegistry createRegistryWithTestedDatabaseAndMetadataAccessAllowed() {
237+
final StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
238+
239+
registryBuilder.addInitiator( new CapturingDialectFactory.Initiator() );
240+
241+
// allow access to the jdbc metadata
242+
registryBuilder.applySetting( JdbcSettings.ALLOW_METADATA_ON_BOOT, true );
243+
244+
// leave connection info as defined in global test configuration (most likely system properties)
245+
246+
return registryBuilder.build();
247+
}
248+
249+
private static Dialect getDialect(StandardServiceRegistry registry) {
250+
return registry.getService( JdbcEnvironment.class ).getDialect();
251+
}
252+
253+
private static DialectResolutionInfo getDialectResolutionInfo(StandardServiceRegistry registry) {
254+
return ( (CapturingDialectFactory) registry.getService( DialectFactory.class ) )
255+
.capturedDialectResolutionInfoSource.getDialectResolutionInfo();
256+
}
257+
204258
// Ugly hack because neither MINIMUM_VERSION nor getMinimumSupportedVersion()
205259
// can be accessed from this test.
206260
private static DatabaseVersion getVersionConstant(Class<? extends Dialect> dialectClass, String versionConstantName) {
@@ -213,4 +267,29 @@ private static DatabaseVersion getVersionConstant(Class<? extends Dialect> diale
213267
throw new RuntimeException( "Error extracting '" + versionConstantName + "' from '" + dialectClass + "'", e );
214268
}
215269
}
270+
271+
// A hack to easily retrieve DialectResolutionInfo exactly as it would be constructed by Hibernate ORM
272+
private static class CapturingDialectFactory extends DialectFactoryImpl {
273+
static class Initiator implements StandardServiceInitiator<DialectFactory> {
274+
@Override
275+
public Class<DialectFactory> getServiceInitiated() {
276+
return DialectFactory.class;
277+
}
278+
279+
@Override
280+
public DialectFactory initiateService(Map<String, Object> configurationValues,
281+
ServiceRegistryImplementor registry) {
282+
return new CapturingDialectFactory();
283+
}
284+
}
285+
286+
DialectResolutionInfoSource capturedDialectResolutionInfoSource;
287+
288+
@Override
289+
public Dialect buildDialect(Map<String, Object> configValues, DialectResolutionInfoSource resolutionInfoSource)
290+
throws HibernateException {
291+
this.capturedDialectResolutionInfoSource = resolutionInfoSource;
292+
return super.buildDialect( configValues, resolutionInfoSource );
293+
}
294+
}
216295
}

hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectContext.java

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,47 @@
2525
*/
2626
public final class DialectContext {
2727

28+
private static Class<? extends Dialect> dialectClass;
2829
private static Dialect dialect;
2930

30-
static void init() {
31+
static void initDialectClass() {
3132
final Properties properties = Environment.getProperties();
32-
final String diverClassName = properties.getProperty( Environment.DRIVER );
3333
final String dialectName = properties.getProperty( Environment.DIALECT );
34-
final String jdbcUrl = properties.getProperty( Environment.URL );
35-
final Properties props = new Properties();
36-
props.setProperty( "user", properties.getProperty( Environment.USER ) );
37-
props.setProperty( "password", properties.getProperty( Environment.PASS ) );
3834
if ( dialectName == null ) {
3935
throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
4036
}
41-
final Constructor<? extends Dialect> constructor;
4237
try {
43-
@SuppressWarnings("unchecked")
44-
final Class<? extends Dialect> dialectClass = (Class<? extends Dialect>) ReflectHelper.classForName( dialectName );
45-
constructor = dialectClass.getConstructor( DialectResolutionInfo.class );
38+
dialectClass = (Class<? extends Dialect>) ReflectHelper.classForName( dialectName );
4639
}
4740
catch (ClassNotFoundException cnfe) {
4841
throw new HibernateException( "Dialect class not found: " + dialectName, cnfe );
4942
}
43+
}
44+
45+
static void init() {
46+
final Properties properties = Environment.getProperties();
47+
final String driverClassName = properties.getProperty( Environment.DRIVER );
48+
final String jdbcUrl = properties.getProperty( Environment.URL );
49+
final Properties props = new Properties();
50+
props.setProperty( "user", properties.getProperty( Environment.USER ) );
51+
props.setProperty( "password", properties.getProperty( Environment.PASS ) );
52+
final Class<? extends Dialect> dialectClass = getDialectClass();
53+
final Constructor<? extends Dialect> constructor;
54+
try {
55+
constructor = dialectClass.getConstructor( DialectResolutionInfo.class );
56+
}
5057
catch (Exception e) {
51-
throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e );
58+
throw new HibernateException( "Could not instantiate given dialect class: " + dialectClass, e );
5259
}
5360
final Driver driver;
5461
try {
55-
driver = (Driver) Class.forName( diverClassName ).newInstance();
62+
driver = (Driver) Class.forName( driverClassName ).newInstance();
5663
}
5764
catch (ClassNotFoundException cnfe) {
58-
throw new HibernateException( "JDBC Driver class not found: " + dialectName, cnfe );
65+
throw new HibernateException( "JDBC Driver class not found: " + driverClassName, cnfe );
5966
}
6067
catch (Exception e) {
61-
throw new HibernateException( "Could not instantiate given JDBC driver class: " + dialectName, e );
68+
throw new HibernateException( "Could not instantiate given JDBC driver class: " + driverClassName, e );
6269
}
6370
try ( Connection connection = driver.connect( jdbcUrl, props ) ) {
6471
// if ( jdbcUrl.startsWith( "jdbc:derby:" ) ) {
@@ -77,15 +84,22 @@ static void init() {
7784
+ jdbcUrl + "' [" + sqle.getMessage() + "]", sqle );
7885
}
7986
catch (Exception e) {
80-
throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e );
87+
throw new HibernateException( "Could not connect to database with dialect class: " + dialectClass, e );
8188
}
8289
}
8390

8491
private DialectContext() {
8592
}
8693

94+
public static synchronized Class<? extends Dialect> getDialectClass() {
95+
if ( dialectClass == null ) {
96+
initDialectClass();
97+
}
98+
return dialectClass;
99+
}
100+
87101
public static synchronized Dialect getDialect() {
88-
if (dialect==null) {
102+
if (dialect == null) {
89103
init();
90104
}
91105
return dialect;

0 commit comments

Comments
 (0)