HHH-17425 Introduce some new dialect-specific config params

This commit is contained in:
Marco Belladelli 2023-11-16 10:05:39 +01:00
parent 1439e4a9a8
commit ac637ea8af
18 changed files with 483 additions and 33 deletions

View File

@ -544,6 +544,11 @@ settingsDocumentation {
description = "Settings used in the integration of Jakarta Validation" description = "Settings used in the integration of Jakarta Validation"
settingsClassName "org.hibernate.cfg.ValidationSettings" settingsClassName "org.hibernate.cfg.ValidationSettings"
} }
dialect {
summary = "Dialect Specific Settings"
description = "Settings used for dialect configuration when the database isn't available"
settingsClassName "org.hibernate.cfg.DialectSpecificSettings"
}
envers { envers {
summary = "Audit/History Settings" summary = "Audit/History Settings"
description = "Settings which control Hibernate's audit/history support (hibernate-envers)" description = "Settings which control Hibernate's audit/history support (hibernate-envers)"

View File

@ -61,7 +61,7 @@ public class MariaDBLegacyDialect extends MySQLLegacyDialect {
} }
public MariaDBLegacyDialect(DialectResolutionInfo info) { public MariaDBLegacyDialect(DialectResolutionInfo info) {
super( createVersion( info ), MySQLServerConfiguration.fromDatabaseMetadata( info.getDatabaseMetadata() ) ); super( createVersion( info ), MySQLServerConfiguration.fromDialectResolutionInfo( info ) );
registerKeywords( info ); registerKeywords( info );
} }

View File

@ -161,7 +161,7 @@ public class MySQLLegacyDialect extends Dialect {
} }
public MySQLLegacyDialect(DialectResolutionInfo info) { public MySQLLegacyDialect(DialectResolutionInfo info) {
this( createVersion( info ), MySQLServerConfiguration.fromDatabaseMetadata( info.getDatabaseMetadata() ) ); this( createVersion( info ), MySQLServerConfiguration.fromDialectResolutionInfo( info ) );
registerKeywords( info ); registerKeywords( info );
} }

View File

@ -0,0 +1,60 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.cfg;
/**
* Settings used as fallback to configure aspects of specific {@link org.hibernate.dialect.Dialect}s
* when the boot process does not have access to a {@link java.sql.DatabaseMetaData} object or
* its underlying JDBC {@link java.sql.Connection}.
*
* @author Marco Belladelli
*/
public interface DialectSpecificSettings {
/**
* Specifies whether this database is running on an Autonomous Database Cloud Service.
*
* @settingDefault {@code false}
*/
public static final String ORACLE_AUTONOMOUS_DATABASE = "hibernate.dialect.oracle.is_autonomous";
/**
* Specifies whether this database's {@code MAX_STRING_SIZE} is set to {@code EXTENDED}.
*
* @settingDefault {@code false}
*/
public static final String ORACLE_EXTENDED_STRING_SIZE = "hibernate.dialect.oracle.extended_string_size";
/**
* Specifies whether this database's {@code ansinull} setting is enabled.
*
* @settingDefault {@code false}
*/
public static final String SYBASE_ANSI_NULL = "hibernate.dialect.sybase.extended_string_size";
/**
* Specifies the bytes per character to use based on the database's configured
* <a href="https://dev.mysql.com/doc/refman/8.0/en/charset-charsets.html">charset</a>.
*
* @settingDefault {@code 4}
*/
public static final String MYSQL_BYTES_PER_CHARACTER = "hibernate.dialect.mysql.bytes_per_character";
/**
* Specifies whether the {@code NO_BACKSLASH_ESCAPES} sql mode is enabled.
*
* @settingDefault {@code false}
*/
public static final String MYSQL_NO_BACKSLASH_ESCAPES = "hibernate.dialect.mysql.no_backslash_escapes";
/**
* Specifies a custom CockroachDB version string. The expected format of the string is
* the one returned from the {@code version()} function, e.g.:
* {@code "CockroachDB CCL v23.1.8 (x86_64-pc-linux-gnu, built 2023/08/04 18:11:44, go1.19.10)"}
*/
public static final String COCKROACH_VERSION_STRING = "hibernate.dialect.cockroach.version_string";
}

View File

@ -47,6 +47,7 @@ import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.query.SemanticException; import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.sqm.TemporalUnit;
@ -75,6 +76,7 @@ import org.jboss.logging.Logger;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.cfg.DialectSpecificSettings.COCKROACH_VERSION_STRING;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.TemporalUnit.DAY; import static org.hibernate.query.sqm.TemporalUnit.DAY;
import static org.hibernate.query.sqm.TemporalUnit.EPOCH; import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
@ -138,6 +140,14 @@ public class CockroachDialect extends Dialect {
registerKeywords( info ); registerKeywords( info );
} }
public CockroachDialect(DialectResolutionInfo info, String versionString) {
this(
versionString != null ? parseVersion( versionString ) : info.makeCopy(),
PostgreSQLDriverKind.determineKind( info )
);
registerKeywords( info );
}
public CockroachDialect(DatabaseVersion version) { public CockroachDialect(DatabaseVersion version) {
super(version); super(version);
driverKind = PostgreSQLDriverKind.PG_JDBC; driverKind = PostgreSQLDriverKind.PG_JDBC;
@ -148,10 +158,10 @@ public class CockroachDialect extends Dialect {
this.driverKind = driverKind; this.driverKind = driverKind;
} }
protected static DatabaseVersion fetchDataBaseVersion( DialectResolutionInfo info ) { protected static DatabaseVersion fetchDataBaseVersion(DialectResolutionInfo info) {
String versionString = null; String versionString = null;
if ( info.getDatabaseMetadata() != null ) { if ( info.getDatabaseMetadata() != null ) {
try (java.sql.Statement s = info.getDatabaseMetadata().getConnection().createStatement() ) { try (java.sql.Statement s = info.getDatabaseMetadata().getConnection().createStatement()) {
final ResultSet rs = s.executeQuery( "SELECT version()" ); final ResultSet rs = s.executeQuery( "SELECT version()" );
if ( rs.next() ) { if ( rs.next() ) {
versionString = rs.getString( 1 ); versionString = rs.getString( 1 );
@ -161,7 +171,11 @@ public class CockroachDialect extends Dialect {
// Ignore // Ignore
} }
} }
return parseVersion( versionString ); if ( versionString == null ) {
// default to the dialect-specific configuration setting
versionString = ConfigurationHelper.getString( COCKROACH_VERSION_STRING, info.getConfigurationValues() );
}
return versionString != null ? parseVersion( versionString ) : info.makeCopy();
} }
public static DatabaseVersion parseVersion( String versionString ) { public static DatabaseVersion parseVersion( String versionString ) {

View File

@ -13,6 +13,9 @@ import java.sql.Statement;
import org.hibernate.engine.jdbc.dialect.spi.BasicSQLExceptionConverter; import org.hibernate.engine.jdbc.dialect.spi.BasicSQLExceptionConverter;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.internal.util.config.ConfigurationHelper;
import static org.hibernate.cfg.DialectSpecificSettings.COCKROACH_VERSION_STRING;
/** /**
* A list of relational database systems for which Hibernate can resolve a {@link Dialect}. * A list of relational database systems for which Hibernate can resolve a {@link Dialect}.
@ -207,9 +210,9 @@ public enum Database {
POSTGRESQL { POSTGRESQL {
@Override @Override
public Dialect createDialect(DialectResolutionInfo info) { public Dialect createDialect(DialectResolutionInfo info) {
final String version = getVersion( info.getDatabaseMetadata() ); final String version = getVersion( info );
if ( version.startsWith( "Cockroach" ) ) { if ( version.startsWith( "Cockroach" ) ) {
return new CockroachDialect( info ); return new CockroachDialect( info, version );
} }
return new PostgreSQLDialect( info ); return new PostgreSQLDialect( info );
} }
@ -221,7 +224,8 @@ public enum Database {
public String getDriverClassName(String jdbcUrl) { public String getDriverClassName(String jdbcUrl) {
return "org.postgresql.Driver"; return "org.postgresql.Driver";
} }
private String getVersion(DatabaseMetaData databaseMetaData) { private String getVersion(DialectResolutionInfo info) {
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
if ( databaseMetaData != null ) { if ( databaseMetaData != null ) {
try ( Statement statement = databaseMetaData.getConnection().createStatement() ) { try ( Statement statement = databaseMetaData.getConnection().createStatement() ) {
final ResultSet rs = statement.executeQuery( "select version()" ); final ResultSet rs = statement.executeQuery( "select version()" );
@ -234,7 +238,8 @@ public enum Database {
} }
} }
return ""; // default to the dialect-specific configuration setting
return ConfigurationHelper.getString( COCKROACH_VERSION_STRING, info.getConfigurationValues(), "" );
} }
}, },

View File

@ -60,7 +60,7 @@ public class MariaDBDialect extends MySQLDialect {
} }
public MariaDBDialect(DialectResolutionInfo info) { public MariaDBDialect(DialectResolutionInfo info) {
super( createVersion( info ), MySQLServerConfiguration.fromDatabaseMetadata( info.getDatabaseMetadata() ) ); super( createVersion( info ), MySQLServerConfiguration.fromDialectResolutionInfo( info ) );
registerKeywords( info ); registerKeywords( info );
} }

View File

@ -183,7 +183,7 @@ public class MySQLDialect extends Dialect {
} }
public MySQLDialect(DialectResolutionInfo info) { public MySQLDialect(DialectResolutionInfo info) {
this( createVersion( info ), MySQLServerConfiguration.fromDatabaseMetadata( info.getDatabaseMetadata() ) ); this( createVersion( info ), MySQLServerConfiguration.fromDialectResolutionInfo( info ) );
registerKeywords( info ); registerKeywords( info );
} }

View File

@ -9,10 +9,20 @@ package org.hibernate.dialect;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.internal.util.config.ConfigurationHelper;
import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_BYTES_PER_CHARACTER;
import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_NO_BACKSLASH_ESCAPES;
/**
* Utility class that extract some initial configuration from the database
* for {@link MySQLDialect} and related dialects.
*
* @author Marco Belladelli
*/
public class MySQLServerConfiguration { public class MySQLServerConfiguration {
private final int bytesPerCharacter; private final int bytesPerCharacter;
private final boolean noBackslashEscapesEnabled; private final boolean noBackslashEscapesEnabled;
@ -29,6 +39,69 @@ public class MySQLServerConfiguration {
return noBackslashEscapesEnabled; return noBackslashEscapesEnabled;
} }
public static MySQLServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) {
Integer bytesPerCharacter = null;
Boolean noBackslashEscapes = null;
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
if ( databaseMetaData != null ) {
try (java.sql.Statement s = databaseMetaData.getConnection().createStatement()) {
final ResultSet rs = s.executeQuery( "SELECT @@character_set_database, @@sql_mode" );
if ( rs.next() ) {
final String characterSet = rs.getString( 1 );
final int collationIndex = characterSet.indexOf( '_' );
// According to https://dev.mysql.com/doc/refman/8.0/en/charset-charsets.html
switch ( collationIndex == -1 ? characterSet : characterSet.substring( 0, collationIndex ) ) {
case "utf16":
case "utf16le":
case "utf32":
case "utf8mb4":
case "gb18030":
bytesPerCharacter = 4;
case "utf8":
case "utf8mb3":
case "eucjpms":
case "ujis":
bytesPerCharacter = 3;
break;
case "ucs2":
case "cp932":
case "big5":
case "euckr":
case "gb2312":
case "gbk":
case "sjis":
bytesPerCharacter = 2;
break;
default:
bytesPerCharacter = 1;
}
// NO_BACKSLASH_ESCAPES
final String sqlMode = rs.getString( 2 );
noBackslashEscapes = sqlMode.toLowerCase().contains( "no_backslash_escapes" );
}
}
catch (SQLException ex) {
// Ignore
}
}
// default to the dialect-specific configuration settings
if ( bytesPerCharacter == null ) {
bytesPerCharacter = ConfigurationHelper.getInt( MYSQL_BYTES_PER_CHARACTER, info.getConfigurationValues(), 4 );
}
if ( noBackslashEscapes == null ) {
noBackslashEscapes = ConfigurationHelper.getBoolean(
MYSQL_NO_BACKSLASH_ESCAPES,
info.getConfigurationValues(),
false
);
}
return new MySQLServerConfiguration( bytesPerCharacter, noBackslashEscapes );
}
/**
* @deprecated Use {@link #fromDialectResolutionInfo} instead.
*/
@Deprecated( since = "6.4" )
public static MySQLServerConfiguration fromDatabaseMetadata(DatabaseMetaData databaseMetaData) { public static MySQLServerConfiguration fromDatabaseMetadata(DatabaseMetaData databaseMetaData) {
int bytesPerCharacter = 4; int bytesPerCharacter = 4;
boolean noBackslashEscapes = false; boolean noBackslashEscapes = false;
@ -75,6 +148,6 @@ public class MySQLServerConfiguration {
// Ignore // Ignore
} }
} }
return new MySQLServerConfiguration(bytesPerCharacter, noBackslashEscapes); return new MySQLServerConfiguration( bytesPerCharacter, noBackslashEscapes );
} }
} }

View File

@ -48,6 +48,7 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.mutation.EntityMutationTarget; import org.hibernate.persister.entity.mutation.EntityMutationTarget;
@ -100,6 +101,8 @@ import static org.hibernate.LockOptions.NO_WAIT;
import static org.hibernate.LockOptions.SKIP_LOCKED; import static org.hibernate.LockOptions.SKIP_LOCKED;
import static org.hibernate.LockOptions.WAIT_FOREVER; import static org.hibernate.LockOptions.WAIT_FOREVER;
import static org.hibernate.cfg.AvailableSettings.BATCH_VERSIONED_DATA; import static org.hibernate.cfg.AvailableSettings.BATCH_VERSIONED_DATA;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
import static org.hibernate.dialect.OracleJdbcHelper.getArrayJdbcTypeConstructor; import static org.hibernate.dialect.OracleJdbcHelper.getArrayJdbcTypeConstructor;
import static org.hibernate.dialect.OracleJdbcHelper.getNestedTableJdbcTypeConstructor; import static org.hibernate.dialect.OracleJdbcHelper.getNestedTableJdbcTypeConstructor;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
@ -187,12 +190,18 @@ public class OracleDialect extends Dialect {
} }
public OracleDialect(DialectResolutionInfo info) { public OracleDialect(DialectResolutionInfo info) {
super(info); this( info, OracleServerConfiguration.fromDialectResolutionInfo( info ) );
autonomous = isAutonomous( info.getDatabaseMetadata() );
extended = isExtended( info.getDatabaseMetadata() );
} }
protected static boolean isExtended(DatabaseMetaData databaseMetaData) { public OracleDialect(DialectResolutionInfo info, OracleServerConfiguration serverConfiguration) {
super( info );
autonomous = serverConfiguration.isAutonomous();
extended = serverConfiguration.isExtended();
}
@Deprecated( since = "6.4" )
protected static boolean isExtended(DialectResolutionInfo info) {
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
if ( databaseMetaData != null ) { if ( databaseMetaData != null ) {
try ( java.sql.Statement statement = databaseMetaData.getConnection().createStatement() ) { try ( java.sql.Statement statement = databaseMetaData.getConnection().createStatement() ) {
statement.execute( "select cast('string' as varchar2(32000)) from dual" ); statement.execute( "select cast('string' as varchar2(32000)) from dual" );
@ -204,10 +213,13 @@ public class OracleDialect extends Dialect {
// Ignore // Ignore
} }
} }
return false; // default to the dialect-specific configuration setting
return ConfigurationHelper.getBoolean( ORACLE_EXTENDED_STRING_SIZE, info.getConfigurationValues(), false );
} }
protected static boolean isAutonomous(DatabaseMetaData databaseMetaData) { @Deprecated( since = "6.4" )
protected static boolean isAutonomous(DialectResolutionInfo info) {
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
if ( databaseMetaData != null ) { if ( databaseMetaData != null ) {
try ( java.sql.Statement statement = databaseMetaData.getConnection().createStatement() ) { try ( java.sql.Statement statement = databaseMetaData.getConnection().createStatement() ) {
return statement.executeQuery( "select 1 from dual where sys_context('USERENV','CLOUD_SERVICE') in ('OLTP','DWCS','JSON')" ) return statement.executeQuery( "select 1 from dual where sys_context('USERENV','CLOUD_SERVICE') in ('OLTP','DWCS','JSON')" )
@ -217,13 +229,18 @@ public class OracleDialect extends Dialect {
// Ignore // Ignore
} }
} }
return false; // default to the dialect-specific configuration setting
return ConfigurationHelper.getBoolean( ORACLE_AUTONOMOUS_DATABASE, info.getConfigurationValues(), false );
} }
public boolean isAutonomous() { public boolean isAutonomous() {
return autonomous; return autonomous;
} }
public boolean isExtended() {
return extended;
}
@Override @Override
protected DatabaseVersion getMinimumSupportedVersion() { protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION; return MINIMUM_VERSION;

View File

@ -0,0 +1,96 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.internal.util.config.ConfigurationHelper;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
/**
* Utility class that extract some initial configuration from the database for {@link OracleDialect}.
*
* @author Marco Belladelli
*/
public class OracleServerConfiguration {
private final boolean autonomous;
private final boolean extended;
public boolean isAutonomous() {
return autonomous;
}
public boolean isExtended() {
return extended;
}
public OracleServerConfiguration(boolean autonomous, boolean extended) {
this.autonomous = autonomous;
this.extended = extended;
}
public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) {
Boolean extended = null;
Boolean autonomous = null;
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
if ( databaseMetaData != null ) {
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
final ResultSet rs = statement.executeQuery(
"select cast('string' as varchar2(32000)), " +
"sys_context('USERENV','CLOUD_SERVICE') from dual"
);
if ( rs.next() ) {
// succeeded, so MAX_STRING_SIZE == EXTENDED
extended = true;
autonomous = isAutonomous( rs.getString( 2 ) );
}
}
catch (SQLException ex) {
// failed, so MAX_STRING_SIZE == STANDARD, still need to check autonomous
extended = false;
autonomous = isAutonomous( databaseMetaData );
}
}
// default to the dialect-specific configuration settings
if ( extended == null ) {
extended = ConfigurationHelper.getBoolean(
ORACLE_EXTENDED_STRING_SIZE,
info.getConfigurationValues(),
false
);
}
if ( autonomous == null ) {
autonomous = ConfigurationHelper.getBoolean(
ORACLE_AUTONOMOUS_DATABASE,
info.getConfigurationValues(),
false
);
}
return new OracleServerConfiguration( autonomous, extended );
}
private static boolean isAutonomous(String cloudServiceParam) {
return List.of( "OLTP", "DWCS", "JSON" ).contains( cloudServiceParam );
}
private static boolean isAutonomous(DatabaseMetaData databaseMetaData) {
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
return statement.executeQuery( "select 1 from dual where sys_context('USERENV','CLOUD_SERVICE') in ('OLTP','DWCS','JSON')" ).next();
}
catch (SQLException ex) {
// Ignore
}
return false;
}
}

View File

@ -10,11 +10,11 @@ import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.Map;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributions;
import org.hibernate.cfg.DialectSpecificSettings;
import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.TopLimitHandler; import org.hibernate.dialect.pagination.TopLimitHandler;
import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.Size;
@ -26,10 +26,10 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
@ -45,6 +45,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.cfg.DialectSpecificSettings.SYBASE_ANSI_NULL;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.BOOLEAN; import static org.hibernate.type.SqlTypes.BOOLEAN;
import static org.hibernate.type.SqlTypes.DATE; import static org.hibernate.type.SqlTypes.DATE;
@ -92,7 +93,7 @@ public class SybaseASEDialect extends SybaseDialect {
public SybaseASEDialect(DialectResolutionInfo info) { public SybaseASEDialect(DialectResolutionInfo info) {
super(info); super(info);
ansiNull = isAnsiNull( info.getDatabaseMetadata() ); ansiNull = isAnsiNull( info );
} }
@Override @Override
@ -163,7 +164,8 @@ public class SybaseASEDialect extends SybaseDialect {
return 16_384; return 16_384;
} }
private static boolean isAnsiNull(DatabaseMetaData databaseMetaData) { private static boolean isAnsiNull(DialectResolutionInfo info) {
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
if ( databaseMetaData != null ) { if ( databaseMetaData != null ) {
try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) { try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) {
final ResultSet rs = s.executeQuery( "SELECT @@options" ); final ResultSet rs = s.executeQuery( "SELECT @@options" );
@ -177,7 +179,8 @@ public class SybaseASEDialect extends SybaseDialect {
// Ignore // Ignore
} }
} }
return false; // default to the dialect-specific configuration setting
return ConfigurationHelper.getBoolean( SYBASE_ANSI_NULL, info.getConfigurationValues(), false );
} }
@Override @Override

View File

@ -39,7 +39,7 @@ public class TiDBDialect extends MySQLDialect {
} }
public TiDBDialect(DialectResolutionInfo info) { public TiDBDialect(DialectResolutionInfo info) {
super( createVersion( info ), MySQLServerConfiguration.fromDatabaseMetadata( info.getDatabaseMetadata() ) ); super( createVersion( info ), MySQLServerConfiguration.fromDialectResolutionInfo( info ) );
registerKeywords( info ); registerKeywords( info );
} }

View File

@ -7,8 +7,11 @@
package org.hibernate.engine.jdbc.dialect.spi; package org.hibernate.engine.jdbc.dialect.spi;
import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.engine.config.spi.ConfigurationService;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.util.Collections;
import java.util.Map;
/** /**
* Exposes information about the database and JDBC driver that can be used in resolving the appropriate Dialect * Exposes information about the database and JDBC driver that can be used in resolving the appropriate Dialect
@ -87,4 +90,13 @@ public interface DialectResolutionInfo extends DatabaseVersion {
default DatabaseMetaData getDatabaseMetadata() { default DatabaseMetaData getDatabaseMetadata() {
return null; return null;
} }
/**
* Obtain access to the complete {@link ConfigurationService#getSettings() map of config settings}.
*
* @return The immutable map of config settings.
*/
default Map<String, Object> getConfigurationValues() {
return Collections.emptyMap();
}
} }

View File

@ -161,7 +161,8 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
null, null,
0, 0,
0, 0,
null null,
configurationValues
); );
return new JdbcEnvironmentImpl( return new JdbcEnvironmentImpl(
registry, registry,
@ -319,7 +320,8 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
dbmd.getDriverName(), dbmd.getDriverName(),
dbmd.getDriverMajorVersion(), dbmd.getDriverMajorVersion(),
dbmd.getDriverMinorVersion(), dbmd.getDriverMinorVersion(),
dbmd.getSQLKeywords() dbmd.getSQLKeywords(),
configurationValues
); );
return new JdbcEnvironmentImpl( return new JdbcEnvironmentImpl(
registry, registry,
@ -474,6 +476,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
private final int driverMajorVersion; private final int driverMajorVersion;
private final int driverMinorVersion; private final int driverMinorVersion;
private final String sqlKeywords; private final String sqlKeywords;
private final Map<String, Object> configurationValues;
public DialectResolutionInfoImpl( public DialectResolutionInfoImpl(
DatabaseMetaData databaseMetadata, DatabaseMetaData databaseMetadata,
@ -484,7 +487,8 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
String driverName, String driverName,
int driverMajorVersion, int driverMajorVersion,
int driverMinorVersion, int driverMinorVersion,
String sqlKeywords) { String sqlKeywords,
Map<String, Object> configurationValues) {
this.databaseMetadata = databaseMetadata; this.databaseMetadata = databaseMetadata;
this.databaseName = databaseName; this.databaseName = databaseName;
this.databaseVersion = databaseVersion; this.databaseVersion = databaseVersion;
@ -494,6 +498,7 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
this.driverMajorVersion = driverMajorVersion; this.driverMajorVersion = driverMajorVersion;
this.driverMinorVersion = driverMinorVersion; this.driverMinorVersion = driverMinorVersion;
this.sqlKeywords = sqlKeywords; this.sqlKeywords = sqlKeywords;
this.configurationValues = configurationValues;
} }
public String getSQLKeywords() { public String getSQLKeywords() {
@ -544,6 +549,11 @@ public class JdbcEnvironmentInitiator implements StandardServiceInitiator<JdbcEn
public String toString() { public String toString() {
return getMajor() + "." + getMinor(); return getMajor() + "." + getMinor();
} }
@Override
public Map<String, Object> getConfigurationValues() {
return configurationValues;
}
} }
/** /**

View File

@ -347,6 +347,11 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
public String getSQLKeywords() { public String getSQLKeywords() {
return ""; return "";
} }
@Override
public Map<String, Object> getConfigurationValues() {
return configurationValues;
}
} }
); );

View File

@ -0,0 +1,121 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.dialect.resolver;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.SybaseASEDialect;
import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
import org.hibernate.testing.orm.junit.Jira;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.cfg.DialectSpecificSettings.COCKROACH_VERSION_STRING;
import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_BYTES_PER_CHARACTER;
import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_NO_BACKSLASH_ESCAPES;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
import static org.hibernate.cfg.DialectSpecificSettings.SYBASE_ANSI_NULL;
import static org.hibernate.dialect.DatabaseVersion.NO_VERSION;
/**
* @author Marco Belladelli
*/
@Jira( "https://hibernate.atlassian.net/browse/HHH-17425" )
public class DialectSpecificConfigTest {
@Test
public void testOracleMaxStringSize() {
final Dialect dialect = resolveDialect(
"Oracle",
values -> values.put( ORACLE_EXTENDED_STRING_SIZE, "true" )
);
assertThat( dialect ).isInstanceOf( OracleDialect.class );
assertThat( dialect.getMaxVarcharLength() ).isEqualTo( 32_767 );
assertThat( dialect.getMaxVarbinaryLength() ).isEqualTo( 32_767 );
}
@Test
public void testOracleIsAutonomous() {
final Dialect dialect = resolveDialect(
"Oracle",
values -> values.put( ORACLE_AUTONOMOUS_DATABASE, "true" )
);
assertThat( dialect ).isInstanceOf( OracleDialect.class );
assertThat( ( (OracleDialect) dialect ).isAutonomous() ).isTrue();
}
@Test
public void testSybaseASEIsAnsiNull() {
final Dialect dialect = resolveDialect(
"ASE",
values -> values.put( SYBASE_ANSI_NULL, "true" )
);
assertThat( dialect ).isInstanceOf( SybaseASEDialect.class );
assertThat( ( (SybaseASEDialect) dialect ).isAnsiNullOn() ).isTrue();
}
@Test
public void testMySQLBytesPerCharacter() {
final Dialect dialect = resolveDialect(
"MySQL",
values -> values.put( MYSQL_BYTES_PER_CHARACTER, "1" )
);
assertThat( dialect ).isInstanceOf( MySQLDialect.class );
assertThat( dialect.getMaxVarcharLength() ).isEqualTo( 65_535 );
}
@Test
public void testMySQLNoBackslashEscape() {
final Dialect dialect = resolveDialect(
"MySQL",
values -> values.put( MYSQL_NO_BACKSLASH_ESCAPES, "true" )
);
assertThat( dialect ).isInstanceOf( MySQLDialect.class );
assertThat( ( (MySQLDialect) dialect ).isNoBackslashEscapesEnabled() ).isTrue();
}
@Test
public void testCockroachDBVersion() {
final Dialect dialect = resolveDialect( "PostgreSQL", values -> values.put(
COCKROACH_VERSION_STRING,
"CockroachDB CCL v23.1.8 (x86_64-pc-linux-gnu, built 2023/08/04 18:11:44, go1.19.10)"
) );
assertThat( dialect ).isInstanceOf( CockroachDialect.class );
assertThat( dialect.getVersion().getMajor() ).isEqualTo( 23 );
assertThat( dialect.getVersion().getMinor() ).isEqualTo( 1 );
assertThat( dialect.getVersion().getMicro() ).isEqualTo( 8 );
}
private static Dialect resolveDialect(String productName, Consumer<Map<String, Object>> configurationProvider) {
final Map<String, Object> configurationValues = new HashMap<>();
configurationProvider.accept( configurationValues );
final TestingDialectResolutionInfo info = TestingDialectResolutionInfo.forDatabaseInfo(
productName,
null,
NO_VERSION,
NO_VERSION,
configurationValues
);
assertThat( info.getDatabaseMetadata() ).isNull();
return new StandardDialectResolver().resolveDialect( info );
}
}

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.orm.test.dialect.resolver; package org.hibernate.orm.test.dialect.resolver;
import java.util.Collections;
import java.util.Map;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
/** /**
@ -20,19 +23,23 @@ public class TestingDialectResolutionInfo implements DialectResolutionInfo {
private final int driverMajorVersion; private final int driverMajorVersion;
private final int driverMinorVersion; private final int driverMinorVersion;
private final Map<String, Object> configurationValues;
TestingDialectResolutionInfo( TestingDialectResolutionInfo(
String databaseName, String databaseName,
int databaseMajorVersion, int databaseMajorVersion,
int databaseMinorVersion, int databaseMinorVersion,
String driverName, String driverName,
int driverMajorVersion, int driverMajorVersion,
int driverMinorVersion) { int driverMinorVersion,
Map<String, Object> configurationValues) {
this.databaseName = databaseName; this.databaseName = databaseName;
this.databaseMajorVersion = databaseMajorVersion; this.databaseMajorVersion = databaseMajorVersion;
this.databaseMinorVersion = databaseMinorVersion; this.databaseMinorVersion = databaseMinorVersion;
this.driverName = driverName; this.driverName = driverName;
this.driverMajorVersion = driverMajorVersion; this.driverMajorVersion = driverMajorVersion;
this.driverMinorVersion = driverMinorVersion; this.driverMinorVersion = driverMinorVersion;
this.configurationValues = configurationValues;
} }
public static TestingDialectResolutionInfo forDatabaseInfo(String name) { public static TestingDialectResolutionInfo forDatabaseInfo(String name) {
@ -44,11 +51,28 @@ public class TestingDialectResolutionInfo implements DialectResolutionInfo {
} }
public static TestingDialectResolutionInfo forDatabaseInfo(String name, int majorVersion, int minorVersion) { public static TestingDialectResolutionInfo forDatabaseInfo(String name, int majorVersion, int minorVersion) {
return new TestingDialectResolutionInfo( name, majorVersion, minorVersion, null, NO_VERSION, NO_VERSION ); return forDatabaseInfo( name, null, majorVersion, minorVersion );
} }
public static TestingDialectResolutionInfo forDatabaseInfo(String databaseName, String driverName, int majorVersion, int minorVersion) { public static TestingDialectResolutionInfo forDatabaseInfo(String databaseName, String driverName, int majorVersion, int minorVersion) {
return new TestingDialectResolutionInfo( databaseName, majorVersion, minorVersion, driverName, NO_VERSION, NO_VERSION ); return forDatabaseInfo( databaseName, driverName, majorVersion, minorVersion, Collections.emptyMap() );
}
public static TestingDialectResolutionInfo forDatabaseInfo(
String databaseName,
String driverName,
int majorVersion,
int minorVersion,
Map<String, Object> configurationValues) {
return new TestingDialectResolutionInfo(
databaseName,
majorVersion,
minorVersion,
driverName,
NO_VERSION,
NO_VERSION,
configurationValues
);
} }
@Override @Override
@ -106,4 +130,9 @@ public class TestingDialectResolutionInfo implements DialectResolutionInfo {
public String toString() { public String toString() {
return getMajor() + "." + getMinor(); return getMajor() + "." + getMinor();
} }
@Override
public Map<String, Object> getConfigurationValues() {
return configurationValues;
}
} }