From 17fa97d1b0c423da397f9a8f014ed96edfc74402 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Fri, 17 Jun 2022 19:57:27 +0200 Subject: [PATCH] HHH-15182 - Remove support for MariaDB versions older than 10.3 Signed-off-by: Jan Schatteman --- .../dialect/MariaDBLegacyDialect.java | 183 ++++++++++++++++++ .../MariaDBLegacySqlAstTranslator.java | 149 ++++++++++++++ .../org/hibernate/dialect/MariaDBDialect.java | 53 +++-- .../dialect/MariaDBSqlAstTranslator.java | 4 +- .../MariaDBExtractSequenceMetadataTest.java | 2 +- .../SequenceInformationMariaDBTest.java | 2 +- 6 files changed, 361 insertions(+), 32 deletions(-) create mode 100644 hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java create mode 100644 hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java new file mode 100644 index 0000000000..bab40f01b6 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java @@ -0,0 +1,183 @@ +/* + * 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.community.dialect; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.InnoDBStorageEngine; +import org.hibernate.dialect.MySQLStorageEngine; +import org.hibernate.dialect.NationalizationSupport; +import org.hibernate.dialect.function.CommonFunctionFactory; +import org.hibernate.dialect.sequence.MariaDBSequenceSupport; +import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.exec.spi.JdbcOperation; +import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorMariaDBDatabaseImpl; +import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; +import org.hibernate.type.StandardBasicTypes; + +/** + * A {@linkplain Dialect SQL dialect} for MariaDB + * + * @author Vlad Mihalcea + * @author Gavin King + */ +public class MariaDBLegacyDialect extends MySQLLegacyDialect { + private static final DatabaseVersion VERSION5 = DatabaseVersion.make( 5 ); + private static final DatabaseVersion VERSION57 = DatabaseVersion.make( 5, 7 ); + + public MariaDBLegacyDialect() { + this( DatabaseVersion.make( 5 ) ); + } + + public MariaDBLegacyDialect(DatabaseVersion version) { + super(version); + } + + public MariaDBLegacyDialect(DialectResolutionInfo info) { + super(info); + } + + @Override + public DatabaseVersion getMySQLVersion() { + return getVersion().isBefore( 5, 3 ) + ? VERSION5 + : VERSION57; + } + + @Override + public NationalizationSupport getNationalizationSupport() { + return NationalizationSupport.IMPLICIT; + } + + @Override + public void initializeFunctionRegistry(QueryEngine queryEngine) { + super.initializeFunctionRegistry(queryEngine); + + if ( getVersion().isSameOrAfter( 10, 2 ) ) { + CommonFunctionFactory commonFunctionFactory = new CommonFunctionFactory( queryEngine ); + commonFunctionFactory.windowFunctions(); + commonFunctionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); + queryEngine.getSqmFunctionRegistry().registerNamed( + "json_valid", + queryEngine.getTypeConfiguration() + .getBasicTypeRegistry() + .resolve( StandardBasicTypes.BOOLEAN ) + ); + if ( getVersion().isSameOrAfter( 10, 3, 3 ) ) { + commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation(); + } + } + } + + @Override + public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { + return new StandardSqlAstTranslatorFactory() { + @Override + protected SqlAstTranslator buildTranslator( + SessionFactoryImplementor sessionFactory, Statement statement) { + return new MariaDBLegacySqlAstTranslator<>( sessionFactory, statement ); + } + }; + } + + @Override + public boolean supportsWindowFunctions() { + return getVersion().isSameOrAfter( 10, 2 ); + } + + @Override + public boolean supportsColumnCheck() { + return getVersion().isSameOrAfter( 10, 2 ); + } + + @Override + protected MySQLStorageEngine getDefaultMySQLStorageEngine() { + return InnoDBStorageEngine.INSTANCE; + } + + @Override + public boolean supportsIfExistsBeforeConstraintName() { + return getVersion().isSameOrAfter( 10 ); + } + + @Override + public boolean supportsIfExistsAfterAlterTable() { + return getVersion().isSameOrAfter( 10, 5 ); + } + + @Override + public SequenceSupport getSequenceSupport() { + return getVersion().isBefore( 10, 3 ) + ? super.getSequenceSupport() + : MariaDBSequenceSupport.INSTANCE; + } + + @Override + public String getQuerySequencesString() { + return getSequenceSupport().supportsSequences() + ? "select table_name from information_schema.TABLES where table_schema=database() and table_type='SEQUENCE'" + : super.getQuerySequencesString(); //fancy way to write "null" + } + + @Override + public SequenceInformationExtractor getSequenceInformationExtractor() { + return getSequenceSupport().supportsSequences() + ? SequenceInformationExtractorMariaDBDatabaseImpl.INSTANCE + : super.getSequenceInformationExtractor(); + } + + @Override + public boolean supportsSkipLocked() { + //only supported on MySQL and as of 10.6 + return getVersion().isSameOrAfter( 10, 6 ); + } + + @Override + public boolean supportsNoWait() { + return getVersion().isSameOrAfter( 10, 3 ); + } + + @Override + public boolean supportsWait() { + return getVersion().isSameOrAfter( 10, 3 ); + } + + @Override + boolean supportsForShare() { + //only supported on MySQL + return false; + } + + @Override + boolean supportsAliasLocks() { + //only supported on MySQL + return false; + } + + @Override + public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) + throws SQLException { + + // some MariaDB drivers does not return case strategy info + builder.setUnquotedCaseStrategy( IdentifierCaseStrategy.MIXED ); + builder.setQuotedCaseStrategy( IdentifierCaseStrategy.MIXED ); + + return super.buildIdentifierHelper( builder, dbMetaData ); + } +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java new file mode 100644 index 0000000000..000de9c9f4 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java @@ -0,0 +1,149 @@ +/* + * 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.community.dialect; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.sqm.ComparisonOperator; +import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; +import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; +import org.hibernate.sql.ast.tree.select.QueryGroup; +import org.hibernate.sql.ast.tree.select.QueryPart; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.exec.spi.JdbcOperation; + +/** + * A SQL AST translator for MariaDB. + * + * @author Christian Beikov + */ +public class MariaDBLegacySqlAstTranslator extends AbstractSqlAstTranslator { + + public MariaDBLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { + super( sessionFactory, statement ); + } + + @Override + protected void renderExpressionAsClauseItem(Expression expression) { + expression.accept( this ); + } + + @Override + public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) { + final boolean isNegated = booleanExpressionPredicate.isNegated(); + if ( isNegated ) { + appendSql( "not(" ); + } + booleanExpressionPredicate.getExpression().accept( this ); + if ( isNegated ) { + appendSql( CLOSE_PARENTHESIS ); + } + } + + @Override + protected String getForShare(int timeoutMillis) { + return " lock in share mode"; + } + + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { + // Check if current query part is already row numbering to avoid infinite recursion + return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart && supportsWindowFunctions() && !isRowsOnlyFetchClauseType( queryPart ); + } + + @Override + public void visitQueryGroup(QueryGroup queryGroup) { + if ( shouldEmulateFetchClause( queryGroup ) ) { + emulateFetchOffsetWithWindowFunctions( queryGroup, true ); + } + else { + super.visitQueryGroup( queryGroup ); + } + } + + @Override + public void visitQuerySpec(QuerySpec querySpec) { + if ( shouldEmulateFetchClause( querySpec ) ) { + emulateFetchOffsetWithWindowFunctions( querySpec, true ); + } + else { + super.visitQuerySpec( querySpec ); + } + } + + @Override + public void visitQueryPartTableReference(QueryPartTableReference tableReference) { + emulateQueryPartTableReferenceColumnAliasing( tableReference ); + } + + @Override + public void visitOffsetFetchClause(QueryPart queryPart) { + if ( !isRowNumberingCurrentQueryPart() ) { + renderCombinedLimitClause( queryPart ); + } + } + + @Override + protected void renderSearchClause(CteStatement cte) { + // MariaDB does not support this, but it's just a hint anyway + } + + @Override + protected void renderCycleClause(CteStatement cte) { + // MariaDB does not support this, but it can be emulated + } + + @Override + protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { + renderComparisonDistinctOperator( lhs, operator, rhs ); + } + + @Override + protected void renderPartitionItem(Expression expression) { + if ( expression instanceof Literal ) { + appendSql( "'0'" ); + } + else if ( expression instanceof Summarization ) { + Summarization summarization = (Summarization) expression; + renderCommaSeparated( summarization.getGroupings() ); + appendSql( " with " ); + appendSql( summarization.getKind().sqlText() ); + } + else { + expression.accept( this ); + } + } + + @Override + public boolean supportsRowValueConstructorSyntaxInSet() { + return false; + } + + @Override + protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() { + return false; + } + + @Override + protected boolean supportsIntersect() { + return getDialect().getVersion().isSameOrAfter( 10, 3 ); + } + + @Override + protected boolean supportsDistinctFromPredicate() { + // It supports a proprietary operator + return true; + } + + private boolean supportsWindowFunctions() { + return getDialect().getVersion().isSameOrAfter( 10, 2 ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java index eb800240d8..cc411fbe08 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java @@ -34,11 +34,11 @@ import org.hibernate.type.StandardBasicTypes; * @author Gavin King */ public class MariaDBDialect extends MySQLDialect { - private static final DatabaseVersion VERSION5 = DatabaseVersion.make( 5 ); - private static final DatabaseVersion VERSION57 = DatabaseVersion.make( 5, 7 ); + private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 10, 3 ); + private static final DatabaseVersion MYSQL57 = DatabaseVersion.make( 5, 7 ); public MariaDBDialect() { - this( DatabaseVersion.make( 5 ) ); + this( MINIMUM_VERSION ); } public MariaDBDialect(DatabaseVersion version) { @@ -51,9 +51,12 @@ public class MariaDBDialect extends MySQLDialect { @Override public DatabaseVersion getMySQLVersion() { - return getVersion().isBefore( 5, 3 ) - ? VERSION5 - : VERSION57; + return MYSQL57; + } + + @Override + protected DatabaseVersion getMinimumSupportedVersion() { + return MINIMUM_VERSION; } @Override @@ -65,20 +68,16 @@ public class MariaDBDialect extends MySQLDialect { public void initializeFunctionRegistry(QueryEngine queryEngine) { super.initializeFunctionRegistry(queryEngine); - if ( getVersion().isSameOrAfter( 10, 2 ) ) { - CommonFunctionFactory commonFunctionFactory = new CommonFunctionFactory( queryEngine ); - commonFunctionFactory.windowFunctions(); - commonFunctionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); - queryEngine.getSqmFunctionRegistry().registerNamed( - "json_valid", - queryEngine.getTypeConfiguration() - .getBasicTypeRegistry() - .resolve( StandardBasicTypes.BOOLEAN ) - ); - if ( getVersion().isSameOrAfter( 10, 3, 3 ) ) { - commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation(); - } - } + CommonFunctionFactory commonFunctionFactory = new CommonFunctionFactory( queryEngine ); + commonFunctionFactory.windowFunctions(); + commonFunctionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); + queryEngine.getSqmFunctionRegistry().registerNamed( + "json_valid", + queryEngine.getTypeConfiguration() + .getBasicTypeRegistry() + .resolve( StandardBasicTypes.BOOLEAN ) + ); + commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation(); } @Override @@ -94,12 +93,12 @@ public class MariaDBDialect extends MySQLDialect { @Override public boolean supportsWindowFunctions() { - return getVersion().isSameOrAfter( 10, 2 ); + return true; } @Override public boolean supportsColumnCheck() { - return getVersion().isSameOrAfter( 10, 2 ); + return true; } @Override @@ -109,7 +108,7 @@ public class MariaDBDialect extends MySQLDialect { @Override public boolean supportsIfExistsBeforeConstraintName() { - return getVersion().isSameOrAfter( 10 ); + return true; } @Override @@ -119,9 +118,7 @@ public class MariaDBDialect extends MySQLDialect { @Override public SequenceSupport getSequenceSupport() { - return getVersion().isBefore( 10, 3 ) - ? super.getSequenceSupport() - : MariaDBSequenceSupport.INSTANCE; + return MariaDBSequenceSupport.INSTANCE; } @Override @@ -146,12 +143,12 @@ public class MariaDBDialect extends MySQLDialect { @Override public boolean supportsNoWait() { - return getVersion().isSameOrAfter( 10, 3 ); + return true; } @Override public boolean supportsWait() { - return getVersion().isSameOrAfter( 10, 3 ); + return true; } @Override 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 570c37f50f..ff2f4002c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -134,7 +134,7 @@ public class MariaDBSqlAstTranslator extends AbstractSq @Override protected boolean supportsIntersect() { - return getDialect().getVersion().isSameOrAfter( 10, 3 ); + return true; } @Override @@ -144,6 +144,6 @@ public class MariaDBSqlAstTranslator extends AbstractSq } private boolean supportsWindowFunctions() { - return getDialect().getVersion().isSameOrAfter( 10, 2 ); + return true; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/MariaDBExtractSequenceMetadataTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/MariaDBExtractSequenceMetadataTest.java index 2ed9947947..01e0a64c3e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/MariaDBExtractSequenceMetadataTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/MariaDBExtractSequenceMetadataTest.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; /** * @author Nathan Xu */ -@RequiresDialect(value = MariaDBDialect.class, majorVersion = 10, minorVersion = 3) +@RequiresDialect(value = MariaDBDialect.class) public class MariaDBExtractSequenceMetadataTest { private static String primaryDbName; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java index e3629d2558..5e5c3dd767 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java @@ -38,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.fail; * @author Vlad Mihalcea */ @TestForIssue(jiraKey = "HHH-12973") -@RequiresDialect(value = MariaDBDialect.class, majorVersion = 10, minorVersion = 3) +@RequiresDialect(value = MariaDBDialect.class) @Jpa( annotatedClasses = { SequenceInformationMariaDBTest.Book.class,