From e54a6e15b2231b40782356de4563f4a2e97d60cb Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 7 Aug 2024 14:29:55 +0200 Subject: [PATCH] HHH-18463 Add AzureDialect and determine SQL Server version based on compatibility level --- .../cfg/DialectSpecificSettings.java | 6 ++++ .../dialect/AzureSQLServerDialect.java | 25 +++++++++++++ .../hibernate/dialect/SQLServerDialect.java | 36 +++++++++++++++++-- 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/AzureSQLServerDialect.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java index de4f24e239..fe5a2ce80e 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java @@ -67,6 +67,12 @@ public interface DialectSpecificSettings { */ public static final String COCKROACH_VERSION_STRING = "hibernate.dialect.cockroach.version_string"; + /** + * Specifies the compatibility level of the SQL Server database as returned by {@code select compatibility_level from sys.databases}. + * The number has three digits, the first two digits are the major version, the last digit is the minor version. + */ + public static final String SQL_SERVER_COMPATIBILITY_LEVEL = "hibernate.dialect.sqlserver.compatibility_level"; + /** * Specifies the LOB prefetch size. LOBs larger than this value will be read into memory as the HANA JDBC driver closes * the LOB when the result set is closed. diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AzureSQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AzureSQLServerDialect.java new file mode 100644 index 0000000000..deb53a0dac --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AzureSQLServerDialect.java @@ -0,0 +1,25 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.dialect; + +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; + +/** + * A {@linkplain Dialect SQL dialect} for Azure SQL Server. + */ +public class AzureSQLServerDialect extends SQLServerDialect { + + public AzureSQLServerDialect() { + // Azure SQL Server always is the latest version, so default to a high number + super( DatabaseVersion.make( Integer.MAX_VALUE ) ); + } + + public AzureSQLServerDialect(DialectResolutionInfo info) { + this(); + registerKeywords( info ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 2a4afa1877..1ecf96529d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -7,6 +7,7 @@ package org.hibernate.dialect; import java.sql.DatabaseMetaData; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.time.temporal.ChronoField; @@ -40,6 +41,7 @@ import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.Size; +import org.hibernate.engine.jdbc.dialect.spi.BasicSQLExceptionConverter; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; @@ -52,6 +54,7 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.hibernate.internal.util.JdbcExceptionHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.mapping.Column; import org.hibernate.persister.entity.mutation.EntityMutationTarget; import org.hibernate.procedure.internal.SQLServerCallableStatementSupport; @@ -89,6 +92,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import jakarta.persistence.TemporalType; +import static org.hibernate.cfg.DialectSpecificSettings.SQL_SERVER_COMPATIBILITY_LEVEL; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; @@ -174,8 +178,36 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { } public SQLServerDialect(DialectResolutionInfo info) { - super(info); - exporter = createSequenceExporter(info); + this( determineDatabaseVersion( info ) ); + registerKeywords( info ); + } + + private static DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) { + final Integer compatibilityLevel = getCompatibilityLevel( info ); + if ( compatibilityLevel != null ) { + final int majorVersion = compatibilityLevel / 10; + final int minorVersion = compatibilityLevel % 10; + return DatabaseVersion.make( majorVersion, minorVersion ); + } + return info.makeCopyOrDefault( MINIMUM_VERSION ); + } + + private static Integer getCompatibilityLevel(DialectResolutionInfo info) { + final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata(); + if ( databaseMetaData != null ) { + try ( java.sql.Statement statement = databaseMetaData.getConnection().createStatement() ) { + final ResultSet rs = statement.executeQuery( "SELECT compatibility_level FROM sys.databases where name = db_name();" ); + if ( rs.next() ) { + return rs.getInt( 1 ); + } + } + catch (SQLException e) { + throw BasicSQLExceptionConverter.INSTANCE.convert( e ); + } + } + + // default to the dialect-specific configuration setting + return ConfigurationHelper.getInteger( SQL_SERVER_COMPATIBILITY_LEVEL, info.getConfigurationValues() ); } private StandardSequenceExporter createSequenceExporter(DatabaseVersion version) {