From cf2e723d6f916d58892288f483d03a71ee3d2ab5 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 27 Feb 2023 15:32:34 +0000 Subject: [PATCH] HHH-16228 Dialect instance might be wrapped when doing instanceof checks for capabilities To properly support the idea of wrapping the Dialect, we need to take this into account whenever an `instanceof` is used to check for Dialect capabilities. Also some code is casting to the expected Dialect. --- .../dialect/MySQLLegacySqlAstTranslator.java | 3 ++- .../dialect/DialectDelegateWrapper.java | 17 +++++++++++++++++ .../dialect/MariaDBSqlAstTranslator.java | 2 +- .../dialect/MySQLSqlAstTranslator.java | 2 +- .../enhanced/OrderedSequenceStructure.java | 4 +++- .../entities/mapper/SinglePropertyMapper.java | 4 +++- .../org/hibernate/testing/DialectChecks.java | 2 ++ .../orm/junit/DialectFeatureChecks.java | 18 ++++++++++++++++++ .../testing/transaction/TransactionUtil.java | 14 ++++++++------ 9 files changed, 55 insertions(+), 11 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java index 3d5cd991db..fe287744ad 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java @@ -6,6 +6,7 @@ */ package org.hibernate.community.dialect; +import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.MySQLSqlAstTranslator; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -245,7 +246,7 @@ public class MySQLLegacySqlAstTranslator extends Abstra @Override public MySQLDialect getDialect() { - return (MySQLDialect) super.getDialect(); + return (MySQLDialect) DialectDelegateWrapper.extractRealDialect( super.getDialect() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java index 2b384f80d4..85a16ea26e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java @@ -109,6 +109,23 @@ public class DialectDelegateWrapper extends Dialect { } /** + * Extract the wrapped dialect, recursively until a non-wrapped implementation is found; + * this is useful for all the code needing to know "of which type" the underlying + * implementation actually is. + * @param dialect the Dialect to unwrap + * @return a Dialect implementation which is not a DialectDelegateWrapper; could be the same as the argument. + */ + public static Dialect extractRealDialect(Dialect dialect) { + Objects.requireNonNull( dialect ); + if ( !( dialect instanceof DialectDelegateWrapper ) ) { + return dialect; + } + else { + return extractRealDialect( ( (DialectDelegateWrapper) dialect ).wrapped ); + } + } + + /** * Exposed so to allow code needing to know the implementation. * @return the wrapped Dialect */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java index e5f4940b37..ea9ad651c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -33,7 +33,7 @@ public class MariaDBSqlAstTranslator extends AbstractSq public MariaDBSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { super( sessionFactory, statement ); - this.dialect = (MariaDBDialect) super.getDialect(); + this.dialect = (MariaDBDialect) DialectDelegateWrapper.extractRealDialect( super.getDialect() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java index e14bc552a1..b2a99fc0aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java @@ -281,7 +281,7 @@ public class MySQLSqlAstTranslator extends AbstractSqlA @Override public MySQLDialect getDialect() { - return (MySQLDialect) super.getDialect(); + return (MySQLDialect) DialectDelegateWrapper.extractRealDialect( super.getDialect() ); } @Override diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/enhanced/OrderedSequenceStructure.java b/hibernate-envers/src/main/java/org/hibernate/envers/enhanced/OrderedSequenceStructure.java index 12aa92220f..9a9f8a0611 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/enhanced/OrderedSequenceStructure.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/enhanced/OrderedSequenceStructure.java @@ -11,6 +11,7 @@ import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.QualifiedName; import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.OracleDialect; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.id.enhanced.SequenceStructure; @@ -43,7 +44,8 @@ public class OrderedSequenceStructure extends SequenceStructure { Class numberType) { super( jdbcEnvironment, "envers", qualifiedSequenceName, initialValue, incrementSize, numberType ); this.sequenceObject = new OrderedSequence(); - if ( jdbcEnvironment.getDialect() instanceof OracleDialect ) { + final Dialect dialect = DialectDelegateWrapper.extractRealDialect( jdbcEnvironment.getDialect() ); + if ( dialect instanceof OracleDialect ) { this.suffix = ( noCache ? " NOCACHE" : "" ) + " ORDER"; } else { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java index 5cff5ee573..3567855370 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java @@ -14,6 +14,7 @@ import java.util.Objects; import org.hibernate.HibernateException; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.OracleDialect; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.boot.internal.EnversService; @@ -59,8 +60,9 @@ public class SinglePropertyMapper extends AbstractPropertyMapper implements Simp Object oldObj) { data.put( propertyData.getName(), newObj ); boolean dbLogicallyDifferent = true; - final Dialect dialect = session.getFactory().getJdbcServices() + Dialect dialect = session.getFactory().getJdbcServices() .getDialect(); + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); if ( ( dialect instanceof OracleDialect ) && (newObj instanceof String || oldObj instanceof String) ) { // Don't generate new revision when database replaces empty string with NULL during INSERT or UPDATE statements. dbLogicallyDifferent = !(StringTools.isEmpty( newObj ) && StringTools.isEmpty( oldObj )); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java index 48493fe3a2..a8f9bb6099 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java @@ -12,6 +12,7 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.NationalizationSupport; import org.hibernate.dialect.PostgreSQLDialect; @@ -110,6 +111,7 @@ abstract public class DialectChecks { public static class SupportsRowValueConstructorSyntaxCheck implements DialectCheck { public boolean isMatch(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof AbstractHANADialect || dialect instanceof CockroachDialect || dialect instanceof MySQLDialect diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index d721cc8aa0..6313dbb321 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -15,6 +15,7 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MariaDBDialect; @@ -149,6 +150,7 @@ abstract public class DialectFeatureChecks { public static class SupportsRowValueConstructorSyntaxCheck implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof AbstractHANADialect || dialect instanceof CockroachDialect || dialect instanceof MySQLDialect @@ -158,6 +160,7 @@ abstract public class DialectFeatureChecks { public static class SupportsJdbcDriverProxying implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return !( dialect instanceof DB2Dialect || dialect instanceof DerbyDialect || dialect instanceof FirebirdDialect ); @@ -245,12 +248,14 @@ abstract public class DialectFeatureChecks { public static class SupportsPadWithChar implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return !( dialect instanceof DerbyDialect ); } } public static class SupportsGroupByRollup implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof DB2Dialect || dialect instanceof OracleDialect || dialect instanceof PostgreSQLDialect @@ -263,6 +268,7 @@ abstract public class DialectFeatureChecks { public static class SupportsGroupByGroupingSets implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof DB2Dialect || dialect instanceof OracleDialect || dialect instanceof PostgreSQLDialect @@ -297,6 +303,7 @@ abstract public class DialectFeatureChecks { public static class SupportsCharCodeConversion implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); // Derby doesn't support the `ASCII` or `CHR` functions return !( dialect instanceof DerbyDialect ); } @@ -304,6 +311,7 @@ abstract public class DialectFeatureChecks { public static class SupportsReplace implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); // Derby doesn't support the `REPLACE` function return !( dialect instanceof DerbyDialect ); } @@ -353,6 +361,7 @@ abstract public class DialectFeatureChecks { public static class SupportsOrderByInCorrelatedSubquery implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect.supportsOrderByInSubquery() // For some reason, HANA doesn't support order by in correlated subqueries... && !( dialect instanceof AbstractHANADialect ); @@ -385,6 +394,7 @@ abstract public class DialectFeatureChecks { public static class SupportsStringAggregation implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof H2Dialect || dialect instanceof HSQLDialect || dialect instanceof MySQLDialect @@ -400,6 +410,7 @@ abstract public class DialectFeatureChecks { public static class SupportsInverseDistributionFunctions implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof H2Dialect && dialect.getVersion().isSameOrAfter( 2 ) || dialect instanceof PostgreSQLDialect || dialect instanceof AbstractHANADialect @@ -413,6 +424,7 @@ abstract public class DialectFeatureChecks { public static class SupportsHypotheticalSetFunctions implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof H2Dialect && dialect.getVersion().isSameOrAfter( 2 ) || dialect instanceof PostgreSQLDialect || dialect instanceof AbstractHANADialect @@ -426,6 +438,7 @@ abstract public class DialectFeatureChecks { public static class SupportsWindowFunctions implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); // Derby doesn't really support window functions, only row_number() return dialect.supportsWindowFunctions() && !( dialect instanceof DerbyDialect ); } @@ -433,6 +446,7 @@ abstract public class DialectFeatureChecks { public static class SupportsFilterClause implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); // Derby doesn't really support window functions, only row_number() return dialect instanceof PostgreSQLDialect; } @@ -440,6 +454,7 @@ abstract public class DialectFeatureChecks { public static class SupportsSubqueryInOnClause implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); // TiDB db does not support subqueries for ON condition return !( dialect instanceof TiDBDialect ); } @@ -447,6 +462,7 @@ abstract public class DialectFeatureChecks { public static class SupportsFullJoin implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); // TiDB db does not support subqueries for ON condition return !( dialect instanceof H2Dialect || dialect instanceof MySQLDialect @@ -457,6 +473,7 @@ abstract public class DialectFeatureChecks { public static class SupportsMedian implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return !( dialect instanceof MySQLDialect || dialect instanceof SybaseDialect || dialect instanceof DerbyDialect @@ -474,6 +491,7 @@ abstract public class DialectFeatureChecks { public static class SupportsTruncateTable implements DialectFeatureCheck { public boolean apply(Dialect dialect) { + dialect = DialectDelegateWrapper.extractRealDialect( dialect ); return dialect instanceof MySQLDialect || dialect instanceof H2Dialect || dialect instanceof SQLServerDialect diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java index df2f68b5f2..4adc0ff8a6 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java @@ -29,6 +29,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.PostgreSQLDialect; @@ -589,37 +590,38 @@ public class TransactionUtil { public static void setJdbcTimeout(Session session, long millis) { final Dialect dialect = session.getSessionFactory().unwrap( SessionFactoryImplementor.class ).getJdbcServices().getDialect(); session.doWork( connection -> { - if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + Dialect extractedDialect = DialectDelegateWrapper.extractRealDialect( dialect ); + if ( extractedDialect instanceof PostgreSQLDialect || extractedDialect instanceof CockroachDialect ) { try (Statement st = connection.createStatement()) { //Prepared Statements fail for SET commands st.execute(String.format( "SET statement_timeout TO %d", millis / 10)); } } - else if( dialect instanceof MySQLDialect ) { + else if( extractedDialect instanceof MySQLDialect ) { try (PreparedStatement st = connection.prepareStatement("SET SESSION innodb_lock_wait_timeout = ?")) { st.setLong( 1, TimeUnit.MILLISECONDS.toSeconds( millis ) ); st.execute(); } } - else if( dialect instanceof H2Dialect ) { + else if( extractedDialect instanceof H2Dialect ) { try (PreparedStatement st = connection.prepareStatement("SET LOCK_TIMEOUT ?")) { st.setLong( 1, millis / 10 ); st.execute(); } } - else if( dialect instanceof SQLServerDialect ) { + else if( extractedDialect instanceof SQLServerDialect ) { try (Statement st = connection.createStatement()) { //Prepared Statements fail for SET commands st.execute(String.format( "SET LOCK_TIMEOUT %d", millis / 10)); } } - else if( dialect instanceof AbstractHANADialect ) { + else if( extractedDialect instanceof AbstractHANADialect ) { try (Statement st = connection.createStatement()) { //Prepared Statements fail for SET commands st.execute(String.format( "SET TRANSACTION LOCK WAIT TIMEOUT %d", millis )); } } - else if( dialect instanceof SybaseASEDialect ) { + else if( extractedDialect instanceof SybaseASEDialect ) { try (Statement st = connection.createStatement()) { //Prepared Statements fail for SET commands st.execute(String.format( "SET LOCK WAIT %d", millis/1000 ));