diff --git a/docker_db.sh b/docker_db.sh index 4cc2d099fd0b..8bc6cb6eab43 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -371,7 +371,7 @@ EOF } mssql() { - mssql_2022 + mssql_2025 } mssql_2017() { @@ -418,6 +418,28 @@ mssql_2022() { fi } +mssql_2025() { + $CONTAINER_CLI rm -f mssql || true + #This sha256 matches a specific tag of 2025-latest (https://mcr.microsoft.com/en-us/product/mssql/server/tags): + $CONTAINER_CLI run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y ${DB_IMAGE_MSSQL_2025:-mcr.microsoft.com/mssql/server@sha256:2fa59c23272a23dfd9600abf4ee52c0de6ae7ac640f14c617bc717ec139a5295} + sleep 5 + n=0 + until [ "$n" -ge 5 ] + do + # We need a database that uses a non-lock based MVCC approach + # https://github.com/microsoft/homebrew-mssql-release/issues/2#issuecomment-682285561 + $CONTAINER_CLI exec mssql bash -c 'echo "create database hibernate_orm_test collate SQL_Latin1_General_CP1_CS_AS; alter database hibernate_orm_test set READ_COMMITTED_SNAPSHOT ON" | /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P Hibernate_orm_test -i /dev/stdin' && break + echo "Waiting for SQL Server to start..." + n=$((n+1)) + sleep 5 + done + if [ "$n" -ge 5 ]; then + echo "SQL Server failed to start and configure after 25 seconds" + else + echo "SQL Server successfully started" + fi +} + sybase() { $CONTAINER_CLI rm -f sybase || true # Yup, that sucks, but on ubuntu we need to use -T11889 as per: https://github.com/DataGrip/docker-env/issues/12 diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java index 9a3457bfbd4d..9a3b1d2819b4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java @@ -257,6 +257,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio .setExactArgumentCount( 2 ) .setArgumentTypeResolver( StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE ) .register(); + functionFactory.regexpLike_predicateFunction(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java index 6d63ea34a024..037f94c10486 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java @@ -266,6 +266,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.addMonths(); functionFactory.monthsBetween(); functionFactory.rownumInstOrderbyGroupbyNum(); + functionFactory.regexpLike(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index 25ff2c9fcea8..b1490380b477 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -548,6 +548,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio ) ); functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" ); + functionFactory.regexpLike_postgresql( false ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java index 7b165775a200..d84f4e3a8f64 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java @@ -504,6 +504,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio if ( getDB2Version().isSameOrAfter( 11 ) ) { functionFactory.sha( "hash(?1, 2)" ); functionFactory.md5( "hash(?1, 0)" ); + + functionFactory.regexpLike(); } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java index 5c85182958de..e26baf789bbb 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java @@ -71,11 +71,14 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio "substring", new DB2SubstringFunction( false, functionContributions.getTypeConfiguration() ) ); - if ( getVersion().isSameOrAfter( 7, 2 ) ) { + if ( getVersion().isSameOrAfter( 7, 1 ) ) { CommonFunctionFactory functionFactory = new CommonFunctionFactory( functionContributions ); - functionFactory.listagg( null ); - functionFactory.inverseDistributionOrderedSetAggregates(); - functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); + functionFactory.regexpLike(); + if ( getVersion().isSameOrAfter( 7, 2 ) ) { + functionFactory.listagg( null ); + functionFactory.inverseDistributionOrderedSetAggregates(); + functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); + } } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2zLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2zLegacyDialect.java index 3ce2acb8cb71..1169e88b1125 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2zLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2zLegacyDialect.java @@ -78,6 +78,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.listagg( null ); functionFactory.inverseDistributionOrderedSetAggregates(); functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); + functionFactory.regexpLike(); } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBFunctionRegistry.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBFunctionRegistry.java index 434fe93f9b04..879b7290289d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBFunctionRegistry.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBFunctionRegistry.java @@ -127,6 +127,7 @@ public void register() { arraySet_gaussdb(); arrayFill_gaussdb(); jsonObject_gaussdb(); + functionFactory.regexpLike(); } public void array_gaussdb() { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java index 3ff01b0ed31d..ab0812eac6ce 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java @@ -443,6 +443,10 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.unnest_h2( getMaximumArraySize() ); functionFactory.generateSeries_h2( getMaximumSeriesSize() ); functionFactory.jsonTable_h2( getMaximumArraySize() ); + + if ( getVersion().isSameOrAfter( 1, 4, 193 ) ) { + functionFactory.regexpLike(); + } } /** diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java index 60e821c0a973..ac37c7bf24b4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java @@ -529,6 +529,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio // functionFactory.xmlextract(); } + + functionFactory.regexpLike_like_regexp(); } /** diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java index 36bc428ee2d8..859c42ece687 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java @@ -292,6 +292,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionContributions.getTypeConfiguration(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER ) ); + functionFactory.regexpLike_hsql(); } /** diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index c6ccadc462ba..d6e58d027162 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -16,6 +16,7 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.community.dialect.function.InformixRegexpLikeFunction; import org.hibernate.community.dialect.identity.InformixIdentityColumnSupport; import org.hibernate.community.dialect.pagination.FirstLimitHandler; import org.hibernate.community.dialect.pagination.SkipFirstLimitHandler; @@ -38,6 +39,7 @@ import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; +import org.hibernate.mapping.CheckConstraint; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.function.SqmFunctionRegistry; @@ -111,6 +113,7 @@ import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode; +import static org.hibernate.internal.util.StringHelper.isBlank; import static org.hibernate.query.common.TemporalUnit.DAY; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; import static org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers.impliedOrInvariant; @@ -411,11 +414,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionContributions.getFunctionRegistry().register( "trim", new TrimFunction( this, typeConfiguration, SqlAstNodeRenderingMode.NO_UNTYPED ) ); - //TODO: emulate support for the 'i' flag argument - functionRegistry.namedDescriptorBuilder( "regexp_like", "regex_match" ) - .setParameterTypes( STRING, STRING ) - .setInvariantType( booleanBasicType ) - .register(); + functionRegistry.register( "regexp_like", new InformixRegexpLikeFunction( typeConfiguration ) ); } @Override @@ -608,11 +607,22 @@ public boolean supportsIfExistsBeforeConstraintName() { } @Override - public boolean supportsTableCheck() { - // multi-column check constraints are created using 'alter table' + public boolean supportsNamedColumnCheck() { + // It seems the constraint name is ignored on column level return false; } + @Override + public String getCheckConstraintString(CheckConstraint checkConstraint) { + final String constraintName = checkConstraint.getName(); + final String constraint = " check (" + checkConstraint.getConstraint() + ")"; + final String constraintWithName = + isBlank( constraintName ) + ? constraint + : constraint + " constraint " + constraintName; + return appendCheckConstraintOptions( checkConstraint, constraintWithName ); + } + @Override public String getCascadeConstraintsString() { return getVersion().isSameOrAfter( 12, 10 ) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java index 130f86be7bef..d57832a9c371 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java @@ -695,6 +695,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionRegistry.registerAlternateKey( "char", "chr" ); functionFactory.listagg_groupConcat(); + functionFactory.regexpLike_regexp(); if ( getMySQLVersion().isSameOrAfter( 5, 7 ) ) { functionFactory.jsonValue_mysql(); @@ -715,6 +716,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio if ( getMySQLVersion().isSameOrAfter( 8 ) ) { functionFactory.unnest_emulated(); functionFactory.jsonTable_mysql(); + functionFactory.regexpLike(); } if ( supportsRecursiveCTE() ) { functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, false ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index 91c59b8af375..d54a97077b66 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -375,6 +375,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.unnest_oracle(); functionFactory.generateSeries_recursive( getMaximumSeriesSize(), true, false ); + functionFactory.regexpLike_predicateFunction(); } /** diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index 91debe50d7dd..7b4e27b9bdff 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -768,6 +768,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.unnest_postgresql( getVersion().isSameOrAfter( 17 ) ); functionFactory.generateSeries( null, "ordinality", false ); + + functionFactory.regexpLike_postgresql( getVersion().isSameOrAfter( 15 ) ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java index 8f9a692afded..da1c6fcace9a 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java @@ -521,6 +521,9 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, false ); } } + if ( getVersion().isSameOrAfter( 17 ) ) { + functionFactory.regexpLike_predicateFunction(); + } } /** diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java index 75be6ae017ac..24299cb39f60 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java @@ -354,6 +354,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio } functionFactory.windowFunctions(); functionFactory.listagg_groupConcat(); + functionFactory.regexpLike_regexp(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java index a1ed90516f95..0c1cac9a73c9 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java @@ -651,6 +651,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionRegistry.register( "json_mergepatch", new SingleStoreJsonMergepatchFunction( typeConfiguration ) ); functionRegistry.register( "json_array_append", new SingleStoreJsonArrayAppendFunction( typeConfiguration ) ); functionRegistry.register( "json_array_insert", new SingleStoreJsonArrayInsertFunction( typeConfiguration ) ); + commonFunctionFactory.regexpLike_regexp(); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/function/InformixRegexpLikeFunction.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/function/InformixRegexpLikeFunction.java new file mode 100644 index 000000000000..21aa87b047ba --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/function/InformixRegexpLikeFunction.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.function; + +import org.hibernate.dialect.function.AbstractRegexpLikeFunction; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + + +/** + * Informix has special integer constants as third argument. + */ +public class InformixRegexpLikeFunction extends AbstractRegexpLikeFunction { + + public InformixRegexpLikeFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final boolean caseSensitive; + if ( arguments.size() > 2 ) { + if ( !(arguments.get( 2 ) instanceof Literal literal) + || !(literal.getLiteralValue() instanceof String flags) + || !flags.equals( "i" ) ) { + throw new IllegalArgumentException( "Informix only supports the case insensitive flag 'i' as literal but got." ); + } + caseSensitive = false; + } + else { + caseSensitive = true; + } + + sqlAppender.appendSql( "regex_match(" ); + arguments.get( 0 ).accept( walker ); + sqlAppender.appendSql( ',' ); + arguments.get( 1 ).accept( walker ); + if ( !caseSensitive ) { + // 1 is extended POSIX regex which is the default, 3 is extended POSIX regex and case-insensitive + // See https://www.ibm.com/docs/en/informix-servers/14.10.0?topic=routines-regex-match-function + sqlAppender.appendSql( ",3" ); + } + sqlAppender.appendSql( ')' ); + } + + @Override + public boolean isPredicate() { + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 5c11e834894d..db81841e09e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -512,6 +512,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.hex( "encode(?1, 'hex')" ); functionFactory.sha( "digest(?1, 'sha256')" ); functionFactory.md5( "digest(?1, 'md5')" ); + functionFactory.regexpLike_postgresql( false ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index b879a94a7f3c..a50bac1e03cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -435,6 +435,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.hex( "hex(?1)" ); functionFactory.sha( "hash(?1, 2)" ); functionFactory.md5( "hash(?1, 0)" ); + + functionFactory.regexpLike(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 585e8d7f7375..b0cc0e2e8826 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -1373,8 +1373,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionRegistry.registerAlternateKey( "current_instant", "instant" ); //deprecated legacy! functionRegistry.register( "sql", new SqlFunction() ); - - functionFactory.regexpLike(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 05da7444be3f..6cccf6bff990 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -382,6 +382,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.hex( "rawtohex(?1)" ); functionFactory.sha( "hash('SHA-256', ?1)" ); functionFactory.md5( "hash('MD5', ?1)" ); + + functionFactory.regexpLike(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java index 3a821bceaacf..5addd6a8140c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java @@ -533,6 +533,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.hex( "bintohex(?1)" ); functionFactory.sha( "hash_sha256(to_binary(?1))" ); functionFactory.md5( "hash_md5(to_binary(?1))" ); + functionFactory.regexpLike_like_regexp(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index b30dcdc651c9..d5d9c3068ca8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -239,6 +239,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio ) ); functionFactory.hex( "hex(?1)" ); + functionFactory.regexpLike_hsql(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index c6c354fe0f95..3c64dec330f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -681,10 +681,12 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.jsonMergepatch_mysql(); functionFactory.jsonArrayAppend_mysql(); functionFactory.jsonArrayInsert_mysql(); + functionFactory.regexpLike_regexp(); if ( getMySQLVersion().isSameOrAfter( 8 ) ) { functionFactory.unnest_emulated(); functionFactory.jsonTable_mysql(); + functionFactory.regexpLike(); } if ( supportsRecursiveCTE() ) { functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, false ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index e9e75adcc0ef..cbafc046020f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -434,6 +434,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio "extract", new OracleExtractFunction( this, typeConfiguration ) ); + functionFactory.regexpLike_predicateFunction(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 54fd00b75940..ad7b7072c418 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -706,6 +706,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.hex( "encode(?1, 'hex')" ); functionFactory.sha( "sha256(?1)" ); functionFactory.md5( "decode(md5(?1), 'hex')" ); + + functionFactory.regexpLike_postgresql( getVersion().isSameOrAfter( 15 ) ); } @Override 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 f89f53f5d2bb..46d3de5b485b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -496,6 +496,9 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.hex( "convert(varchar(MAX), ?1, 2)" ); functionFactory.sha( "hashbytes('SHA2_256', ?1)" ); functionFactory.md5( "hashbytes('MD5', ?1)" ); + if ( getVersion().isSameOrAfter( 17 ) ) { + functionFactory.regexpLike_predicateFunction(); + } } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/AbstractRegexpLikeFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/AbstractRegexpLikeFunction.java new file mode 100644 index 000000000000..3bc6eebe2fa6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/AbstractRegexpLikeFunction.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; +import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; +import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; +import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; +import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.spi.TypeConfiguration; + +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; + +/** + * Base implementation for the regexp_like predicate. + */ +public abstract class AbstractRegexpLikeFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public AbstractRegexpLikeFunction(TypeConfiguration typeConfiguration) { + super( + "regexp_like", + new ArgumentTypesValidator( StandardArgumentsValidators.between( 2, 3 ), STRING, STRING, STRING ), + StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.getBasicTypeRegistry().resolve( + StandardBasicTypes.BOOLEAN ) ), + StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, STRING, STRING, STRING ) + ); + } + + @Override + public String getSignature(String name) { + return "(STRING string, STRING pattern[, STRING flags])"; + } + + @Override + public boolean isPredicate() { + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java index 73ea22312d64..d2f944187055 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java @@ -2674,13 +2674,38 @@ public void regexpLike() { } /** - * For MariaDB + * For legacy PostgreSQL and CockroachDB + */ + public void regexpLike_postgresql(boolean supportsStandard) { + functionRegistry.register( "regexp_like", new RegexpLikeOperatorFunction( typeConfiguration, supportsStandard ) ); + } + + /** + * For MariaDB, legacy MySQL, SingleStore and SQLite */ public void regexpLike_regexp() { - functionRegistry.patternDescriptorBuilder( "regexp_like", "?1 regexp ?2" ) - .setParameterTypes( STRING, STRING ) - .setInvariantType( booleanType ) - .register(); + functionRegistry.register( "regexp_like", new RegexpPredicateFunction( typeConfiguration ) ); + } + + /** + * For HSQLDB + */ + public void regexpLike_hsql() { + functionRegistry.register( "regexp_like", new HSQLRegexpLikeFunction( typeConfiguration ) ); + } + + /** + * For Oracle and SQL Server + */ + public void regexpLike_predicateFunction() { + functionRegistry.register( "regexp_like", new RegexpLikePredicateFunction( typeConfiguration ) ); + } + + /** + * For SAP HANA + */ + public void regexpLike_like_regexp() { + functionRegistry.register( "regexp_like", new HANARegexpLikeFunction( typeConfiguration ) ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/HANARegexpLikeFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/HANARegexpLikeFunction.java new file mode 100644 index 000000000000..2899e9937f12 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/HANARegexpLikeFunction.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + + +/** + * HANA has a special predicate. + */ +public class HANARegexpLikeFunction extends AbstractRegexpLikeFunction { + + public HANARegexpLikeFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + arguments.get( 0 ).accept( walker ); + sqlAppender.appendSql( " like_regexpr " ); + arguments.get( 1 ).accept( walker ); + if ( arguments.size() > 2 ) { + sqlAppender.appendSql( " flag " ); + arguments.get( 2 ).accept( walker ); + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/HSQLRegexpLikeFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/HSQLRegexpLikeFunction.java new file mode 100644 index 000000000000..32d0ec3f6e34 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/HSQLRegexpLikeFunction.java @@ -0,0 +1,68 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + + +/** + * HSQLDB doesn't support the third argument. + */ +public class HSQLRegexpLikeFunction extends AbstractRegexpLikeFunction { + + public HSQLRegexpLikeFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final boolean caseSensitive; + if ( arguments.size() > 2 ) { + if ( !(arguments.get( 2 ) instanceof Literal literal) + || !(literal.getLiteralValue() instanceof String flags) + || !flags.equals( "i" ) ) { + throw new IllegalArgumentException( "HSQLDB only supports the case insensitive flag 'i' as literal." ); + } + caseSensitive = false; + } + else { + caseSensitive = true; + } + + sqlAppender.appendSql( "regexp_like(" ); + if ( !caseSensitive ) { + sqlAppender.appendSql( "lower(" ); + } + arguments.get( 0 ).accept( walker ); + if ( !caseSensitive ) { + sqlAppender.appendSql( ')' ); + } + sqlAppender.appendSql( ',' ); + if ( !caseSensitive ) { + sqlAppender.appendSql( "lower(" ); + } + arguments.get( 1 ).accept( walker ); + if ( !caseSensitive ) { + sqlAppender.appendSql( ')' ); + } + sqlAppender.appendSql( ')' ); + } + + @Override + public boolean isPredicate() { + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpLikeOperatorFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpLikeOperatorFunction.java new file mode 100644 index 000000000000..c313014977cf --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpLikeOperatorFunction.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + +/** + * PostgreSQL and CockroachDB have a special predicate. + */ +public class RegexpLikeOperatorFunction extends RegexpLikePredicateFunction { + + private final boolean supportsStandard; + + public RegexpLikeOperatorFunction(TypeConfiguration typeConfiguration, boolean supportsStandard) { + super( typeConfiguration ); + this.supportsStandard = supportsStandard; + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final boolean caseSensitive; + if ( arguments.size() > 2 ) { + if ( !(arguments.get( 2 ) instanceof Literal literal) + || !(literal.getLiteralValue() instanceof String flags) + || !flags.equals( "i" ) ) { + if ( supportsStandard ) { + super.render( sqlAppender, arguments, returnType, walker ); + return; + } + else { + throw new IllegalArgumentException( + "PostgreSQL and CockroachDB only support the case insensitive flag 'i' as literal." ); + } + } + caseSensitive = false; + } + else { + caseSensitive = true; + } + + sqlAppender.appendSql( '(' ); + arguments.get( 0 ).accept( walker ); + sqlAppender.appendSql( caseSensitive ? "~" : "~*" ); + arguments.get( 1 ).accept( walker ); + sqlAppender.appendSql( ')' ); + } + + @Override + public boolean isPredicate() { + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpLikePredicateFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpLikePredicateFunction.java new file mode 100644 index 000000000000..9287c1484c1f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpLikePredicateFunction.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + + +/** + * Oracle and SQL Server have a special predicate function. + */ +public class RegexpLikePredicateFunction extends AbstractRegexpLikeFunction { + + public RegexpLikePredicateFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + sqlAppender.appendSql( "regexp_like(" ); + arguments.get( 0 ).accept( walker ); + sqlAppender.appendSql( ',' ); + arguments.get( 1 ).accept( walker ); + if ( arguments.size() > 2 ) { + sqlAppender.appendSql( ',' ); + arguments.get( 2 ).accept( walker ); + } + sqlAppender.appendSql( ')' ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpPredicateFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpPredicateFunction.java new file mode 100644 index 000000000000..1c2cf56da8a2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/RegexpPredicateFunction.java @@ -0,0 +1,69 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + + +/** + * MariaDB and legacy MySQL have a special predicate. + */ +public class RegexpPredicateFunction extends AbstractRegexpLikeFunction { + + public RegexpPredicateFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final boolean caseSensitive; + if ( arguments.size() > 2 ) { + if ( !(arguments.get( 2 ) instanceof Literal literal) + || !(literal.getLiteralValue() instanceof String flags) + || !flags.equals( "i" ) ) { + throw new IllegalArgumentException( "MariaDB and legacy MySQL only support the case insensitive flag 'i' as literal." ); + } + caseSensitive = false; + } + else { + caseSensitive = true; + } + + if ( !caseSensitive ) { + sqlAppender.appendSql( "lower(" ); + } + arguments.get( 0 ).accept( walker ); + if ( !caseSensitive ) { + sqlAppender.appendSql( ')' ); + } + sqlAppender.appendSql( " regexp " ); + if ( caseSensitive ) { + sqlAppender.appendSql( "binary " ); + } + else { + sqlAppender.appendSql( "lower(" ); + } + arguments.get( 1 ).accept( walker ); + if ( !caseSensitive ) { + sqlAppender.appendSql( ')' ); + } + } + + @Override + public boolean isPredicate() { + return false; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest.java index 8a77605a473e..93ecabe7b05d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest.java @@ -14,7 +14,9 @@ import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import org.hibernate.community.dialect.InformixDialect; +import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.HANADialect; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; @@ -36,6 +38,7 @@ public class ConstraintInterpretationTest { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.NOT_NULL, cve.getKind() ); + // DB2 and Informix error messages don't contain the primary key constraint name if ( !(scope.getDialect() instanceof DB2Dialect) && !(scope.getDialect() instanceof InformixDialect) ) { assertTrue( cve.getConstraintName().toLowerCase().endsWith( "id" ) ); } @@ -62,6 +65,7 @@ public class ConstraintInterpretationTest { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.NOT_NULL, cve.getKind() ); + // DB2 error message doesn't contain constraint or column name if ( !(scope.getDialect() instanceof DB2Dialect) ) { assertTrue( cve.getConstraintName().toLowerCase().endsWith( "name" ) ); } @@ -77,6 +81,7 @@ public class ConstraintInterpretationTest { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.UNIQUE, cve.getKind() ); + // DB2 error message doesn't contain unique constraint name if ( !(scope.getDialect() instanceof DB2Dialect) ) { assertTrue( cve.getConstraintName().toLowerCase().contains( "ssnuk" ) ); } @@ -91,7 +96,8 @@ public class ConstraintInterpretationTest { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.CHECK, cve.getKind() ); - if ( !(scope.getDialect() instanceof InformixDialect) ) { + // CockroachDB error messages don't contain the check constraint name + if ( !(scope.getDialect() instanceof CockroachDialect) ) { assertTrue( cve.getConstraintName().toLowerCase().endsWith( "namecheck" ) ); } } @@ -105,7 +111,10 @@ public class ConstraintInterpretationTest { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.FOREIGN_KEY, cve.getKind() ); - assertTrue( cve.getConstraintName().toLowerCase().endsWith( "id2to1fk" ) ); + // HANA error messages don't contain the foreign key constraint name + if ( !(scope.getDialect() instanceof HANADialect) ) { + assertTrue( cve.getConstraintName().toLowerCase().endsWith( "id2to1fk" ) ); + } } } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest2.java b/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest2.java index ed37a7b15560..11f92f02eb60 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest2.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/constraint/ConstraintInterpretationTest2.java @@ -14,12 +14,12 @@ import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import org.hibernate.community.dialect.InformixDialect; +import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.HANADialect; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -37,6 +37,7 @@ public class ConstraintInterpretationTest2 { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.NOT_NULL, cve.getKind() ); + // DB2 and Informix error messages don't contain the primary key constraint name if ( !(scope.getDialect() instanceof DB2Dialect) && !(scope.getDialect() instanceof InformixDialect) ) { assertTrue( cve.getConstraintName().toLowerCase().endsWith( "id" ) ); } @@ -63,6 +64,7 @@ public class ConstraintInterpretationTest2 { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.NOT_NULL, cve.getKind() ); + // DB2 error message doesn't contain constraint or column name if ( !(scope.getDialect() instanceof DB2Dialect) ) { assertTrue( cve.getConstraintName().toLowerCase().endsWith( "name" ) ); } @@ -78,6 +80,7 @@ public class ConstraintInterpretationTest2 { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.UNIQUE, cve.getKind() ); + // DB2 error message doesn't contain unique constraint name if ( !(scope.getDialect() instanceof DB2Dialect) ) { assertTrue( cve.getConstraintName().toLowerCase().contains( "ssnuk" ) ); } @@ -85,8 +88,6 @@ public class ConstraintInterpretationTest2 { } ); } - @SkipForDialect(dialectClass = InformixDialect.class, - reason = "multi-column check constraints must be created using 'alter table', and we don't have a StandardCheckConstraintExporter") @Test void testCheck(EntityManagerFactoryScope scope) { scope.inTransaction( em -> { try { @@ -95,7 +96,10 @@ public class ConstraintInterpretationTest2 { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.CHECK, cve.getKind() ); - assertTrue( cve.getConstraintName().toLowerCase().endsWith( "namecheck" ) ); + // CockroachDB error messages don't contain the check constraint name + if ( !(scope.getDialect() instanceof CockroachDialect) ) { + assertTrue( cve.getConstraintName().toLowerCase().endsWith( "namecheck" ) ); + } } } ); } @@ -107,6 +111,7 @@ public class ConstraintInterpretationTest2 { } catch (ConstraintViolationException cve) { assertEquals( ConstraintViolationException.ConstraintKind.FOREIGN_KEY, cve.getKind() ); + // HANA error messages don't contain the foreign key constraint name if ( !(scope.getDialect() instanceof HANADialect) ) { assertTrue( cve.getConstraintName().toLowerCase().endsWith( "id2to1fk" ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/RegexTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/RegexTest.java index c113b20ff308..315d53af2d68 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/RegexTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/RegexTest.java @@ -4,16 +4,11 @@ */ package org.hibernate.orm.test.query.hql; -import org.hibernate.community.dialect.InformixDialect; -import org.hibernate.dialect.HSQLDialect; -import org.hibernate.dialect.MariaDBDialect; -import org.hibernate.dialect.OracleDialect; -import org.hibernate.dialect.SQLServerDialect; -import org.hibernate.dialect.SybaseASEDialect; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; -import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -21,70 +16,49 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @Jpa +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsRegexpLike.class) class RegexTest { @Test - @SkipForDialect(dialectClass = SQLServerDialect.class, - reason = "regexp_like coming in 2025") - @SkipForDialect(dialectClass = SybaseASEDialect.class, - reason = "no regex support in Sybase ASE") void testInSelect(EntityManagerFactoryScope scope) { - if ( !( scope.getDialect() instanceof OracleDialect dialect - && ( dialect.isAutonomous() || dialect.getVersion().isBefore( 23 ) ) ) ) { - scope.inEntityManager( em -> { - assertTrue( em.createQuery( "select regexp_like('abcdef', 'ab.*')", Boolean.class ).getSingleResult() ); - assertTrue( em.createQuery( "select 'abcdef' like regexp 'ab.*'", Boolean.class ).getSingleResult() ); - var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); - var query = builder.createQuery( Boolean.class ); - query.select( builder.likeRegexp( builder.literal( "abcdef" ), "ab.*" ) ); - assertTrue( em.createQuery( query ).getSingleResult() ); - } ); - scope.inEntityManager( em -> { - assertFalse( em.createQuery( "select not regexp_like('abcdef', 'ab.*')", Boolean.class ).getSingleResult() ); - assertFalse( em.createQuery( "select 'abcdef' not like regexp 'ab.*'", Boolean.class ).getSingleResult() ); - var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); - var query = builder.createQuery( Boolean.class ); - query.select( builder.notLikeRegexp( builder.literal( "abcdef" ), "ab.*" ) ); - assertFalse( em.createQuery( query ).getSingleResult() ); - } ); - } + scope.inEntityManager( em -> { + assertTrue( em.createQuery( "select regexp_like('abcdef', 'ab.*')", Boolean.class ).getSingleResult() ); + assertTrue( em.createQuery( "select 'abcdef' like regexp 'ab.*'", Boolean.class ).getSingleResult() ); + var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); + var query = builder.createQuery( Boolean.class ); + query.select( builder.likeRegexp( builder.literal( "abcdef" ), "ab.*" ) ); + assertTrue( em.createQuery( query ).getSingleResult() ); + } ); + scope.inEntityManager( em -> { + assertFalse( em.createQuery( "select not regexp_like('abcdef', 'ab.*')", Boolean.class ).getSingleResult() ); + assertFalse( em.createQuery( "select 'abcdef' not like regexp 'ab.*'", Boolean.class ).getSingleResult() ); + var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); + var query = builder.createQuery( Boolean.class ); + query.select( builder.notLikeRegexp( builder.literal( "abcdef" ), "ab.*" ) ); + assertFalse( em.createQuery( query ).getSingleResult() ); + } ); } @Test - @SkipForDialect(dialectClass = MariaDBDialect.class) - @SkipForDialect(dialectClass = HSQLDialect.class) - @SkipForDialect(dialectClass = SQLServerDialect.class, - reason = "regexp_like coming in 2025") - @SkipForDialect(dialectClass = SybaseASEDialect.class, - reason = "no regex support in Sybase ASE") - @SkipForDialect(dialectClass = InformixDialect.class, - reason = "This could be made to work on Informix by changing the flags") void testInSelectCaseInsensitive(EntityManagerFactoryScope scope) { - if ( !( scope.getDialect() instanceof OracleDialect dialect - && ( dialect.isAutonomous() || dialect.getVersion().isBefore( 23 ) ) ) ) { - scope.inEntityManager( em -> { - assertTrue( em.createQuery( "select regexp_like('ABCDEF', 'ab.*', 'i')", Boolean.class ).getSingleResult() ); - assertTrue( em.createQuery( "select 'abcdef' ilike regexp 'ab.*'", Boolean.class ).getSingleResult() ); - var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); - var query = builder.createQuery( Boolean.class ); - query.select( builder.ilikeRegexp( builder.literal( "ABCDEF" ), "ab.*" ) ); - assertTrue( em.createQuery( query ).getSingleResult() ); - } ); - scope.inEntityManager( em -> { - assertFalse( em.createQuery( "select not regexp_like('ABCDEF', 'ab.*', 'i')", Boolean.class ).getSingleResult() ); - assertFalse( em.createQuery( "select 'abcdef' not ilike regexp 'ab.*'", Boolean.class ).getSingleResult() ); - var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); - var query = builder.createQuery( Boolean.class ); - query.select( builder.notIlikeRegexp( builder.literal( "ABCDEF" ), "ab.*" ) ); - assertFalse( em.createQuery( query ).getSingleResult() ); - } ); - } + scope.inEntityManager( em -> { + assertTrue( em.createQuery( "select regexp_like('ABCDEF', 'ab.*', 'i')", Boolean.class ).getSingleResult() ); + assertTrue( em.createQuery( "select 'abcdef' ilike regexp 'ab.*'", Boolean.class ).getSingleResult() ); + var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); + var query = builder.createQuery( Boolean.class ); + query.select( builder.ilikeRegexp( builder.literal( "ABCDEF" ), "ab.*" ) ); + assertTrue( em.createQuery( query ).getSingleResult() ); + } ); + scope.inEntityManager( em -> { + assertFalse( em.createQuery( "select not regexp_like('ABCDEF', 'ab.*', 'i')", Boolean.class ).getSingleResult() ); + assertFalse( em.createQuery( "select 'abcdef' not ilike regexp 'ab.*'", Boolean.class ).getSingleResult() ); + var builder = (HibernateCriteriaBuilder) em.getCriteriaBuilder(); + var query = builder.createQuery( Boolean.class ); + query.select( builder.notIlikeRegexp( builder.literal( "ABCDEF" ), "ab.*" ) ); + assertFalse( em.createQuery( query ).getSingleResult() ); + } ); } @Test - @SkipForDialect(dialectClass = SQLServerDialect.class, - reason = "regexp_like coming in 2025") - @SkipForDialect(dialectClass = SybaseASEDialect.class, - reason = "no regex support in Sybase ASE") void testInWhere(EntityManagerFactoryScope scope) { scope.inEntityManager( em -> { assertEquals( 1, em.createQuery( "select 1 where regexp_like('abcdef', 'ab.*')", Integer.class ).getSingleResult() ); 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 9476ef9707b3..b968c4fbcf9d 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 @@ -1075,6 +1075,12 @@ public boolean apply(Dialect dialect) { } } + public static class SupportsRegexpLike implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return definesFunction( dialect, "regexp_like" ); + } + } + public static class IsJtds implements DialectFeatureCheck { public boolean apply(Dialect dialect) { return dialect instanceof SybaseDialect && ( (SybaseDialect) dialect ).getDriverKind() == SybaseDriverKind.JTDS; diff --git a/settings.gradle b/settings.gradle index 43cf51e4c366..1439c851d559 100644 --- a/settings.gradle +++ b/settings.gradle @@ -230,7 +230,7 @@ dependencyResolutionManagement { def hsqldbVersion = version "hsqldb", overrideableVersion( "gradle.libs.versions.hsqldb", "2.7.4" ) def informixVersion = version "informix", "15.0.0.1.1" def mariadbVersion = version "mariadb", "3.5.5" - def mssqlVersion = version "mssql", "12.10.1.jre11" + def mssqlVersion = version "mssql", "13.2.0.jre11" def mysqlVersion = version "mysql", "9.4.0" def oracleVersion = version "oracle", "23.9.0.25.07" def oracleJacksonOsonExtension = version "oracleJacksonOsonExtension", "1.0.4"