From 4914d14a43cc18cd58000ed55d138e140b3bd49e Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 3 Nov 2022 15:15:01 +0100 Subject: [PATCH] HHH-15626 Move old databases to nightly pipeline and add some SQL Server 2022 features --- .github/workflows/contributor-build.yml | 2 - Jenkinsfile | 110 +++++-- README.adoc | 28 +- ci/build.sh | 41 ++- ci/database-start.sh | 10 +- docker_db.sh | 295 +++++++++++++----- .../org/hibernate/userguide/hql/HQLTest.java | 19 +- .../mapping/basic/BooleanMappingTests.java | 2 +- .../basic/EnumerationCustomTypeTest.java | 2 + edb/{Dockerfile => edb10.Dockerfile} | 0 edb/edb14.Dockerfile | 48 +++ gradle/databases.gradle | 161 +--------- .../community/dialect/DB2LegacyDialect.java | 129 +++++--- .../dialect/DB2LegacySqlAstTranslator.java | 86 ++++- .../community/dialect/H2LegacyDialect.java | 1 + .../dialect/H2LegacySqlAstTranslator.java | 6 + .../MariaDBLegacySqlAstTranslator.java | 19 ++ .../dialect/MySQLLegacySqlAstTranslator.java | 10 + .../dialect/OracleLegacySqlAstTranslator.java | 35 +-- .../dialect/PostgreSQLLegacyDialect.java | 3 +- .../PostgreSQLLegacySqlAstTranslator.java | 10 + .../dialect/SQLServerLegacyDialect.java | 33 +- .../SQLServerLegacySqlAstTranslator.java | 10 + .../SybaseASELegacySqlAstTranslator.java | 10 + .../SybaseAnywhereSqlAstTranslator.java | 10 + .../dialect/SybaseLegacySqlAstTranslator.java | 10 + .../process/spi/MetadataBuildingProcess.java | 26 +- .../org/hibernate/dialect/DB2Dialect.java | 162 +++++++--- .../dialect/DB2SqlAstTranslator.java | 94 +++++- .../java/org/hibernate/dialect/Dialect.java | 10 +- .../java/org/hibernate/dialect/H2Dialect.java | 1 + .../hibernate/dialect/H2SqlAstTranslator.java | 6 + .../dialect/HANASqlAstTranslator.java | 20 -- .../dialect/MariaDBSqlAstTranslator.java | 19 ++ .../dialect/OracleSqlAstTranslator.java | 48 +-- .../hibernate/dialect/PostgreSQLDialect.java | 3 +- .../dialect/PostgreSQLSqlAstTranslator.java | 10 + .../dialect/SQLServer2016Dialect.java | 2 +- .../hibernate/dialect/SQLServerDialect.java | 33 +- .../dialect/SQLServerSqlAstTranslator.java | 11 + .../dialect/SybaseASESqlAstTranslator.java | 10 + .../dialect/SybaseSqlAstTranslator.java | 10 + .../function/CommonFunctionFactory.java | 43 ++- .../dialect/function/DB2PositionFunction.java | 62 ++++ .../function/DB2SubstringFunction.java | 64 ++++ .../dialect/function/EveryAnyEmulation.java | 26 +- .../ordering/ast/OrderingSpecification.java | 2 +- .../hql/internal/SemanticQueryBuilder.java | 8 +- .../function/ArgumentTypesValidator.java | 2 +- .../sqm/tree/select/SqmSortSpecification.java | 7 +- .../sql/ast/spi/AbstractSqlAstTranslator.java | 213 +++++++------ .../ast/tree/select/SortSpecification.java | 3 + .../manytoone/NotNullManyToOneTest.java | 3 + .../refcolnames/misc/Misc3Test.java | 6 +- ...ySQLDropConstraintThrowsExceptionTest.java | 2 +- .../xml/ejb3/PreParsedOrmXmlTest.java | 3 + .../xml/hbm/PreParsedHbmXmlTest.java | 3 + .../batch/BatchOptimisticLockingTest.java | 10 + .../DefaultCatalogAndSchemaTest.java | 3 + ...kingNotInDefaultFetchGroupPersistTest.java | 3 + .../dirty/DirtyTrackingPersistTest.java | 3 + ...dAndUpdateEntitiesWithCollectionsTest.java | 3 + .../DynamicUpdateAndCollectionsTest.java | 3 + .../orm/test/cut/CompositeUserTypeTest.java | 2 +- .../OracleDialectSequenceInformationTest.java | 55 ++-- .../test/hql/DB297SubStringFunctionsTest.java | 10 +- .../identity/hhh9983/SaveEntityTest.java | 3 + ...rsistAndQueryingInSameTransactionTest.java | 3 + ...ativeQueryResultTypeAutoDiscoveryTest.java | 81 ++--- .../generated/always/GeneratedAlwaysTest.java | 2 + ...entityGeneratorWithNaturalIdCacheTest.java | 6 +- .../pagination/SubqueryPaginationTest.java | 7 + .../internal/hhh11877/HHH111877Test.java | 3 + .../internal/hhh14197/HHH14197Test.java | 3 + .../internal/hhh14916/HHH14916Test.java | 3 + .../orm/test/query/hql/FunctionTests.java | 3 +- .../test/query/hql/StandardFunctionTests.java | 4 +- .../test/schemafilter/RecordingTarget.java | 4 +- .../lazy/ManyToOneLazyDeleteTest.java | 3 + .../lazy/ManyToOneLazyFetchTest.java | 3 + .../integration/query/OrderByLimitQuery.java | 1 - .../internal/HikariCPConnectionProvider.java | 5 +- .../src/test/resources/hibernate.properties | 4 +- hibernate-spatial/hibernate-spatial.gradle | 10 +- .../oracle/OracleSQLMMFunctionTests.java | 3 + .../functions/CommonFunctionTests.java | 4 + ...TestGeometryConstructionWithParameter.java | 3 + .../predicates/PredicateSmokeTest.java | 3 + .../SpatialPredicatesTestInlineMode.java | 3 + .../testing/junit4/CustomRunner.java | 50 +++ .../orm/junit/DialectFeatureChecks.java | 7 +- .../orm/junit/DialectFilterExtension.java | 2 +- .../testing/transaction/TransactionUtil.java | 38 ++- nightly.Jenkinsfile | 110 +++++-- settings.gradle | 28 +- 95 files changed, 1732 insertions(+), 761 deletions(-) rename edb/{Dockerfile => edb10.Dockerfile} (100%) create mode 100644 edb/edb14.Dockerfile create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/DB2PositionFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java diff --git a/.github/workflows/contributor-build.yml b/.github/workflows/contributor-build.yml index f570fd7a4e..acf7fb8a82 100644 --- a/.github/workflows/contributor-build.yml +++ b/.github/workflows/contributor-build.yml @@ -39,10 +39,8 @@ jobs: - rdbms: hsqldb - rdbms: derby - rdbms: mysql - - rdbms: mysql8 - rdbms: mariadb - rdbms: postgresql - - rdbms: postgresql_14 - rdbms: edb - rdbms: oracle - rdbms: db2 diff --git a/Jenkinsfile b/Jenkinsfile index 8feb7dd4f2..d5e18b8fe5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -26,14 +26,24 @@ this.helper = new JobHelper(this) helper.runWithNotification { stage('Configure') { this.environments = [ + // TODO: this block is just temporary for testing, but should ultimately be removed because this is tested nightly + new BuildEnvironment( dbName: 'h2_1_4' ), + new BuildEnvironment( dbName: 'hsqldb_2_6' ), + new BuildEnvironment( dbName: 'derby_10_14' ), + new BuildEnvironment( dbName: 'mysql_5_7' ), + new BuildEnvironment( dbName: 'mariadb_10_3' ), + new BuildEnvironment( dbName: 'postgresql_10' ), + new BuildEnvironment( dbName: 'edb_10' ), + new BuildEnvironment( dbName: 'oracle_11_2' ), + new BuildEnvironment( dbName: 'db2_10_5', longRunning: true ), + new BuildEnvironment( dbName: 'mssql_2017' ), + // new BuildEnvironment( dbName: 'h2' ), // new BuildEnvironment( dbName: 'hsqldb' ), // new BuildEnvironment( dbName: 'derby' ), // new BuildEnvironment( dbName: 'mysql' ), -// new BuildEnvironment( dbName: 'mysql8' ), // new BuildEnvironment( dbName: 'mariadb' ), // new BuildEnvironment( dbName: 'postgresql' ), -// new BuildEnvironment( dbName: 'postgresql_14' ), // new BuildEnvironment( dbName: 'edb' ), // new BuildEnvironment( dbName: 'oracle' ), // new BuildEnvironment( dbName: 'db2' ), @@ -126,55 +136,84 @@ stage('Build') { try { stage('Start database') { switch (buildEnv.dbName) { - case "cockroachdb": - docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('cockroachdb/cockroach:v21.1.21').pull() - } - sh "./docker_db.sh cockroachdb" - state[buildEnv.tag]['containerName'] = "cockroach" + case "h2_1_4": + state[buildEnv.tag]['additionalOptions'] = state[buildEnv.tag]['additionalOptions'] + + " -Pgradle.libs.versions.h2=1.4.197 -Pgradle.libs.versions.h2gis=1.5.0" + break; + case "hsqldb_2_6": + state[buildEnv.tag]['additionalOptions'] = state[buildEnv.tag]['additionalOptions'] + + " -Pgradle.libs.versions.hsqldb=2.6.1" + break; + case "derby_10_14": + state[buildEnv.tag]['additionalOptions'] = state[buildEnv.tag]['additionalOptions'] + + " -Pgradle.libs.versions.derby=10.14.2.0" break; case "mysql": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('mysql:5.7').pull() + docker.image('mysql:8.0.31').pull() } sh "./docker_db.sh mysql" state[buildEnv.tag]['containerName'] = "mysql" break; - case "mysql8": + case "mysql_5_7": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('mysql:8.0.21').pull() + docker.image('mysql:5.7.40').pull() } - sh "./docker_db.sh mysql_8_0" + sh "./docker_db.sh mysql_5_7" state[buildEnv.tag]['containerName'] = "mysql" break; case "mariadb": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('mariadb:10.7.5').pull() + docker.image('mariadb:10.9.3').pull() } sh "./docker_db.sh mariadb" state[buildEnv.tag]['containerName'] = "mariadb" break; + case "mariadb_10_3": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('mariadb:10.3.36').pull() + } + sh "./docker_db.sh mariadb_10_3" + state[buildEnv.tag]['containerName'] = "mariadb" + break; case "postgresql": // use the postgis image to enable the PGSQL GIS (spatial) extension docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('postgis/postgis:9.5-2.5').pull() + docker.image('postgis/postgis:14-3.3').pull() } sh "./docker_db.sh postgresql" state[buildEnv.tag]['containerName'] = "postgres" break; - case "postgresql_14": + case "postgresql_10": // use the postgis image to enable the PGSQL GIS (spatial) extension docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('postgis/postgis:14-3.3').pull() + docker.image('postgis/postgis:10-2.5').pull() } - sh "./docker_db.sh postgresql_14" + sh "./docker_db.sh postgresql_10" state[buildEnv.tag]['containerName'] = "postgres" break; + case "edb": + docker.image('quay.io/enterprisedb/edb-postgres-advanced:14.5-3.2-postgis').pull() + sh "./docker_db.sh edb" + state[buildEnv.tag]['containerName'] = "edb" + break; + case "edb_10": + docker.image('quay.io/enterprisedb/edb-postgres-advanced:10.22').pull() + sh "./docker_db.sh edb_10" + state[buildEnv.tag]['containerName'] = "edb" + break; case "oracle": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('gvenzl/oracle-xe:18.4.0-full').pull() + docker.image('gvenzl/oracle-xe:21.3.0-full').pull() } - sh "./docker_db.sh oracle_18" + sh "./docker_db.sh oracle" + state[buildEnv.tag]['containerName'] = "oracle" + break; + case "oracle_11_2": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('gvenzl/oracle-xe:11.2.0.2-full').pull() + } + sh "./docker_db.sh oracle_11" state[buildEnv.tag]['containerName'] = "oracle" break; case "db2": @@ -184,11 +223,23 @@ stage('Build') { sh "./docker_db.sh db2" state[buildEnv.tag]['containerName'] = "db2" break; + case "db2_10_5": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('ibmoms/db2express-c@sha256:a499afd9709a1f69fb41703e88def9869955234c3525547e2efc3418d1f4ca2b').pull() + } + sh "./docker_db.sh db2_10_5" + state[buildEnv.tag]['containerName'] = "db2" + break; case "mssql": docker.image('mcr.microsoft.com/mssql/server@sha256:f54a84b8a802afdfa91a954e8ddfcec9973447ce8efec519adf593b54d49bedf').pull() sh "./docker_db.sh mssql" state[buildEnv.tag]['containerName'] = "mssql" break; + case "mssql_2017": + docker.image('mcr.microsoft.com/mssql/server@sha256:7d194c54e34cb63bca083542369485c8f4141596805611e84d8c8bab2339eede').pull() + sh "./docker_db.sh mssql_2017" + state[buildEnv.tag]['containerName'] = "mssql" + break; case "sybase": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { docker.image('nguoianphu/docker-sybase').pull() @@ -196,10 +247,19 @@ stage('Build') { sh "./docker_db.sh sybase" state[buildEnv.tag]['containerName'] = "sybase" break; - case "edb": - docker.image('quay.io/enterprisedb/edb-postgres-advanced:10.22').pull() - sh "./docker_db.sh edb" - state[buildEnv.tag]['containerName'] = "edb" + case "cockroachdb": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('cockroachdb/cockroach:v22.1.10').pull() + } + sh "./docker_db.sh cockroachdb" + state[buildEnv.tag]['containerName'] = "cockroach" + break; + case "cockroachdb_21_2": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('cockroachdb/cockroach:v21.2.16').pull() + } + sh "./docker_db.sh cockroachdb_21_2" + state[buildEnv.tag]['containerName'] = "cockroach" break; } } @@ -208,7 +268,7 @@ stage('Build') { withEnv(["RDBMS=${buildEnv.dbName}"]) { try { if (buildEnv.dbLockableResource == null) { - timeout( [time: buildEnv.longRunning ? 240 : 120, unit: 'MINUTES'] ) { + timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { sh cmd } } @@ -217,7 +277,7 @@ stage('Build') { if ( buildEnv.dbLockResourceAsHost ) { cmd += " -DdbHost=${LOCKED_RESOURCE}" } - timeout( [time: buildEnv.longRunning ? 240 : 120, unit: 'MINUTES'] ) { + timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { sh cmd } } diff --git a/README.adoc b/README.adoc index e76650c3a8..a8f863c3c2 100644 --- a/README.adoc +++ b/README.adoc @@ -161,26 +161,18 @@ The following table illustrates a list of commands for various databases that ca |- |`./gradlew test -Pdb=derby` -|MySQL 5.7 +|MySQL |`./docker_db.sh mysql` |`./gradlew test -Pdb=mysql_ci` -|MySQL 8.0 -|`./docker_db.sh mysql_8_0` -|`./gradlew test -Pdb=mysql_ci` - |MariaDB |`./docker_db.sh mariadb` |`./gradlew test -Pdb=mariadb_ci` -|PostgreSQL 10 +|PostgreSQL |`./docker_db.sh postgresql` |`./gradlew test -Pdb=pgsql_ci` -|PostgreSQL 14 -|`./docker_db.sh postgresql_14` -|`./gradlew test -Pdb=pgsql_ci` - |EnterpriseDB |`./docker_db.sh edb` |`./gradlew test -Pdb=edb_ci` @@ -189,22 +181,6 @@ The following table illustrates a list of commands for various databases that ca |`./docker_db.sh oracle` |`./gradlew test -Pdb=oracle_ci` -|Oracle 11g -|`./docker_db.sh oracle_11` -|`./gradlew test -Pdb=oracle_ci` - -|Oracle XE 18 -|`./docker_db.sh oracle_18` -|`./gradlew test -Pdb=oracle_ci` - -|Oracle XE 21 -|`./docker_db.sh oracle_21` -|`./gradlew test -Pdb=oracle_ci` - -|Oracle EE -|`./docker_db.sh oracle_ee` -|`./gradlew test -Pdb=oracle_docker` - |DB2 |`./docker_db.sh db2` |`./gradlew test -Pdb=db2_ci` diff --git a/ci/build.sh b/ci/build.sh index 54ebf564bb..28446f14ac 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -1,44 +1,39 @@ #! /bin/bash goal= -if [ "$RDBMS" == "h2" ]; then +if [ "$RDBMS" == "h2" ] || [ "$RDBMS" == "h2_1_4" ]; then # This is the default. goal="" +elif [ "$RDBMS" == "hsqldb" ] || [ "$RDBMS" == "hsqldb_2_6" ]; then + goal="-Pdb=hsqldb" elif [ "$RDBMS" == "derby" ]; then goal="-Pdb=derby" -elif [ "$RDBMS" == "edb" ]; then - goal="-Pdb=edb_ci -DdbHost=localhost:5444" -elif [ "$RDBMS" == "hsqldb" ]; then - goal="-Pdb=hsqldb" -elif [ "$RDBMS" == "mysql8" ]; then +elif [ "$RDBMS" == "derby_10_14" ]; then + goal="-Pdb=derby_old" +elif [ "$RDBMS" == "mysql" ] || [ "$RDBMS" == "mysql_5_7" ]; then goal="-Pdb=mysql_ci" -elif [ "$RDBMS" == "mysql" ]; then - goal="-Pdb=mysql_ci" -elif [ "$RDBMS" == "mariadb" ]; then +elif [ "$RDBMS" == "mariadb" ] || [ "$RDBMS" == "mariadb_10_3" ]; then goal="-Pdb=mariadb_ci" -elif [ "$RDBMS" == "postgresql" ]; then +elif [ "$RDBMS" == "postgresql" ] || [ "$RDBMS" == "postgresql_10" ]; then goal="-Pdb=pgsql_ci" -elif [ "$RDBMS" == "postgresql_14" ]; then - goal="-Pdb=pgsql_ci" -elif [ "$RDBMS" == "oracle" ]; then - # I have no idea why, but these tests don't work on GH Actions - # yrodiere: Apparently those have been disabled on Jenkins as well... +elif [ "$RDBMS" == "edb" ] || [ "$RDBMS" == "edb_10" ]; then + goal="-Pdb=edb_ci -DdbHost=localhost:5444" +elif [ "$RDBMS" == "oracle" ] || [ "$RDBMS" == "oracle_11_2" ]; then + # I have no idea why, but these tests don't seem to work on CI... goal="-Pdb=oracle_ci -PexcludeTests=**.LockTest.testQueryTimeout*" -elif [ "$RDBMS" == "oracle_ee" ]; then - goal="-Pdb=oracle_jenkins" elif [ "$RDBMS" == "db2" ]; then goal="-Pdb=db2_ci" -elif [ "$RDBMS" == "mssql" ]; then +elif [ "$RDBMS" == "db2_10_5" ]; then + goal="-Pdb=db2" +elif [ "$RDBMS" == "mssql" ] || [ "$RDBMS" == "mssql_2017" ]; then goal="-Pdb=mssql_ci" -elif [ "$RDBMS" == "hana" ]; then - goal="-Pdb=hana_ci" -elif [ "$RDBMS" == "hana_cloud" ]; then - goal="-Pdb=hana_cloud" elif [ "$RDBMS" == "sybase" ]; then goal="-Pdb=sybase_ci" elif [ "$RDBMS" == "tidb" ]; then goal="-Pdb=tidb" -elif [ "$RDBMS" == "cockroachdb" ]; then +elif [ "$RDBMS" == "hana_cloud" ]; then + goal="-Pdb=hana_cloud" +elif [ "$RDBMS" == "cockroachdb" ] || [ "$RDBMS" == "cockroachdb_21_2" ]; then goal="-Pdb=cockroachdb" fi diff --git a/ci/database-start.sh b/ci/database-start.sh index 62e0bea3bd..a1baf2444b 100755 --- a/ci/database-start.sh +++ b/ci/database-start.sh @@ -4,26 +4,22 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" if [ "$RDBMS" == 'mysql' ]; then bash $DIR/../docker_db.sh mysql -elif [ "$RDBMS" == 'mysql8' ]; then - bash $DIR/../docker_db.sh mysql_8_0 elif [ "$RDBMS" == 'mariadb' ]; then bash $DIR/../docker_db.sh mariadb elif [ "$RDBMS" == 'postgresql' ]; then bash $DIR/../docker_db.sh postgresql -elif [ "$RDBMS" == 'postgresql_14' ]; then - bash $DIR/../docker_db.sh postgresql_14 elif [ "$RDBMS" == 'edb' ]; then bash $DIR/../docker_db.sh edb elif [ "$RDBMS" == 'db2' ]; then bash $DIR/../docker_db.sh db2 elif [ "$RDBMS" == 'oracle' ]; then - bash $DIR/../docker_db.sh oracle_18 + bash $DIR/../docker_db.sh oracle elif [ "$RDBMS" == 'mssql' ]; then bash $DIR/../docker_db.sh mssql -elif [ "$RDBMS" == 'hana' ]; then - bash $DIR/../docker_db.sh hana elif [ "$RDBMS" == 'sybase' ]; then bash $DIR/../docker_db.sh sybase elif [ "$RDBMS" == 'cockroachdb' ]; then bash $DIR/../docker_db.sh cockroachdb +elif [ "$RDBMS" == 'hana' ]; then + bash $DIR/../docker_db.sh hana fi \ No newline at end of file diff --git a/docker_db.sh b/docker_db.sh index ffe63b0543..bbb674a722 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -16,12 +16,12 @@ else fi mysql() { - mysql_5_7 + mysql_8_0 } mysql_5_7() { $CONTAINER_CLI rm -f mysql || true - $CONTAINER_CLI run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --log-bin-trust-function-creators=1 + $CONTAINER_CLI run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mysql:5.7.40 --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --log-bin-trust-function-creators=1 # Give the container some time to start OUTPUT= n=0 @@ -45,7 +45,7 @@ mysql_5_7() { mysql_8_0() { $CONTAINER_CLI rm -f mysql || true - $CONTAINER_CLI run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mysql:8.0.21 --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_as_cs --skip-character-set-client-handshake --log-bin-trust-function-creators=1 + $CONTAINER_CLI run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mysql:8.0.31 --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_as_cs --skip-character-set-client-handshake --log-bin-trust-function-creators=1 # Give the container some time to start OUTPUT= n=0 @@ -68,8 +68,35 @@ mysql_8_0() { } mariadb() { + mariadb_10_9 +} + +mariadb_10_3() { $CONTAINER_CLI rm -f mariadb || true - $CONTAINER_CLI run --name mariadb -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mariadb:10.7.5 --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake + $CONTAINER_CLI run --name mariadb -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mariadb:10.3.36 --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake + OUTPUT= + n=0 + until [ "$n" -ge 5 ] + do + # Need to access STDERR. Thanks for the snippet https://stackoverflow.com/a/56577569/412446 + { OUTPUT="$( { $CONTAINER_CLI logs mariadb; } 2>&1 1>&3 3>&- )"; } 3>&1; + if [[ $OUTPUT == *"ready for connections"* ]]; then + break; + fi + n=$((n+1)) + echo "Waiting for MariaDB to start..." + sleep 3 + done + if [ "$n" -ge 5 ]; then + echo "MariaDB failed to start and configure after 15 seconds" + else + echo "MariaDB successfully started" + fi +} + +mariadb_10_9() { + $CONTAINER_CLI rm -f mariadb || true + $CONTAINER_CLI run --name mariadb -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mariadb:10.9.3 --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake OUTPUT= n=0 until [ "$n" -ge 5 ] @@ -91,7 +118,7 @@ mariadb() { } postgresql() { - postgresql_10 + postgresql_14 } postgresql_9_5() { @@ -115,19 +142,28 @@ postgresql_14() { } edb() { - edb_10 + edb_14 } edb_10() { $CONTAINER_CLI rm -f edb || true - # The version of the base image can be seen and updated in ./edb/Dockerfile # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator - (cd edb; $CONTAINER_CLI build -t edb-test:latest .) - $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:latest + (cd edb; $CONTAINER_CLI build -t edb-test:10 -f edb10.Dockerfile .) + $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:10 +} + +edb_14() { + $CONTAINER_CLI rm -f edb || true + # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator + (cd edb; $CONTAINER_CLI build -t edb-test:14 -f edb14.Dockerfile .) + $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:14 } db2() { - echo $CONTAINER_CLI + db2_11_5 +} + +db2_11_5() { $PRIVILEGED_CLI $CONTAINER_CLI rm -f db2 || true $PRIVILEGED_CLI $CONTAINER_CLI run --name db2 --privileged -e DB2INSTANCE=orm_test -e DB2INST1_PASSWORD=orm_test -e DBNAME=orm_test -e LICENSE=accept -e AUTOCONFIG=false -e ARCHIVE_LOGS=false -e TO_CREATE_SAMPLEDB=false -e REPODB=false -p 50000:50000 -d docker.io/ibmcom/db2:11.5.7.0 # Give the container some time to start @@ -140,6 +176,28 @@ db2() { $PRIVILEGED_CLI $CONTAINER_CLI exec -t db2 su - orm_test bash -c ". /database/config/orm_test/sqllib/db2profile && /database/config/orm_test/sqllib/bin/db2 'connect to orm_test' && /database/config/orm_test/sqllib/bin/db2 'CREATE USER TEMPORARY TABLESPACE usr_tbsp MANAGED BY AUTOMATIC STORAGE'" } +db2_10_5() { + $PRIVILEGED_CLI $CONTAINER_CLI rm -f db2 || true + # The sha represents the tag 10.5.0.5-3.10.0 + $PRIVILEGED_CLI $CONTAINER_CLI run --name db2 --privileged -e DB2INST1_PASSWORD=db2inst1-pwd -e LICENSE=accept -p 50000:50000 -d docker.io/ibmoms/db2express-c@sha256:a499afd9709a1f69fb41703e88def9869955234c3525547e2efc3418d1f4ca2b db2start + # Give the container some time to start + OUTPUT= + while [[ $OUTPUT != *"DB2START"* ]]; do + echo "Waiting for DB2 to start..." + sleep 10 + OUTPUT=$($PRIVILEGED_CLI $CONTAINER_CLI logs db2) + done + $PRIVILEGED_CLI $CONTAINER_CLI exec -t db2 su - db2inst1 bash -c "/home/db2inst1/sqllib/bin/db2 create database orm_test && + /home/db2inst1/sqllib/bin/db2 'connect to orm_test' && + /home/db2inst1/sqllib/bin/db2 'CREATE BUFFERPOOL BP8K pagesize 8K' && + /home/db2inst1/sqllib/bin/db2 'CREATE SYSTEM TEMPORARY TABLESPACE STB_8 PAGESIZE 8K BUFFERPOOL BP8K' && + /home/db2inst1/sqllib/bin/db2 'CREATE BUFFERPOOL BP16K pagesize 16K' && + /home/db2inst1/sqllib/bin/db2 'CREATE SYSTEM TEMPORARY TABLESPACE STB_16 PAGESIZE 16K BUFFERPOOL BP16K' && + /home/db2inst1/sqllib/bin/db2 'CREATE BUFFERPOOL BP32K pagesize 32K' && + /home/db2inst1/sqllib/bin/db2 'CREATE SYSTEM TEMPORARY TABLESPACE STB_32 PAGESIZE 32K BUFFERPOOL BP32K' && + /home/db2inst1/sqllib/bin/db2 'CREATE USER TEMPORARY TABLESPACE usr_tbsp MANAGED BY AUTOMATIC STORAGE'" +} + db2_spatial() { $PRIVILEGED_CLI $CONTAINER_CLI rm -f db2spatial || true temp_dir=$(mktemp -d) @@ -200,9 +258,35 @@ EOF } mssql() { + mssql_2022 +} + +mssql_2017() { $CONTAINER_CLI rm -f mssql || true - #This sha256 matches a specific tag of mcr.microsoft.com/mssql/server:2019-latest : - $CONTAINER_CLI run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y mcr.microsoft.com/mssql/server@sha256:f54a84b8a802afdfa91a954e8ddfcec9973447ce8efec519adf593b54d49bedf + #This sha256 matches a specific tag of mcr.microsoft.com/mssql/server:2017-latest : + $CONTAINER_CLI run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y mcr.microsoft.com/mssql/server@sha256:7d194c54e34cb63bca083542369485c8f4141596805611e84d8c8bab2339eede + 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-tools/bin/sqlcmd -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 +} + +mssql_2022() { + $CONTAINER_CLI rm -f mssql || true + #This sha256 matches a specific tag of mcr.microsoft.com/mssql/server:2022-latest : + $CONTAINER_CLI run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y mcr.microsoft.com/mssql/server@sha256:5439be9edc3b514cf647bcd3651779fa13f487735a985f40cbdcfecc60fea273 sleep 5 n=0 until [ "$n" -ge 5 ] @@ -362,13 +446,13 @@ oracle_setup() { # We increase file sizes to avoid online resizes as that requires lots of CPU which is restricted in XE $CONTAINER_CLI exec oracle bash -c "source /home/oracle/.bashrc; bash -c \" cat < /dev/null; then + $CONTAINER_CLI healthcheck run oracle > /dev/null + fi + HEALTHSTATUS="`$CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`" + HEALTHSTATUS=${HEALTHSTATUS##+( )} #Remove longest matching series of spaces from the front + HEALTHSTATUS=${HEALTHSTATUS%%+( )} #Remove longest matching series of spaces from the back + done + # We increase file sizes to avoid online resizes as that requires lots of CPU which is restricted in XE + $CONTAINER_CLI exec oracle bash -c "source /home/oracle/.bashrc; bash -c \" +cat < /dev/null; then + $CONTAINER_CLI healthcheck run oracle > /dev/null + fi + HEALTHSTATUS="`$CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`" + HEALTHSTATUS=${HEALTHSTATUS##+( )} #Remove longest matching series of spaces from the front + HEALTHSTATUS=${HEALTHSTATUS%%+( )} #Remove longest matching series of spaces from the back + done + sleep 2; + echo "Oracle successfully started" } oracle() { - oracle_18 + oracle_21 } oracle_11() { @@ -404,7 +537,7 @@ oracle_11() { --health-timeout 5s \ --health-retries 10 \ docker.io/gvenzl/oracle-xe:11.2.0.2-full - oracle_setup + oracle_setup_old } oracle_18() { @@ -433,46 +566,6 @@ oracle_21() { oracle_setup } -oracle_ee() { - #$CONTAINER_CLI login - $CONTAINER_CLI rm -f oracle || true - # We need to use the defaults - # sys as sysdba/Oradoc_db1 - $CONTAINER_CLI run --name oracle -d -p 1521:1521 docker.io/store/oracle/database-enterprise:12.2.0.1-slim - # Give the container some time to start - OUTPUT= - while [[ $OUTPUT != *"NLS_CALENDAR"* ]]; do - echo "Waiting for Oracle to start..." - sleep 10 - OUTPUT=$($CONTAINER_CLI logs oracle) - done - echo "Oracle successfully started" - # We increase file sizes to avoid online resizes as that requires lots of CPU which is restricted in XE - $CONTAINER_CLI exec oracle bash -c "source /home/oracle/.bashrc; \$ORACLE_HOME/bin/sqlplus sys/Oradoc_db1@ORCLCDB as sysdba <$temp_dir/password.json @@ -500,6 +593,54 @@ hana() { } cockroachdb() { + cockroachdb_22_1 +} + +cockroachdb_22_1() { + $CONTAINER_CLI rm -f cockroach || true + LOG_CONFIG=" +sinks: + stderr: + channels: all + filter: ERROR + redact: false + exit-on-error: true +" + $CONTAINER_CLI run -d --name=cockroach -m 3g -p 26257:26257 -p 8080:8080 docker.io/cockroachdb/cockroach:v22.1.10 start-single-node \ + --insecure --store=type=mem,size=640MiB --advertise-addr=localhost --log="$LOG_CONFIG" + OUTPUT= + while [[ $OUTPUT != *"CockroachDB node starting"* ]]; do + echo "Waiting for CockroachDB to start..." + sleep 10 + # Note we need to redirect stderr to stdout to capture the logs + OUTPUT=$($CONTAINER_CLI logs cockroach 2>&1) + done + echo "Enabling experimental box2d operators and some optimized settings for running the tests" + #settings documented in https://www.cockroachlabs.com/docs/v21.2/local-testing.html#use-a-local-single-node-cluster-with-in-memory-storage + $CONTAINER_CLI exec cockroach bash -c "cat < { //tag::hql-aggregate-functions-within-group-example[] @@ -1552,7 +1552,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test - @SkipForDialect(value = DerbyDialect.class, comment = "See https://issues.apache.org/jira/browse/DERBY-2072") + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "See https://issues.apache.org/jira/browse/DERBY-2072") public void test_hql_concat_function_example() { doInJPA(this::entityManagerFactory, entityManager -> { //tag::hql-concat-function-example[] @@ -1716,7 +1716,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test - @SkipForDialect(value = CockroachDialect.class, comment = "https://github.com/cockroachdb/cockroach/issues/26710") + @SkipForDialect(dialectClass = CockroachDialect.class, reason = "https://github.com/cockroachdb/cockroach/issues/26710") public void test_hql_sqrt_function_example() { doInJPA(this::entityManagerFactory, entityManager -> { //tag::hql-sqrt-function-example[] @@ -1731,8 +1731,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test - @SkipForDialect(SQLServerDialect.class) - @SkipForDialect(value = DerbyDialect.class, comment = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported") + @SkipForDialect(dialectClass = SQLServerDialect.class) + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported") public void test_hql_current_date_function_example() { doInJPA(this::entityManagerFactory, entityManager -> { //tag::hql-current-date-function-example[] @@ -1869,7 +1869,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test - @SkipForDialect(SQLServerDialect.class) + @SkipForDialect(dialectClass = SQLServerDialect.class) public void test_hql_str_function_example() { doInJPA(this::entityManagerFactory, entityManager -> { //tag::hql-str-function-example[] @@ -2006,7 +2006,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test - @SkipForDialect(value = DerbyDialect.class, comment = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported") + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported") public void test_hql_collection_expressions_example_8() { doInJPA(this::entityManagerFactory, entityManager -> { //tag::hql-collection-expressions-all-example[] @@ -3234,6 +3234,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { DialectChecks.SupportsSubqueryInOnClause.class, DialectChecks.SupportsOrderByInCorrelatedSubquery.class }) + @SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "The lateral emulation for Oracle 11 would be very complex because nested correlation is unsupported") public void test_hql_derived_join_example() { doInJPA(this::entityManagerFactory, entityManager -> { diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BooleanMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BooleanMappingTests.java index 5399a62369..620508fc50 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BooleanMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BooleanMappingTests.java @@ -51,7 +51,7 @@ public class BooleanMappingTests { assertThat( jdbcMapping.getJdbcType().getJdbcTypeCode(), // the implicit mapping will depend on the Dialect - isOneOf(Types.BOOLEAN, Types.BIT, Types.TINYINT) + isOneOf( Types.BOOLEAN, Types.BIT, Types.TINYINT, Types.SMALLINT ) ); } diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/EnumerationCustomTypeTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/EnumerationCustomTypeTest.java index 04b7e5b950..a29422693a 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/EnumerationCustomTypeTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/EnumerationCustomTypeTest.java @@ -11,6 +11,7 @@ import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.junit.Test; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; @@ -49,6 +50,7 @@ public class EnumerationCustomTypeTest extends BaseEntityManagerFunctionalTestCa private String name; @Type(org.hibernate.userguide.mapping.basic.GenderType.class) + @Column(length = 6) public Gender gender; //Getters and setters are omitted for brevity diff --git a/edb/Dockerfile b/edb/edb10.Dockerfile similarity index 100% rename from edb/Dockerfile rename to edb/edb10.Dockerfile diff --git a/edb/edb14.Dockerfile b/edb/edb14.Dockerfile new file mode 100644 index 0000000000..46abf67b87 --- /dev/null +++ b/edb/edb14.Dockerfile @@ -0,0 +1,48 @@ +FROM quay.io/enterprisedb/edb-postgres-advanced:14.5-3.2-postgis +USER root +# this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values) +RUN chown -R postgres:postgres /var/lib/edb && chmod 777 /var/lib/edb && rm /docker-entrypoint-initdb.d/10_postgis.sh + +USER postgres +ENV LANG en_US.utf8 +ENV PG_MAJOR 14 +ENV PG_VERSION 14 +ENV PGPORT 5444 +ENV PGDATA /var/lib/edb/as$PG_MAJOR/data/ +VOLUME /var/lib/edb/as$PG_MAJOR/data/ + +COPY docker-entrypoint.sh /usr/local/bin/ +ENTRYPOINT ["docker-entrypoint.sh"] + +# We set the default STOPSIGNAL to SIGINT, which corresponds to what PostgreSQL +# calls "Fast Shutdown mode" wherein new connections are disallowed and any +# in-progress transactions are aborted, allowing PostgreSQL to stop cleanly and +# flush tables to disk, which is the best compromise available to avoid data +# corruption. +# +# Users who know their applications do not keep open long-lived idle connections +# may way to use a value of SIGTERM instead, which corresponds to "Smart +# Shutdown mode" in which any existing sessions are allowed to finish and the +# server stops when all sessions are terminated. +# +# See https://www.postgresql.org/docs/12/server-shutdown.html for more details +# about available PostgreSQL server shutdown signals. +# +# See also https://www.postgresql.org/docs/12/server-start.html for further +# justification of this as the default value, namely that the example (and +# shipped) systemd service files use the "Fast Shutdown mode" for service +# termination. +# +STOPSIGNAL SIGINT +# +# An additional setting that is recommended for all users regardless of this +# value is the runtime "--stop-timeout" (or your orchestrator/runtime's +# equivalent) for controlling how long to wait between sending the defined +# STOPSIGNAL and sending SIGKILL (which is likely to cause data corruption). +# +# The default in most runtimes (such as Docker) is 10 seconds, and the +# documentation at https://www.postgresql.org/docs/12/server-start.html notes +# that even 90 seconds may not be long enough in many instances. + +EXPOSE 5444 +CMD ["postgres"] \ No newline at end of file diff --git a/gradle/databases.gradle b/gradle/databases.gradle index a5326cfc7a..d2901bd02e 100644 --- a/gradle/databases.gradle +++ b/gradle/databases.gradle @@ -29,6 +29,14 @@ ext { 'connection.init_sql' : '' ], derby : [ + 'db.dialect' : 'org.hibernate.dialect.DerbyDialect', + 'jdbc.driver': 'org.apache.derby.iapi.jdbc.AutoloadedDriver', + 'jdbc.user' : 'hibernate_orm_test', + 'jdbc.pass' : 'hibernate_orm_test', + 'jdbc.url' : 'jdbc:derby:target/tmp/derby/hibernate_orm_test;databaseName=hibernate_orm_test;create=true', + 'connection.init_sql' : '' + ], + derby_old : [ 'db.dialect' : 'org.hibernate.dialect.DerbyDialect', 'jdbc.driver': 'org.apache.derby.jdbc.EmbeddedDriver', 'jdbc.user' : 'hibernate_orm_test', @@ -45,15 +53,6 @@ ext { 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0', 'connection.init_sql' : '' ], - pgsql_docker : [ - 'db.dialect' : 'org.hibernate.dialect.PostgreSQLDialect', - 'jdbc.driver': 'org.postgresql.Driver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com - 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0', - 'connection.init_sql' : '' - ], pgsql_ci : [ 'db.dialect' : 'org.hibernate.dialect.PostgreSQLDialect', 'jdbc.driver': 'org.postgresql.Driver', @@ -81,15 +80,6 @@ ext { 'jdbc.url' : 'jdbc:jtds:sybase://' + dbHost + ':5000/hibernate_orm_test;maxStatements=0;cacheMetaData=false', 'connection.init_sql' : 'set ansinull on' ], - sybase_ci_legacynull : [ - 'db.dialect' : 'org.hibernate.dialect.SybaseASEDialect', - 'jdbc.driver': 'net.sourceforge.jtds.jdbc.Driver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - // Disable prepared statement caching to avoid issues with changing schemas - 'jdbc.url' : 'jdbc:jtds:sybase://' + dbHost + ':5000/hibernate_orm_test;maxStatements=0;cacheMetaData=false', - 'connection.init_sql' : '' - ], mysql : [ 'db.dialect' : 'org.hibernate.dialect.MySQLDialect', 'jdbc.driver': 'com.mysql.cj.jdbc.Driver', @@ -98,14 +88,6 @@ ext { 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test', 'connection.init_sql' : '' ], - mysql_docker : [ - 'db.dialect' : 'org.hibernate.dialect.MySQLDialect', - 'jdbc.driver': 'com.mysql.cj.jdbc.Driver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?allowPublicKeyRetrieval=true', - 'connection.init_sql' : '' - ], mysql_ci : [ 'db.dialect' : 'org.hibernate.dialect.MySQLDialect', 'jdbc.driver': 'com.mysql.cj.jdbc.Driver', @@ -114,15 +96,6 @@ ext { 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?allowPublicKeyRetrieval=true', 'connection.init_sql' : '' ], - // uses docker mysql_8_0 - mysql8_spatial_ci: [ - 'db.dialect' : 'org.hibernate.spatial.dialect.mysql.MySQL8SpatialDialect', - 'jdbc.driver': 'com.mysql.cj.jdbc.Driver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?allowPublicKeyRetrieval=true', - 'connection.init_sql' : '' - ], mariadb : [ 'db.dialect' : 'org.hibernate.dialect.MariaDBDialect', 'jdbc.driver': 'org.mariadb.jdbc.Driver', @@ -139,14 +112,6 @@ ext { 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test', 'connection.init_sql' : '' ], - mariadb_spatial_ci : [ - 'db.dialect' : 'org.hibernate.spatial.dialect.mariadb.MariaDB103SpatialDialect', - 'jdbc.driver': 'org.mariadb.jdbc.Driver', - 'jdbc.user' : 'root', - 'jdbc.pass' : 'hibernate_orm_test', - 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test', - 'connection.init_sql' : '' - ], tidb : [ 'db.dialect' : 'org.hibernate.dialect.TiDBDialect', 'jdbc.driver': 'com.mysql.jdbc.Driver', @@ -155,23 +120,6 @@ ext { 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test', 'connection.init_sql' : '' ], - tidb_ci5 : [ - 'db.dialect' : 'org.hibernate.dialect.TiDBDialect', - 'jdbc.driver': 'com.mysql.jdbc.Driver', - 'jdbc.user' : 'root', - 'jdbc.pass' : '', - 'jdbc.url' : 'jdbc:mysql://' + dbHost + ':4000/test', - 'connection.init_sql' : '' - ], - postgis : [ - 'db.dialect' : 'org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect', - 'jdbc.driver': 'org.postgresql.Driver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com - 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0', - 'connection.init_sql' : '' - ], oracle : [ 'db.dialect' : 'org.hibernate.dialect.OracleDialect', 'jdbc.driver': 'oracle.jdbc.OracleDriver', @@ -180,31 +128,6 @@ ext { 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/xe', 'connection.init_sql' : '' ], - oracle_jenkins : [ - 'db.dialect' : 'org.hibernate.dialect.OracleDialect', - 'jdbc.driver': 'oracle.jdbc.OracleDriver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - 'jdbc.url' : 'jdbc:oracle:thin:@hibernate-testing-oracle-se.ccuzkqo3zqzq.us-east-1.rds.amazonaws.com:1521:ORCL', - 'connection.init_sql' : '' - ], - oracle_rds : [ - 'db.dialect' : 'org.hibernate.dialect.OracleDialect', - 'jdbc.driver': 'oracle.jdbc.OracleDriver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - 'jdbc.url' : 'jdbc:oracle:thin:@localhost:1521:ORCL', - 'connection.init_sql' : '' - ], - // Use ./docker_db.sh oracle_ee to start the database - oracle_docker : [ - 'db.dialect' : 'org.hibernate.dialect.OracleDialect', - 'jdbc.driver': 'oracle.jdbc.OracleDriver', - 'jdbc.user' : 'c##hibernate_orm_test', - 'jdbc.pass' : 'hibernate_orm_test', - 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/ORCLPDB1.localdomain', - 'connection.init_sql' : '' - ], oracle_ci : [ 'db.dialect' : 'org.hibernate.dialect.OracleDialect', 'jdbc.driver': 'oracle.jdbc.OracleDriver', @@ -213,14 +136,6 @@ ext { 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE', 'connection.init_sql' : '' ], - oracle_spatial_ci : [ - 'db.dialect' : 'org.hibernate.spatial.dialect.oracle.OracleSpatial10gDialect', - 'jdbc.driver': 'oracle.jdbc.OracleDriver', - 'jdbc.user' : 'SYSTEM', - 'jdbc.pass' : 'Oracle18', - 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE', - 'connection.init_sql' : '' - ], mssql : [ 'db.dialect' : 'org.hibernate.dialect.SQLServerDialect', 'jdbc.driver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver', @@ -250,7 +165,7 @@ ext { 'jdbc.driver': 'com.ibm.db2.jcc.DB2Driver', 'jdbc.user' : 'db2inst1', 'jdbc.pass' : 'db2inst1-pwd', - 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/hibern8', + 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test', 'connection.init_sql' : '' ], db2_ci : [ @@ -261,23 +176,6 @@ ext { 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test', 'connection.init_sql' : '' ], - db2_spatial_ci : [ - 'db.dialect' : 'org.hibernate.spatial.dialect.db2.DB2SpatialDialect', - 'jdbc.driver': 'com.ibm.db2.jcc.DB2Driver', - 'jdbc.user' : 'orm_test', - 'jdbc.pass' : 'orm_test', - 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test', - 'connection.init_sql' : '' - ], - hana : [ - 'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect', - 'jdbc.driver': 'com.sap.db.jdbc.Driver', - 'jdbc.user' : 'HIBERNATE_TEST', - 'jdbc.pass' : 'H1bernate_test', - // Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html - 'jdbc.url' : 'jdbc:sap://' + dbHost + ':30015/?statementCacheSize=0', - 'connection.init_sql' : '' - ], hana_cloud : [ 'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect', 'jdbc.driver': 'com.sap.db.jdbc.Driver', @@ -287,24 +185,6 @@ ext { 'jdbc.url' : 'jdbc:sap://' + dbHost + ':443/?encrypt=true&validateCertificate=false&statementCacheSize=0', 'connection.init_sql' : '' ], - hana_jenkins : [ - 'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect', - 'jdbc.driver': 'com.sap.db.jdbc.Driver', - 'jdbc.user' : 'HIBERNATE_TEST', - 'jdbc.pass' : 'H1bernate_test', - // Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html - 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39015/?statementCacheSize=0', - 'connection.init_sql' : '' - ], - hana_docker : [ - 'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect', - 'jdbc.driver': 'com.sap.db.jdbc.Driver', - 'jdbc.user' : 'SYSTEM', - 'jdbc.pass' : 'H1bernate_test', - // Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html - 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0', - 'connection.init_sql' : '' - ], hana_ci : [ 'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect', 'jdbc.driver': 'com.sap.db.jdbc.Driver', @@ -314,15 +194,6 @@ ext { 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0', 'connection.init_sql' : '' ], - hana_spatial_ci : [ - 'db.dialect' : 'org.hibernate.spatial.dialect.hana.HANASpatialDialect', - 'jdbc.driver': 'com.sap.db.jdbc.Driver', - 'jdbc.user' : 'SYSTEM', - 'jdbc.pass' : 'H1bernate_test', - // Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html - 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0', - 'connection.init_sql' : '' - ], cockroachdb : [ 'db.dialect' : 'org.hibernate.dialect.CockroachDialect', // CockroachDB uses the same pgwire protocol as PostgreSQL, so the driver is the same. @@ -333,16 +204,6 @@ ext { 'jdbc.url' : 'jdbc:postgresql://' + dbHost + ':26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0', 'connection.init_sql' : '' ], - cockroachdb_spatial : [ - 'db.dialect' : 'org.hibernate.spatial.dialect.cockroachdb.CockroachDB202SpatialDialect', - // CockroachDB uses the same pgwire protocol as PostgreSQL, so the driver is the same. - 'jdbc.driver': 'org.postgresql.Driver', - 'jdbc.user' : 'root', - 'jdbc.pass' : '', - // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com - 'jdbc.url' : 'jdbc:postgresql://'+ dbHost +'localhost:26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0', - 'connection.init_sql' : '' - ], firebird : [ 'db.dialect' : 'org.hibernate.dialect.FirebirdDialect', 'jdbc.driver': 'org.firebirdsql.jdbc.FBDriver', @@ -362,5 +223,9 @@ def processTestResourcesTask = project.tasks.findByName( 'processTestResources' if ( processTestResourcesTask != null ) { processTestResourcesTask.inputs.property( 'db', db ) processTestResourcesTask.inputs.property( 'dbHost', dbHost ) +// processTestResourcesTask.inputs.property( "gradle.libs.versions.h2", project.getProperty( "gradle.libs.versions.h2", "2.1.214" ) ) +// processTestResourcesTask.inputs.property( "gradle.libs.versions.h2gis", project.getProperty( "gradle.libs.versions.h2gis", "2.1.0" ) ) +// processTestResourcesTask.inputs.property( "gradle.libs.versions.hsqldb", project.getProperty( "gradle.libs.versions.hsqldb", "2.7.1" ) ) +// processTestResourcesTask.inputs.property( "gradle.libs.versions.derby", project.getProperty( "gradle.libs.versions.derby", "10.15.2.0" ) ) processTestResourcesTask.filter( ReplaceTokens, tokens: dbBundle[db] ) } \ No newline at end of file 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 765cb733bc..63d399dc7a 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 @@ -14,6 +14,7 @@ import java.sql.Types; import org.hibernate.LockOptions; import org.hibernate.boot.model.TypeContributions; +import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.OracleDialect; @@ -21,6 +22,8 @@ import org.hibernate.dialect.function.CastingConcatFunction; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CountFunction; import org.hibernate.dialect.function.DB2FormatEmulation; +import org.hibernate.dialect.function.DB2PositionFunction; +import org.hibernate.dialect.function.DB2SubstringFunction; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.DB2LimitHandler; @@ -151,6 +154,11 @@ public class DB2LegacyDialect extends Dialect { return 0; } + @Override + public int getPreferredSqlTypeCodeForBoolean() { + return getDB2Version().isBefore( 11 ) ? Types.SMALLINT : Types.BOOLEAN; + } + @Override protected String columnType(int sqlTypeCode) { switch ( sqlTypeCode ) { @@ -213,6 +221,11 @@ public class DB2LegacyDialect extends Dialect { return 31; } + @Override + protected boolean supportsPredicateAsExpression() { + return getDB2Version().isSameOrAfter( 11 ); + } + @Override public boolean supportsDistinctFromPredicate() { return getDB2Version().isSameOrAfter( 11, 1 ); @@ -240,18 +253,14 @@ public class DB2LegacyDialect extends Dialect { .setInvariantType( queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING ) ) - .setArgumentCountBetween( 2, 4 ) - .setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER, FunctionParameterType.ANY) - .setArgumentListSignature( "(STRING string, INTEGER start[, INTEGER length[, units]])" ) - .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "substring" ) - .setInvariantType( - queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING ) - ) - .setArgumentCountBetween( 2, 4 ) - .setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER, FunctionParameterType.ANY) - .setArgumentListSignature( "(STRING string{ INTEGER from|,} start[{ INTEGER for|,} length[, units]])" ) + .setArgumentCountBetween( 2, 3 ) + .setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER) + .setArgumentListSignature( "(STRING string, INTEGER start[, INTEGER length])" ) .register(); + queryEngine.getSqmFunctionRegistry().register( + "substring", + new DB2SubstringFunction( queryEngine.getTypeConfiguration() ) + ); functionFactory.translate(); functionFactory.bitand(); functionFactory.bitor(); @@ -269,18 +278,38 @@ public class DB2LegacyDialect extends Dialect { functionFactory.octetLength(); functionFactory.ascii(); functionFactory.char_chr(); - functionFactory.position(); functionFactory.trunc(); functionFactory.truncate(); functionFactory.insert(); - functionFactory.overlayCharacterLength_overlay(); - functionFactory.median(); functionFactory.stddev(); - functionFactory.stddevPopSamp(); - functionFactory.varPopSamp(); functionFactory.regrLinearRegressionAggregates(); functionFactory.variance(); - functionFactory.varianceSamp(); + functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); + if ( getDB2Version().isSameOrAfter( 11 ) ) { + functionFactory.position(); + functionFactory.overlayLength_overlay( false ); + functionFactory.median(); + functionFactory.inverseDistributionOrderedSetAggregates(); + functionFactory.stddevPopSamp(); + functionFactory.varPopSamp(); + functionFactory.varianceSamp(); + } + else { + // Before version 11, the position function required the use of the code units + queryEngine.getSqmFunctionRegistry().register( + "position", + new DB2PositionFunction( queryEngine.getTypeConfiguration() ) + ); + // Before version 11, the overlay function required the use of the code units + functionFactory.overlayLength_overlay( true ); + // ordered set aggregate functions are only available as of version 11, and we can't reasonably emulate them + // so no percent_rank, cume_dist, median, mode, percentile_cont or percentile_disc + queryEngine.getSqmFunctionRegistry().registerAlternateKey( "stddev_pop", "stddev" ); + functionFactory.stddevSamp_sumCount(); + queryEngine.getSqmFunctionRegistry().registerAlternateKey( "var_pop", "variance" ); + functionFactory.varSamp_sumCount(); + } + functionFactory.addYearsMonthsDaysHoursMinutesSeconds(); functionFactory.yearsMonthsDaysHoursMinutesSecondsBetween(); functionFactory.dateTrunc(); @@ -335,10 +364,6 @@ public class DB2LegacyDialect extends Dialect { functionFactory.windowFunctions(); if ( getDB2Version().isSameOrAfter( 9, 5 ) ) { functionFactory.listagg( null ); - if ( getDB2Version().isSameOrAfter( 11, 1 ) ) { - functionFactory.inverseDistributionOrderedSetAggregates(); - functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); - } } } @@ -362,50 +387,53 @@ public class DB2LegacyDialect extends Dialect { @Override public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { + if ( getDB2Version().isBefore( 11 ) ) { + return DB2Dialect.timestampdiffPatternV10( unit, fromTemporalType, toTemporalType ); + } StringBuilder pattern = new StringBuilder(); boolean castFrom = fromTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit(); boolean castTo = toTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit(); - switch (unit) { + switch ( unit ) { case NATIVE: case NANOSECOND: - pattern.append("(seconds_between("); + pattern.append( "(seconds_between(" ); break; //note: DB2 does have weeks_between() case MONTH: case QUARTER: // the months_between() function results // in a non-integral value, so trunc() it - pattern.append("trunc(months_between("); + pattern.append( "trunc(months_between(" ); break; default: - pattern.append("?1s_between("); + pattern.append( "?1s_between(" ); } - if (castTo) { - pattern.append("cast(?3 as timestamp)"); + if ( castTo ) { + pattern.append( "cast(?3 as timestamp)" ); } else { - pattern.append("?3"); + pattern.append( "?3" ); } - pattern.append(","); - if (castFrom) { - pattern.append("cast(?2 as timestamp)"); + pattern.append( "," ); + if ( castFrom ) { + pattern.append( "cast(?2 as timestamp)" ); } else { - pattern.append("?2"); + pattern.append( "?2" ); } - pattern.append(")"); - switch (unit) { + pattern.append( ")" ); + switch ( unit ) { case NATIVE: - pattern.append("+(microsecond(?3)-microsecond(?2))/1e6)"); + pattern.append( "+(microsecond(?3)-microsecond(?2))/1e6)" ); break; case NANOSECOND: - pattern.append("*1e9+(microsecond(?3)-microsecond(?2))*1e3)"); + pattern.append( "*1e9+(microsecond(?3)-microsecond(?2))*1e3)" ); break; case MONTH: - pattern.append(")"); + pattern.append( ")" ); break; case QUARTER: - pattern.append("/3)"); + pattern.append( "/3)" ); break; } return pattern.toString(); @@ -692,7 +720,13 @@ public class DB2LegacyDialect extends Dialect { @Override public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { - appender.appendSql( "BX'" ); + if ( getDB2Version().isSameOrAfter( 11 ) ) { + appender.appendSql( "BX'" ); + } + else { + // This should be fine on DB2 prior to 10 + appender.appendSql( "X'" ); + } PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes ); appender.appendSql( '\'' ); } @@ -811,13 +845,18 @@ public class DB2LegacyDialect extends Dialect { @Override public String extractPattern(TemporalUnit unit) { - if ( unit == TemporalUnit.WEEK ) { - // Not sure why, but `extract(week from '2019-05-27')` wrongly returns 21 and week_iso behaves correct - return "week_iso(?2)"; - } - else { - return super.extractPattern( unit ); + switch ( unit ) { + case WEEK: + // Not sure why, but `extract(week from '2019-05-27')` wrongly returns 21 and week_iso behaves correct + return "week_iso(?2)"; + case DAY_OF_YEAR: + return "dayofyear(?2)"; + case DAY_OF_WEEK: + return "dayofweek(?2)"; + case QUARTER: + return "quarter(?2)"; } + return super.extractPattern( unit ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacySqlAstTranslator.java index 32da4db998..6514d46f1d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacySqlAstTranslator.java @@ -30,11 +30,13 @@ import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.insert.InsertStatement; 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.ast.tree.select.SelectStatement; import org.hibernate.sql.ast.tree.update.UpdateStatement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -45,6 +47,9 @@ import org.hibernate.sql.exec.spi.JdbcOperation; */ public class DB2LegacySqlAstTranslator extends AbstractSqlAstTranslator { + // We have to track whether we are in a later query for applying lateral during window emulation + private boolean inLateral; + public DB2LegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { super( sessionFactory, statement ); } @@ -203,24 +208,60 @@ public class DB2LegacySqlAstTranslator extends Abstract } protected boolean shouldEmulateFetchClause(QueryPart queryPart) { - // Percent fetches or ties fetches aren't supported in DB2 - // According to LegacyDB2LimitHandler, variable limit also isn't supported before 11.1 // Check if current query part is already row numbering to avoid infinite recursion - return getQueryPartForRowNumbering() != queryPart && ( - useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart ) - || getDB2Version().isBefore( 11, 1 ) && ( queryPart.isRoot() && hasLimit() || !( queryPart.getFetchClauseExpression() instanceof Literal ) ) - ); + if ( getQueryPartForRowNumbering() == queryPart ) { + return false; + } + // Percent fetches or ties fetches aren't supported in DB2 + if ( useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart ) ) { + return true; + } + // According to LegacyDB2LimitHandler, variable limit also isn't supported before 11.1 + return getDB2Version().isBefore( 11, 1 ) + && queryPart.getFetchClauseExpression() != null + && !( queryPart.getFetchClauseExpression() instanceof Literal ); } protected boolean supportsOffsetClause() { return getDB2Version().isSameOrAfter( 11, 1 ); } + @Override + public void visitQueryPartTableReference(QueryPartTableReference tableReference) { + final boolean oldLateral = inLateral; + inLateral = tableReference.isLateral(); + super.visitQueryPartTableReference( tableReference ); + inLateral = oldLateral; + } + + @Override + public void visitSelectStatement(SelectStatement statement) { + if ( getQueryPartForRowNumbering() == statement.getQueryPart() && inLateral ) { + appendSql( "lateral " ); + } + super.visitSelectStatement( statement ); + } + + @Override + protected void emulateFetchOffsetWithWindowFunctionsVisitQueryPart(QueryPart queryPart) { + if ( inLateral ) { + appendSql( "lateral " ); + final boolean oldLateral = inLateral; + inLateral = false; + super.emulateFetchOffsetWithWindowFunctionsVisitQueryPart( queryPart ); + inLateral = oldLateral; + } + else { + super.emulateFetchOffsetWithWindowFunctionsVisitQueryPart( queryPart ); + } + } + @Override public void visitQueryGroup(QueryGroup queryGroup) { final boolean emulateFetchClause = shouldEmulateFetchClause( queryGroup ); - if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( queryGroup ) ) { - emulateFetchOffsetWithWindowFunctions( queryGroup, emulateFetchClause ); + if ( emulateFetchClause || + getQueryPartForRowNumbering() != queryGroup && !supportsOffsetClause() && hasOffset( queryGroup ) ) { + emulateFetchOffsetWithWindowFunctions( queryGroup, true ); } else { super.visitQueryGroup( queryGroup ); @@ -230,8 +271,9 @@ public class DB2LegacySqlAstTranslator extends Abstract @Override public void visitQuerySpec(QuerySpec querySpec) { final boolean emulateFetchClause = shouldEmulateFetchClause( querySpec ); - if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( querySpec ) ) { - emulateFetchOffsetWithWindowFunctions( querySpec, emulateFetchClause ); + if ( emulateFetchClause || + getQueryPartForRowNumbering() != querySpec && !supportsOffsetClause() && hasOffset( querySpec ) ) { + emulateFetchOffsetWithWindowFunctions( querySpec, true ); } else { super.visitQuerySpec( querySpec ); @@ -253,6 +295,26 @@ public class DB2LegacySqlAstTranslator extends Abstract } } + @Override + protected void renderOffsetExpression(Expression offsetExpression) { + if ( supportsParameterOffsetFetchExpression() ) { + super.renderOffsetExpression( offsetExpression ); + } + else { + renderExpressionAsLiteral( offsetExpression, getJdbcParameterBindings() ); + } + } + + @Override + protected void renderFetchExpression(Expression fetchExpression) { + if ( supportsParameterOffsetFetchExpression() ) { + super.renderFetchExpression( fetchExpression ); + } + else { + renderExpressionAsLiteral( fetchExpression, getJdbcParameterBindings() ); + } + } + @Override protected void visitDeleteStatementOnly(DeleteStatement statement) { final boolean closeWrapper = renderReturningClause( statement ); @@ -376,4 +438,8 @@ public class DB2LegacySqlAstTranslator extends Abstract return this.getDialect().getVersion(); } + protected boolean supportsParameterOffsetFetchExpression() { + return getDB2Version().isSameOrAfter( 11 ); + } + } 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 74101ca54d..fb19840c78 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 @@ -287,6 +287,7 @@ public class H2LegacyDialect extends Dialect { functionFactory.radians(); functionFactory.degrees(); functionFactory.log10(); + functionFactory.mod_operator(); functionFactory.rand(); functionFactory.truncate(); functionFactory.soundex(); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacySqlAstTranslator.java index 122db5d612..d822cad324 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacySqlAstTranslator.java @@ -230,6 +230,12 @@ public class H2LegacySqlAstTranslator extends AbstractS return getDialect().getVersion().isSameOrAfter( 1, 4, 197 ); } + @Override + protected boolean supportsRowValueConstructorDistinctFromSyntax() { + // Seems that before, this was buggy + return getDialect().getVersion().isSameOrAfter( 1, 4, 200 ); + } + @Override protected boolean supportsNullPrecedence() { // Support for nulls clause in listagg was added in 2.0 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 index d9fe9108c1..2c786af554 100644 --- 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 @@ -85,6 +85,25 @@ public class MariaDBLegacySqlAstTranslator extends Abst return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart && supportsWindowFunctions() && !isRowsOnlyFetchClauseType( queryPart ); } + @Override + protected boolean supportsSimpleQueryGrouping() { + return getDialect().getVersion().isSameOrAfter( 10, 4 ); + } + + @Override + protected boolean shouldEmulateLateralWithIntersect(QueryPart queryPart) { + // Intersect emulation requires nested correlation when no simple query grouping is possible + // and the query has an offset/fetch clause, so we have to disable the emulation in this case, + // because nested correlation is not supported though + return supportsSimpleQueryGrouping() || !queryPart.hasOffsetOrFetchClause(); + } + + @Override + protected boolean supportsNestedSubqueryCorrelation() { + // It seems it doesn't support it + return false; + } + @Override public void visitQueryGroup(QueryGroup queryGroup) { if ( shouldEmulateFetchClause( queryGroup ) ) { 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 a368ebce7e..4d0801af9a 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 @@ -170,6 +170,16 @@ public class MySQLLegacySqlAstTranslator extends Abstra return getDialect().getVersion().isSameOrAfter( 8 ); } + @Override + protected boolean supportsSimpleQueryGrouping() { + return getDialect().getVersion().isSameOrAfter( 8 ); + } + + @Override + protected boolean supportsNestedSubqueryCorrelation() { + return false; + } + @Override protected String getFromDual() { return " from dual"; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java index 7705b03802..91f53b6230 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java @@ -200,22 +200,8 @@ public class OracleLegacySqlAstTranslator extends Abstr querySpec, true, // we need select aliases to avoid ORA-00918: column ambiguously defined () -> { - final QueryPart currentQueryPart = getQueryPartStack().getCurrent(); - final boolean needsWrapper; - if ( currentQueryPart instanceof QueryGroup ) { - // visitQuerySpec will add the select wrapper - needsWrapper = !currentQueryPart.hasOffsetOrFetchClause(); - } - else { - needsWrapper = true; - } - if ( needsWrapper ) { - appendSql( "select * from (" ); - } - super.visitQuerySpec( querySpec ); - if ( needsWrapper ) { - appendSql( ')' ); - } + appendSql( "select * from " ); + emulateFetchOffsetWithWindowFunctionsVisitQueryPart( querySpec ); appendSql( " where rownum<=" ); final Stack clauseStack = getClauseStack(); clauseStack.push( Clause.WHERE ); @@ -499,21 +485,4 @@ public class OracleLegacySqlAstTranslator extends Abstr return getDialect().supportsFetchClause( FetchClauseType.ROWS_ONLY ); } - @Override - public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { - final BinaryArithmeticOperator operator = arithmeticExpression.getOperator(); - if ( operator == BinaryArithmeticOperator.MODULO ) { - append( "mod" ); - appendSql( OPEN_PARENTHESIS ); - arithmeticExpression.getLeftHandOperand().accept( this ); - appendSql( ',' ); - arithmeticExpression.getRightHandOperand().accept( this ); - appendSql( CLOSE_PARENTHESIS ); - return; - } - else { - super.visitBinaryArithmeticExpression( arithmeticExpression ); - } - } - } 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 f526810829..6c711fba6d 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 @@ -524,7 +524,8 @@ public class PostgreSQLLegacyDialect extends Dialect { functionFactory.degrees(); functionFactory.trunc(); functionFactory.log(); - if ( getVersion().isSameOrAfter(12) ) { + functionFactory.mod_operator(); + if ( getVersion().isSameOrAfter( 12 ) ) { functionFactory.log10(); functionFactory.tanh(); functionFactory.sinh(); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacySqlAstTranslator.java index 21a3cadb14..dce9efa45f 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacySqlAstTranslator.java @@ -12,6 +12,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Summarization; @@ -212,4 +213,13 @@ public class PostgreSQLLegacySqlAstTranslator extends A } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + } 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 afa43b875a..98bf788b57 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 @@ -44,6 +44,7 @@ import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.sqm.TrimSpec; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; @@ -309,6 +310,31 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect { if ( getVersion().isSameOrAfter( 14 ) ) { functionFactory.listagg_stringAggWithinGroup( "varchar(max)" ); } + if ( getVersion().isSameOrAfter( 16 ) ) { + functionFactory.leastGreatest(); + } + } + + @Override + public String trimPattern(TrimSpec specification, char character) { + if ( getVersion().isSameOrAfter( 16 ) ) { + switch ( specification ) { + case BOTH: + return character == ' ' + ? "trim(?1)" + : "trim('" + character + "' from ?1)"; + case LEADING: + return character == ' ' + ? "ltrim(?1)" + : "ltrim(?1,'" + character + "')"; + case TRAILING: + return character == ' ' + ? "rtrim(?1)" + : "rtrim(?1,'" + character + "')"; + } + throw new UnsupportedOperationException( "Unsupported specification: " + specification ); + } + return super.trimPattern( specification, character ); } @Override @@ -397,6 +423,11 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect { return getVersion().isSameOrAfter( 10 ); } + @Override + public boolean supportsDistinctFromPredicate() { + return getVersion().isSameOrAfter( 16 ); + } + @Override public char closeQuote() { return ']'; @@ -902,7 +933,7 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect { @Override public String[] getDropSchemaCommand(String schemaName) { - if ( getVersion().isSameOrAfter( 16 ) ) { + if ( getVersion().isSameOrAfter( 13 ) ) { return new String[] { "drop schema if exists " + schemaName }; } return super.getDropSchemaCommand( schemaName ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java index f881f2bf05..9fb72030d4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java @@ -20,6 +20,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; @@ -420,6 +421,15 @@ public class SQLServerLegacySqlAstTranslator extends Ab } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + @Override protected boolean supportsRowValueConstructorSyntax() { return false; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java index 2924f0e4b3..9cdba28296 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java @@ -18,6 +18,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.ColumnReference; @@ -325,6 +326,15 @@ public class SybaseASELegacySqlAstTranslator extends Ab } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + @Override public void visitColumnReference(ColumnReference columnReference) { final String dmlTargetTableAlias = getDmlTargetTableAlias(); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereSqlAstTranslator.java index 8d2ebd40fc..9bcc5a05c7 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereSqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereSqlAstTranslator.java @@ -17,6 +17,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.Expression; @@ -186,6 +187,15 @@ public class SybaseAnywhereSqlAstTranslator extends Abs } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + @Override protected boolean supportsRowValueConstructorSyntax() { return false; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacySqlAstTranslator.java index 39e6561859..c4424d5120 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacySqlAstTranslator.java @@ -17,6 +17,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.Expression; @@ -148,6 +149,15 @@ public class SybaseLegacySqlAstTranslator extends Abstr } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + @Override protected boolean supportsRowValueConstructorSyntax() { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index 88144efd81..db56ff8175 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -379,11 +379,15 @@ public class MetadataBuildingProcess { final ClassLoaderService classLoaderService = options.getServiceRegistry().getService( ClassLoaderService.class ); final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration(); + final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry(); final TypeContributions typeContributions = () -> typeConfiguration; // add Dialect contributed types final Dialect dialect = options.getServiceRegistry().getService( JdbcServices.class ).getDialect(); dialect.contributeTypes( typeContributions, options.getServiceRegistry() ); + final JdbcType dialectUuidDescriptor = jdbcTypeRegistry.findDescriptor( SqlTypes.UUID ); + final JdbcType dialectArrayDescriptor = jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY ); + final JdbcType dialectIntervalDescriptor = jdbcTypeRegistry.findDescriptor( SqlTypes.INTERVAL_SECOND ); // add TypeContributor contributed types. for ( TypeContributor contributor : classLoaderService.loadJavaServices( TypeContributor.class ) ) { @@ -391,10 +395,14 @@ public class MetadataBuildingProcess { } // add fallback type descriptors - final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry(); final int preferredSqlTypeCodeForUuid = ConfigurationHelper.getPreferredSqlTypeCodeForUuid( bootstrapContext.getServiceRegistry() ); if ( preferredSqlTypeCodeForUuid != SqlTypes.UUID ) { - jdbcTypeRegistry.addDescriptor( SqlTypes.UUID, jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForUuid ) ); + if ( jdbcTypeRegistry.findDescriptor( SqlTypes.UUID ) == dialectUuidDescriptor ) { + jdbcTypeRegistry.addDescriptor( + SqlTypes.UUID, + jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForUuid ) + ); + } } else { addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.UUID, SqlTypes.BINARY ); @@ -404,7 +412,12 @@ public class MetadataBuildingProcess { final int preferredSqlTypeCodeForArray = ConfigurationHelper.getPreferredSqlTypeCodeForArray( bootstrapContext.getServiceRegistry() ); if ( preferredSqlTypeCodeForArray != SqlTypes.ARRAY ) { - jdbcTypeRegistry.addDescriptor( SqlTypes.ARRAY, jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray ) ); + if ( jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY ) == dialectArrayDescriptor ) { + jdbcTypeRegistry.addDescriptor( + SqlTypes.ARRAY, + jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray ) + ); + } } else if ( jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY ) == null ) { // Fallback to VARBINARY @@ -413,7 +426,12 @@ public class MetadataBuildingProcess { addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY ); final int preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( bootstrapContext.getServiceRegistry() ); if ( preferredSqlTypeCodeForDuration != SqlTypes.INTERVAL_SECOND ) { - jdbcTypeRegistry.addDescriptor( SqlTypes.INTERVAL_SECOND, jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForDuration ) ); + if ( jdbcTypeRegistry.findDescriptor( SqlTypes.INTERVAL_SECOND ) == dialectIntervalDescriptor ) { + jdbcTypeRegistry.addDescriptor( + SqlTypes.INTERVAL_SECOND, + jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForDuration ) + ); + } } else { addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.NUMERIC ); 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 3f888f43a1..6bc6f957aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -12,6 +12,8 @@ import org.hibernate.dialect.function.CastingConcatFunction; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CountFunction; import org.hibernate.dialect.function.DB2FormatEmulation; +import org.hibernate.dialect.function.DB2PositionFunction; +import org.hibernate.dialect.function.DB2SubstringFunction; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.DB2LimitHandler; @@ -193,6 +195,11 @@ public class DB2Dialect extends Dialect { return 31; } + @Override + protected boolean supportsPredicateAsExpression() { + return getDB2Version().isSameOrAfter( 11 ); + } + @Override public boolean supportsDistinctFromPredicate() { return getDB2Version().isSameOrAfter( 11, 1 ); @@ -222,18 +229,14 @@ public class DB2Dialect extends Dialect { .setInvariantType( queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING ) ) - .setArgumentCountBetween( 2, 4 ) - .setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER, FunctionParameterType.ANY) - .setArgumentListSignature( "(STRING string, INTEGER start[, INTEGER length[, units]])" ) - .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "substring" ) - .setInvariantType( - queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING ) - ) - .setArgumentCountBetween( 2, 4 ) - .setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER, FunctionParameterType.ANY) - .setArgumentListSignature( "(STRING string{ INTEGER from|,} start[{ INTEGER for|,} length[, units]])" ) + .setArgumentCountBetween( 2, 3 ) + .setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER) + .setArgumentListSignature( "(STRING string, INTEGER start[, INTEGER length])" ) .register(); + queryEngine.getSqmFunctionRegistry().register( + "substring", + new DB2SubstringFunction( queryEngine.getTypeConfiguration() ) + ); functionFactory.translate(); functionFactory.bitand(); functionFactory.bitor(); @@ -251,18 +254,39 @@ public class DB2Dialect extends Dialect { functionFactory.octetLength(); functionFactory.ascii(); functionFactory.char_chr(); - functionFactory.position(); functionFactory.trunc(); functionFactory.truncate(); functionFactory.insert(); - functionFactory.overlayCharacterLength_overlay(); - functionFactory.median(); + functionFactory.characterLength_length( SqlAstNodeRenderingMode.DEFAULT ); functionFactory.stddev(); - functionFactory.stddevPopSamp(); - functionFactory.varPopSamp(); functionFactory.regrLinearRegressionAggregates(); functionFactory.variance(); - functionFactory.varianceSamp(); + functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); + if ( getDB2Version().isSameOrAfter( 11 ) ) { + functionFactory.position(); + functionFactory.overlayLength_overlay( false ); + functionFactory.median(); + functionFactory.inverseDistributionOrderedSetAggregates(); + functionFactory.stddevPopSamp(); + functionFactory.varPopSamp(); + functionFactory.varianceSamp(); + } + else { + // Before version 11, the position function required the use of the code units + queryEngine.getSqmFunctionRegistry().register( + "position", + new DB2PositionFunction( queryEngine.getTypeConfiguration() ) + ); + // Before version 11, the overlay function required the use of the code units + functionFactory.overlayLength_overlay( true ); + // ordered set aggregate functions are only available as of version 11, and we can't reasonably emulate them + // so no percent_rank, cume_dist, median, mode, percentile_cont or percentile_disc + queryEngine.getSqmFunctionRegistry().registerAlternateKey( "stddev_pop", "stddev" ); + functionFactory.stddevSamp_sumCount(); + queryEngine.getSqmFunctionRegistry().registerAlternateKey( "var_pop", "variance" ); + functionFactory.varSamp_sumCount(); + } + functionFactory.addYearsMonthsDaysHoursMinutesSeconds(); functionFactory.yearsMonthsDaysHoursMinutesSecondsBetween(); functionFactory.dateTrunc(); @@ -316,10 +340,6 @@ public class DB2Dialect extends Dialect { functionFactory.windowFunctions(); functionFactory.listagg( null ); - if ( getDB2Version().isSameOrAfter( 11, 1 ) ) { - functionFactory.inverseDistributionOrderedSetAggregates(); - functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); - } } @Override @@ -342,55 +362,96 @@ public class DB2Dialect extends Dialect { @Override public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { + if ( getDB2Version().isBefore( 11 ) ) { + return timestampdiffPatternV10( unit, fromTemporalType, toTemporalType ); + } StringBuilder pattern = new StringBuilder(); boolean castFrom = fromTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit(); boolean castTo = toTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit(); - switch (unit) { + switch ( unit ) { case NATIVE: case NANOSECOND: - pattern.append("(seconds_between("); + pattern.append( "(seconds_between(" ); break; //note: DB2 does have weeks_between() case MONTH: case QUARTER: // the months_between() function results // in a non-integral value, so trunc() it - pattern.append("trunc(months_between("); + pattern.append( "trunc(months_between(" ); break; default: - pattern.append("?1s_between("); + pattern.append( "?1s_between(" ); } - if (castTo) { - pattern.append("cast(?3 as timestamp)"); + if ( castTo ) { + pattern.append( "cast(?3 as timestamp)" ); } else { - pattern.append("?3"); + pattern.append( "?3" ); } - pattern.append(","); - if (castFrom) { - pattern.append("cast(?2 as timestamp)"); + pattern.append( ',' ); + if ( castFrom ) { + pattern.append( "cast(?2 as timestamp)" ); } else { - pattern.append("?2"); + pattern.append( "?2" ); } - pattern.append(")"); - switch (unit) { + pattern.append( ')' ); + switch ( unit ) { case NATIVE: - pattern.append("+(microsecond(?3)-microsecond(?2))/1e6)"); + pattern.append( "+(microsecond(?3)-microsecond(?2))/1e6)" ); break; case NANOSECOND: - pattern.append("*1e9+(microsecond(?3)-microsecond(?2))*1e3)"); + pattern.append( "*1e9+(microsecond(?3)-microsecond(?2))*1e3)" ); break; case MONTH: - pattern.append(")"); + pattern.append( ')' ); break; case QUARTER: - pattern.append("/3)"); + pattern.append( "/3)" ); break; } return pattern.toString(); } + public static String timestampdiffPatternV10(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { + final boolean castFrom = fromTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit(); + final boolean castTo = toTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit(); + final String fromExpression = castFrom ? "cast(?2 as timestamp)" : "?2"; + final String toExpression = castTo ? "cast(?3 as timestamp)" : "?3"; + switch ( unit ) { + case NATIVE: + return "(select (days(t2)-days(t1))*86400+(midnight_seconds(t2)-midnight_seconds(t1))+(microsecond(t2)-microsecond(t1))/1e6 " + + "from lateral(values(" + fromExpression + ',' + toExpression + ")) as temp(t1,t2))"; + case NANOSECOND: + return "(select (days(t2)-days(t1))*86400+(midnight_seconds(t2)-midnight_seconds(t1))*1e9+(microsecond(t2)-microsecond(t1))*1e3 " + + "from lateral(values(" + fromExpression + ',' + toExpression + ")) as temp(t1,t2))"; + case SECOND: + return "(select (days(t2)-days(t1))*86400+(midnight_seconds(t2)-midnight_seconds(t1)) " + + "from lateral(values(" + fromExpression + ',' + toExpression + ")) as temp(t1,t2))"; + case MINUTE: + return "(select (days(t2)-days(t1))*1440+(midnight_seconds(t2)-midnight_seconds(t1))/60 from " + + "lateral(values(" + fromExpression + ',' + toExpression + ")) as temp(t1,t2))"; + case HOUR: + return "(select (days(t2)-days(t1))*24+(midnight_seconds(t2)-midnight_seconds(t1))/3600 " + + "from lateral(values(" + fromExpression + ',' + toExpression + ")) as temp(t1,t2))"; + case YEAR: + return "(year(" + toExpression + ")-year(" + fromExpression + "))"; + // the months_between() function results + // in a non-integral value, so trunc() it + case MONTH: + return "trunc(months_between(" + toExpression + ',' + fromExpression + "))"; + case QUARTER: + return "trunc(months_between(" + toExpression + ',' + fromExpression + ")/3)"; + case WEEK: + return "int((days" + toExpression + ")-days(" + fromExpression + "))/7)"; + case DAY: + return "(days(" + toExpression + ")-days(" + fromExpression + "))"; + default: + throw new UnsupportedOperationException( "Unsupported unit: " + unit ); + } + } + @Override public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { final StringBuilder pattern = new StringBuilder(); @@ -665,7 +726,13 @@ public class DB2Dialect extends Dialect { @Override public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { - appender.appendSql( "BX'" ); + if ( getDB2Version().isSameOrAfter( 11 ) ) { + appender.appendSql( "BX'" ); + } + else { + // This should be fine on DB2 prior to 10 + appender.appendSql( "X'" ); + } PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes ); appender.appendSql( '\'' ); } @@ -784,13 +851,18 @@ public class DB2Dialect extends Dialect { @Override public String extractPattern(TemporalUnit unit) { - if ( unit == TemporalUnit.WEEK ) { - // Not sure why, but `extract(week from '2019-05-27')` wrongly returns 21 and week_iso behaves correct - return "week_iso(?2)"; - } - else { - return super.extractPattern( unit ); + switch ( unit ) { + case WEEK: + // Not sure why, but `extract(week from '2019-05-27')` wrongly returns 21 and week_iso behaves correct + return "week_iso(?2)"; + case DAY_OF_YEAR: + return "dayofyear(?2)"; + case DAY_OF_WEEK: + return "dayofweek(?2)"; + case QUARTER: + return "quarter(?2)"; } + return super.extractPattern( unit ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java index ed48628536..c2ba8b908e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java @@ -29,11 +29,14 @@ import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.insert.InsertStatement; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; +import org.hibernate.sql.ast.tree.predicate.Predicate; 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.ast.tree.select.SelectStatement; import org.hibernate.sql.ast.tree.update.UpdateStatement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -44,6 +47,9 @@ import org.hibernate.sql.exec.spi.JdbcOperation; */ public class DB2SqlAstTranslator extends AbstractSqlAstTranslator { + // We have to track whether we are in a later query for applying lateral during window emulation + private boolean inLateral; + public DB2SqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { super( sessionFactory, statement ); } @@ -114,7 +120,12 @@ public class DB2SqlAstTranslator extends AbstractSqlAst @Override protected void renderExpressionAsClauseItem(Expression expression) { - expression.accept( this ); + if ( expression instanceof Predicate && getDB2Version().isBefore( 11 ) ) { + super.renderExpressionAsClauseItem( expression ); + } + else { + expression.accept( this ); + } } @Override @@ -202,24 +213,60 @@ public class DB2SqlAstTranslator extends AbstractSqlAst } protected boolean shouldEmulateFetchClause(QueryPart queryPart) { - // Percent fetches or ties fetches aren't supported in DB2 - // According to LegacyDB2LimitHandler, variable limit also isn't supported before 11.1 // Check if current query part is already row numbering to avoid infinite recursion - return getQueryPartForRowNumbering() != queryPart && ( - useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart ) - || getDB2Version().isBefore( 11, 1 ) && ( queryPart.isRoot() && hasLimit() || !( queryPart.getFetchClauseExpression() instanceof Literal ) ) - ); + if ( getQueryPartForRowNumbering() == queryPart ) { + return false; + } + // Percent fetches or ties fetches aren't supported in DB2 + if ( useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart ) ) { + return true; + } + // According to LegacyDB2LimitHandler, variable limit also isn't supported before 11.1 + return getDB2Version().isBefore( 11, 1 ) + && queryPart.getFetchClauseExpression() != null + && !( queryPart.getFetchClauseExpression() instanceof Literal ); } protected boolean supportsOffsetClause() { return getDB2Version().isSameOrAfter( 11, 1 ); } + @Override + public void visitQueryPartTableReference(QueryPartTableReference tableReference) { + final boolean oldLateral = inLateral; + inLateral = tableReference.isLateral(); + super.visitQueryPartTableReference( tableReference ); + inLateral = oldLateral; + } + + @Override + public void visitSelectStatement(SelectStatement statement) { + if ( getQueryPartForRowNumbering() == statement.getQueryPart() && inLateral ) { + appendSql( "lateral " ); + } + super.visitSelectStatement( statement ); + } + + @Override + protected void emulateFetchOffsetWithWindowFunctionsVisitQueryPart(QueryPart queryPart) { + if ( inLateral ) { + appendSql( "lateral " ); + final boolean oldLateral = inLateral; + inLateral = false; + super.emulateFetchOffsetWithWindowFunctionsVisitQueryPart( queryPart ); + inLateral = oldLateral; + } + else { + super.emulateFetchOffsetWithWindowFunctionsVisitQueryPart( queryPart ); + } + } + @Override public void visitQueryGroup(QueryGroup queryGroup) { final boolean emulateFetchClause = shouldEmulateFetchClause( queryGroup ); - if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( queryGroup ) ) { - emulateFetchOffsetWithWindowFunctions( queryGroup, emulateFetchClause ); + if ( emulateFetchClause || + getQueryPartForRowNumbering() != queryGroup && !supportsOffsetClause() && hasOffset( queryGroup ) ) { + emulateFetchOffsetWithWindowFunctions( queryGroup, true ); } else { super.visitQueryGroup( queryGroup ); @@ -229,8 +276,9 @@ public class DB2SqlAstTranslator extends AbstractSqlAst @Override public void visitQuerySpec(QuerySpec querySpec) { final boolean emulateFetchClause = shouldEmulateFetchClause( querySpec ); - if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( querySpec ) ) { - emulateFetchOffsetWithWindowFunctions( querySpec, emulateFetchClause ); + if ( emulateFetchClause || + getQueryPartForRowNumbering() != querySpec && !supportsOffsetClause() && hasOffset( querySpec ) ) { + emulateFetchOffsetWithWindowFunctions( querySpec, true ); } else { super.visitQuerySpec( querySpec ); @@ -252,6 +300,26 @@ public class DB2SqlAstTranslator extends AbstractSqlAst } } + @Override + protected void renderOffsetExpression(Expression offsetExpression) { + if ( supportsParameterOffsetFetchExpression() ) { + super.renderOffsetExpression( offsetExpression ); + } + else { + renderExpressionAsLiteral( offsetExpression, getJdbcParameterBindings() ); + } + } + + @Override + protected void renderFetchExpression(Expression fetchExpression) { + if ( supportsParameterOffsetFetchExpression() ) { + super.renderFetchExpression( fetchExpression ); + } + else { + renderExpressionAsLiteral( fetchExpression, getJdbcParameterBindings() ); + } + } + @Override protected void visitDeleteStatementOnly(DeleteStatement statement) { final boolean closeWrapper = renderReturningClause( statement ); @@ -375,4 +443,8 @@ public class DB2SqlAstTranslator extends AbstractSqlAst return this.getDialect().getVersion(); } + protected boolean supportsParameterOffsetFetchExpression() { + return getDB2Version().isSameOrAfter( 11 ); + } + } 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 de7b6744c6..2b3c1944c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -852,7 +852,7 @@ public abstract class Dialect implements ConversionContext { //supported on one database, but can be emulated using sum() and case, //though there is a more natural mapping on some databases - functionFactory.everyAny_sumCase(); + functionFactory.everyAny_sumCase( supportsPredicateAsExpression() ); //math functions supported on almost every database @@ -3900,6 +3900,14 @@ public abstract class Dialect implements ConversionContext { return true; } + /** + * Whether a predicate like `a > 0` can appear in an expression context e.g. a select item list. + */ + protected boolean supportsPredicateAsExpression() { + // Most databases seem to allow that + return true; + } + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { appender.appendSql( "X'" ); PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes ); 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 1c1637aa1c..831a8396fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -254,6 +254,7 @@ public class H2Dialect extends Dialect { functionFactory.radians(); functionFactory.degrees(); functionFactory.log10(); + functionFactory.mod_operator(); functionFactory.rand(); functionFactory.truncate(); functionFactory.soundex(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2SqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2SqlAstTranslator.java index f420ea26b4..98516b9f1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2SqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2SqlAstTranslator.java @@ -230,6 +230,12 @@ public class H2SqlAstTranslator extends AbstractSqlAstT return true; } + @Override + protected boolean supportsRowValueConstructorDistinctFromSyntax() { + // Seems that before, this was buggy + return getDialect().getVersion().isSameOrAfter( 1, 4, 200 ); + } + @Override protected boolean supportsNullPrecedence() { // Support for nulls clause in listagg was added in 2.0 diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANASqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANASqlAstTranslator.java index 84551fb440..ac68520788 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANASqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANASqlAstTranslator.java @@ -125,26 +125,6 @@ public class HANASqlAstTranslator extends AbstractSqlAs } } - @Override - public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { - final BinaryArithmeticOperator operator = arithmeticExpression.getOperator(); - if ( operator == BinaryArithmeticOperator.MODULO ) { - append( "mod" ); - appendSql( OPEN_PARENTHESIS ); - arithmeticExpression.getLeftHandOperand().accept( this ); - appendSql( ',' ); - arithmeticExpression.getRightHandOperand().accept( this ); - appendSql( CLOSE_PARENTHESIS ); - } - else { - appendSql( OPEN_PARENTHESIS ); - render( arithmeticExpression.getLeftHandOperand(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER ); - appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); - render( arithmeticExpression.getRightHandOperand(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER ); - appendSql( CLOSE_PARENTHESIS ); - } - } - @Override protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() { return false; 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 370c368e86..cde752f5df 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -80,6 +80,25 @@ public class MariaDBSqlAstTranslator extends AbstractSq return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart && supportsWindowFunctions() && !isRowsOnlyFetchClauseType( queryPart ); } + @Override + protected boolean supportsSimpleQueryGrouping() { + return getDialect().getVersion().isSameOrAfter( 10, 4 ); + } + + @Override + protected boolean shouldEmulateLateralWithIntersect(QueryPart queryPart) { + // Intersect emulation requires nested correlation when no simple query grouping is possible + // and the query has an offset/fetch clause, so we have to disable the emulation in this case, + // because nested correlation is not supported though + return supportsSimpleQueryGrouping() || !queryPart.hasOffsetOrFetchClause(); + } + + @Override + protected boolean supportsNestedSubqueryCorrelation() { + // It seems it doesn't support it + return false; + } + @Override public void visitQueryGroup(QueryGroup queryGroup) { if ( shouldEmulateFetchClause( queryGroup ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java index 7b5b70ab0d..90c87fb8d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java @@ -147,6 +147,19 @@ public class OracleSqlAstTranslator extends AbstractSql return getQueryPartStack().findCurrentFirst( part -> part instanceof QueryGroup ? part : null ) != null; } + @Override + protected boolean shouldEmulateLateralWithIntersect(QueryPart queryPart) { + // On Oracle 11 where there is no lateral support, + // make sure we don't use intersect if the query has an offset/fetch clause + return !queryPart.hasOffsetOrFetchClause(); + } + + @Override + protected boolean supportsNestedSubqueryCorrelation() { + // It seems it doesn't support it, at least on version 11 + return false; + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Check if current query part is already row numbering to avoid infinite recursion if (getQueryPartForRowNumbering() == queryPart) { @@ -200,22 +213,8 @@ public class OracleSqlAstTranslator extends AbstractSql querySpec, true, // we need select aliases to avoid ORA-00918: column ambiguously defined () -> { - final QueryPart currentQueryPart = getQueryPartStack().getCurrent(); - final boolean needsWrapper; - if ( currentQueryPart instanceof QueryGroup ) { - // visitQuerySpec will add the select wrapper - needsWrapper = !currentQueryPart.hasOffsetOrFetchClause(); - } - else { - needsWrapper = true; - } - if ( needsWrapper ) { - appendSql( "select * from (" ); - } - super.visitQuerySpec( querySpec ); - if ( needsWrapper ) { - appendSql( ')' ); - } + appendSql( "select * from " ); + emulateFetchOffsetWithWindowFunctionsVisitQueryPart( querySpec ); appendSql( " where rownum<=" ); final Stack clauseStack = getClauseStack(); clauseStack.push( Clause.WHERE ); @@ -488,21 +487,4 @@ public class OracleSqlAstTranslator extends AbstractSql return getDialect().supportsFetchClause( FetchClauseType.ROWS_ONLY ); } - @Override - public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { - final BinaryArithmeticOperator operator = arithmeticExpression.getOperator(); - if ( operator == BinaryArithmeticOperator.MODULO ) { - append( "mod" ); - appendSql( OPEN_PARENTHESIS ); - arithmeticExpression.getLeftHandOperand().accept( this ); - appendSql( ',' ); - arithmeticExpression.getRightHandOperand().accept( this ); - appendSql( CLOSE_PARENTHESIS ); - return; - } - else { - super.visitBinaryArithmeticExpression( arithmeticExpression ); - } - } - } 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 944a8f2b4b..005819b1a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -508,7 +508,8 @@ public class PostgreSQLDialect extends Dialect { functionFactory.degrees(); functionFactory.trunc(); functionFactory.log(); - if ( getVersion().isSameOrAfter(12) ) { + functionFactory.mod_operator(); + if ( getVersion().isSameOrAfter( 12 ) ) { functionFactory.log10(); functionFactory.tanh(); functionFactory.sinh(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java index d25a85e89f..6f9945694d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java @@ -12,6 +12,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Summarization; @@ -212,4 +213,13 @@ public class PostgreSQLSqlAstTranslator extends Abstrac } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2016Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2016Dialect.java index dae8a85be4..386f42e118 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2016Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2016Dialect.java @@ -14,7 +14,7 @@ package org.hibernate.dialect; public class SQLServer2016Dialect extends SQLServerDialect { public SQLServer2016Dialect() { - super( DatabaseVersion.make( 16 ) ); + super( DatabaseVersion.make( 13 ) ); } @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 88d5b1d73e..524a4c5ca3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -38,6 +38,7 @@ import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.sqm.TrimSpec; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; @@ -327,6 +328,31 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { if ( getVersion().isSameOrAfter( 14 ) ) { functionFactory.listagg_stringAggWithinGroup( "varchar(max)" ); } + if ( getVersion().isSameOrAfter( 16 ) ) { + functionFactory.leastGreatest(); + } + } + + @Override + public String trimPattern(TrimSpec specification, char character) { + if ( getVersion().isSameOrAfter( 16 ) ) { + switch ( specification ) { + case BOTH: + return character == ' ' + ? "trim(?1)" + : "trim('" + character + "' from ?1)"; + case LEADING: + return character == ' ' + ? "ltrim(?1)" + : "ltrim(?1,'" + character + "')"; + case TRAILING: + return character == ' ' + ? "rtrim(?1)" + : "rtrim(?1,'" + character + "')"; + } + throw new UnsupportedOperationException( "Unsupported specification: " + specification ); + } + return super.trimPattern( specification, character ); } @Override @@ -412,6 +438,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { return true; } + @Override + public boolean supportsDistinctFromPredicate() { + return getVersion().isSameOrAfter( 16 ); + } + @Override public char closeQuote() { return ']'; @@ -900,7 +931,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { @Override public String[] getDropSchemaCommand(String schemaName) { - if ( getVersion().isSameOrAfter( 16 ) ) { + if ( getVersion().isSameOrAfter( 13 ) ) { return new String[] { "drop schema if exists " + schemaName }; } return super.getDropSchemaCommand( schemaName ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java index cb990bcf70..fa94a2fa72 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java @@ -10,6 +10,7 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.sqm.ComparisonOperator; @@ -19,6 +20,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; @@ -396,6 +398,15 @@ public class SQLServerSqlAstTranslator extends Abstract } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + @Override protected boolean supportsRowValueConstructorSyntax() { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java index d877bcde8b..83ecde4ee6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java @@ -18,6 +18,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.ColumnReference; @@ -311,6 +312,15 @@ public class SybaseASESqlAstTranslator extends Abstract } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + @Override public void visitColumnReference(ColumnReference columnReference) { final String dmlTargetTableAlias = getDmlTargetTableAlias(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java index 587a194a87..faa7770657 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java @@ -17,6 +17,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.Expression; @@ -148,6 +149,15 @@ public class SybaseSqlAstTranslator extends AbstractSql } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + @Override protected boolean supportsRowValueConstructorSyntax() { return false; 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 bbb9fe70d6..4015681512 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 @@ -435,9 +435,33 @@ public class CommonFunctionFactory { */ public void varianceSamp() { functionRegistry.namedAggregateDescriptorBuilder( "variance_samp" ) - .setInvariantType(doubleType) + .setInvariantType( doubleType ) .setExactArgumentCount( 1 ) - .setParameterTypes(NUMERIC) + .setParameterTypes( NUMERIC ) + .register(); + } + + private static final String VAR_SAMP_SUM_COUNT_PATTERN = "(sum(power(?1,2))-(power(sum(?1),2)/count(?1)))/nullif(count(?1)-1,0)"; + + /** + * DB2 before 11 + */ + public void varSamp_sumCount() { + functionRegistry.patternAggregateDescriptorBuilder( "var_samp", VAR_SAMP_SUM_COUNT_PATTERN ) + .setInvariantType( doubleType ) + .setExactArgumentCount( 1 ) + .setParameterTypes( NUMERIC ) + .register(); + } + + /** + * DB2 before 11 + */ + public void stddevSamp_sumCount() { + functionRegistry.patternAggregateDescriptorBuilder( "stddev_samp", "sqrt(" + VAR_SAMP_SUM_COUNT_PATTERN + ")" ) + .setInvariantType( doubleType ) + .setExactArgumentCount( 1 ) + .setParameterTypes( NUMERIC ) .register(); } @@ -933,11 +957,11 @@ public class CommonFunctionFactory { * for databases that have to emulate the boolean * aggregation functions using sum() and case. */ - public void everyAny_sumCase() { + public void everyAny_sumCase(boolean supportsPredicateAsExpression) { functionRegistry.register( "every", - new EveryAnyEmulation( typeConfiguration, true ) ); + new EveryAnyEmulation( typeConfiguration, true, supportsPredicateAsExpression ) ); functionRegistry.register( "any", - new EveryAnyEmulation( typeConfiguration, false ) ); + new EveryAnyEmulation( typeConfiguration, false, supportsPredicateAsExpression ) ); } /** @@ -1691,14 +1715,13 @@ public class CommonFunctionFactory { /** * For DB2 which has a broken implementation of overlay() */ - public void overlayCharacterLength_overlay() { + public void overlayLength_overlay(boolean withCodeUnits) { + final String codeUnits = withCodeUnits ? " using codeunits32" : ""; functionRegistry.registerTernaryQuaternaryPattern( "overlay", stringType, - //use character_length() here instead of length() - //because DB2 doesn't like "length(?)" - "overlay(?1 placing ?2 from ?3 for character_length(?2))", - "overlay(?1 placing ?2 from ?3 for ?4)", + "overlay(?1 placing ?2 from ?3 for character_length(?2" + (withCodeUnits ? ",codeunits32" : "") + ")" + codeUnits + ")", + "overlay(?1 placing ?2 from ?3 for ?4" + codeUnits + ")", STRING, STRING, INTEGER, INTEGER, typeConfiguration ) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2PositionFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2PositionFunction.java new file mode 100644 index 0000000000..3347b2d3ba --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2PositionFunction.java @@ -0,0 +1,62 @@ +/* + * 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.function; + +import java.util.List; + +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.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.spi.TypeConfiguration; + +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; + +/** + * DB2's position() function always requires a code unit before version 11. + */ +public class DB2PositionFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public DB2PositionFunction(TypeConfiguration typeConfiguration) { + super( + "position", + new ArgumentTypesValidator( StandardArgumentsValidators.between( 2, 3 ), STRING, STRING, ANY ), + StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.getBasicTypeRegistry().resolve( + StandardBasicTypes.INTEGER ) ), + StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, STRING, STRING ) + ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + SqlAstTranslator walker) { + final int argumentCount = arguments.size(); + sqlAppender.appendSql( "position(" ); + arguments.get( 0 ).accept( walker ); + for ( int i = 1; i < argumentCount; i++ ) { + sqlAppender.appendSql( ',' ); + arguments.get( i ).accept( walker ); + } + if ( argumentCount != 3 ) { + sqlAppender.appendSql( ",codeunits32" ); + } + sqlAppender.appendSql( ')' ); + } + + @Override + public String getSignature(String name) { + return "(STRING pattern in STRING string[, units]])"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java new file mode 100644 index 0000000000..029c7b0d6b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java @@ -0,0 +1,64 @@ +/* + * 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.function; + +import java.util.List; + +import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; +import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; +import org.hibernate.query.sqm.produce.function.FunctionParameterType; +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.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.spi.TypeConfiguration; + +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; + +/** + * DB2's substring() function requires a code unit and substr() can't optionally take it, + * so we render substr() by default. If the code unit is passed, we render substring(). + */ +public class DB2SubstringFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public DB2SubstringFunction(TypeConfiguration typeConfiguration) { + super( + "substring", + new ArgumentTypesValidator( StandardArgumentsValidators.between( 2, 4 ), STRING, INTEGER, INTEGER, FunctionParameterType.ANY ), + StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.getBasicTypeRegistry().resolve( + StandardBasicTypes.STRING ) ), + StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, STRING, INTEGER, INTEGER ) + ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + SqlAstTranslator walker) { + final int argumentCount = arguments.size(); + sqlAppender.appendSql( "substring(" ); + arguments.get( 0 ).accept( walker ); + for ( int i = 1; i < argumentCount; i++ ) { + sqlAppender.appendSql( ',' ); + arguments.get( i ).accept( walker ); + } + if ( argumentCount != 4 ) { + sqlAppender.appendSql( ",codeunits32" ); + } + sqlAppender.appendSql( ')' ); + } + + @Override + public String getSignature(String name) { + return "(STRING string, INTEGER start[, INTEGER length[, units]])"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/EveryAnyEmulation.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/EveryAnyEmulation.java index 20070cc991..fc8cc1c26f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/EveryAnyEmulation.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/EveryAnyEmulation.java @@ -18,7 +18,9 @@ import org.hibernate.sql.ast.Clause; 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.QueryLiteral; import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.type.BasicType; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.spi.TypeConfiguration; @@ -37,8 +39,10 @@ import static org.hibernate.query.sqm.produce.function.FunctionParameterType.BOO public class EveryAnyEmulation extends AbstractSqmSelfRenderingFunctionDescriptor { private final boolean every; + private final QueryLiteral trueLiteral; + private final QueryLiteral falseLiteral; - public EveryAnyEmulation(TypeConfiguration typeConfiguration, boolean every) { + public EveryAnyEmulation(TypeConfiguration typeConfiguration, boolean every, boolean supportsPredicateAsExpression) { super( every ? "every" : "any", FunctionKind.AGGREGATE, @@ -49,6 +53,16 @@ public class EveryAnyEmulation extends AbstractSqmSelfRenderingFunctionDescripto StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, BOOLEAN ) ); this.every = every; + if ( supportsPredicateAsExpression ) { + this.trueLiteral = null; + this.falseLiteral = null; + } + else { + final BasicType booleanBasicType = typeConfiguration.getBasicTypeRegistry() + .resolve( StandardBasicTypes.BOOLEAN ); + this.trueLiteral = new QueryLiteral<>( true, booleanBasicType ); + this.falseLiteral = new QueryLiteral<>( false, booleanBasicType ); + } } @Override @@ -57,6 +71,9 @@ public class EveryAnyEmulation extends AbstractSqmSelfRenderingFunctionDescripto List sqlAstArguments, Predicate filter, SqlAstTranslator walker) { + if ( trueLiteral != null ) { + sqlAppender.appendSql( "case when " ); + } sqlAppender.appendSql( "(sum(case when " ); if ( filter != null ) { walker.getCurrentClauseStack().push( Clause.WHERE ); @@ -80,6 +97,13 @@ public class EveryAnyEmulation extends AbstractSqmSelfRenderingFunctionDescripto sqlAppender.appendSql( " then 1 else 0 end)>0)" ); } } + if ( trueLiteral != null ) { + sqlAppender.appendSql( " then " ); + walker.visitQueryLiteral( trueLiteral ); + sqlAppender.appendSql( " else " ); + walker.visitQueryLiteral( falseLiteral ); + sqlAppender.appendSql( " end" ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/OrderingSpecification.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/OrderingSpecification.java index c7df33eec0..71df1f41d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/OrderingSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/OrderingSpecification.java @@ -18,7 +18,7 @@ public class OrderingSpecification implements Node { private final OrderingExpression orderingExpression; private String collation; - private SortOrder sortOrder; + private SortOrder sortOrder = SortOrder.ASCENDING; private NullPrecedence nullPrecedence = NullPrecedence.NONE; private String orderByValue; diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 88aa42662d..7364d791e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -1598,7 +1598,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem nextIndex++; } else { - sortOrder = null; + sortOrder = SortOrder.ASCENDING; } parseTree = ctx.getChild( nextIndex ); if ( parseTree instanceof HqlParser.NullsPrecedenceContext ) { @@ -1614,12 +1614,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem } } else { - nullPrecedence = null; + nullPrecedence = NullPrecedence.NONE; } } else { - sortOrder = null; - nullPrecedence = null; + sortOrder = SortOrder.ASCENDING; + nullPrecedence = NullPrecedence.NONE; } return new SqmSortSpecification( sortExpression, sortOrder, nullPrecedence ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java index 001169e5c2..53f86e81a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java @@ -187,7 +187,7 @@ public class ArgumentTypesValidator implements ArgumentsValidator { case BOOLEAN: // ugh, need to be careful here, need to accept all the // JDBC type codes that a Dialect might use for BOOLEAN - if ( code != BOOLEAN && code != BIT && code != TINYINT ) { + if ( code != BOOLEAN && code != BIT && code != TINYINT && code != SMALLINT ) { throwError(type, javaType, functionName, count); } break; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java index ee32d1644d..ebad20c17f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java @@ -26,17 +26,20 @@ public class SqmSortSpecification implements JpaOrder { SqmExpression sortExpression, SortOrder sortOrder, NullPrecedence nullPrecedence) { + assert sortExpression != null; + assert sortOrder != null; + assert nullPrecedence != null; this.sortExpression = sortExpression; this.sortOrder = sortOrder; this.nullPrecedence = nullPrecedence; } public SqmSortSpecification(SqmExpression sortExpression) { - this( sortExpression, SortOrder.ASCENDING, null ); + this( sortExpression, SortOrder.ASCENDING, NullPrecedence.NONE ); } public SqmSortSpecification(SqmExpression sortExpression, SortOrder sortOrder) { - this( sortExpression, sortOrder, null ); + this( sortExpression, sortOrder, NullPrecedence.NONE ); } public SqmSortSpecification copy(SqmCopyContext context) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 39c8f7601c..40732311ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -2846,7 +2846,7 @@ public abstract class AbstractSqlAstTranslator implemen needsParenthesis = !needsRowNumberingWrapper && !needsQueryGroupWrapper; } else { - needsParenthesis = !queryGroup.isRoot(); + needsParenthesis = queryGroup.hasOffsetOrFetchClause() && !queryGroup.isRoot(); } if ( needsParenthesis ) { appendSql( OPEN_PARENTHESIS ); @@ -2952,9 +2952,12 @@ public abstract class AbstractSqlAstTranslator implemen // because of order by precedence in SQL if ( querySpec.hasOffsetOrFetchClause() ) { queryGroupAlias = ""; - // If the parent is a query group with a fetch clause, + // If the parent is a query group with a fetch clause we must use a select wrapper, // or if the database does not support simple query grouping, we must use a select wrapper - if ( !supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() ) { + if ( ( !supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() ) + // We can skip it though if this query spec is being row numbered, + // because then we already have a wrapper + && queryPartForRowNumbering != querySpec ) { queryGroupAlias = " grp_" + queryGroupAliasCounter + '_'; queryGroupAliasCounter++; appendSql( "select" ); @@ -4326,11 +4329,19 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( OPEN_PARENTHESIS ); } appendSql( "select " ); - if ( getClauseStack().isEmpty() && !( getStatement() instanceof InsertStatement ) ) { + // When we emulate a root statement, we don't need to select the select items + // to filter out the row number column we introduce, because we will simply ignore it anyway + if ( getClauseStack().isEmpty() && !( getStatement() instanceof InsertStatement ) + // If the query part is a child of a query group, we have can't do that, + // since we need the select items to properly align in query group parts + && !( getCurrentQueryPart() instanceof QueryGroup ) ) { appendSql( '*' ); } else { - final int size = queryPart.getFirstQuerySpec().getSelectClause().getSqlSelections().size(); + int size = 0; + for ( SqlSelection sqlSelection : queryPart.getFirstQuerySpec().getSelectClause().getSqlSelections() ) { + size += sqlSelection.getExpressionType().getJdbcTypeCount(); + } String separator = ""; for ( int i = 0; i < size; i++ ) { appendSql( separator ); @@ -4341,9 +4352,7 @@ public abstract class AbstractSqlAstTranslator implemen } } appendSql( " from " ); - appendSql( OPEN_PARENTHESIS ); - queryPart.accept( this ); - appendSql( CLOSE_PARENTHESIS ); + emulateFetchOffsetWithWindowFunctionsVisitQueryPart( queryPart ); appendSql( WHITESPACE ); appendSql( alias ); appendSql( " where " ); @@ -4464,6 +4473,12 @@ public abstract class AbstractSqlAstTranslator implemen } } + protected void emulateFetchOffsetWithWindowFunctionsVisitQueryPart(QueryPart queryPart) { + appendSql( OPEN_PARENTHESIS ); + queryPart.accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + protected final void withRowNumbering(QueryPart queryPart, boolean needsSelectAliases, Runnable r) { final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering; final int queryPartForRowNumberingClauseDepth = this.queryPartForRowNumberingClauseDepth; @@ -4527,46 +4542,56 @@ public abstract class AbstractSqlAstTranslator implemen } if ( needsSelectAliases || referenceStrategy == SelectItemReferenceStrategy.ALIAS && hasSelectAliasInGroupByClause() ) { String separator = NO_SEPARATOR; - if ( columnAliases == null ) { - for ( int i = 0; i < size; i++ ) { - final SqlSelection sqlSelection = sqlSelections.get( i ); - appendSql( separator ); - if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { - parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; - } - else { - parameterRenderingMode = defaultRenderingMode; - } - visitSqlSelection( sqlSelection ); - parameterRenderingMode = original; - appendSql( " c" ); - appendSql( i ); - separator = COMA_SEPARATOR; + int offset = 0; + for ( int i = 0; i < size; i++ ) { + final SqlSelection sqlSelection = sqlSelections.get( i ); + if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { + parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; } - } - else { - int offset = 0; - for ( int i = 0; i < size; i++ ) { - final SqlSelection sqlSelection = sqlSelections.get( i ); + else { + parameterRenderingMode = defaultRenderingMode; + } + final Expression expression = sqlSelection.getExpression(); + final SqlTuple sqlTuple = SqlTupleContainer.getSqlTuple( expression ); + if ( sqlTuple != null ) { + final List expressions = sqlTuple.getExpressions(); + for ( Expression e : expressions ) { + appendSql( separator ); + renderSelectExpression( e ); + appendSql( WHITESPACE ); + if ( columnAliases == null ) { + appendSql( 'c' ); + appendSql( offset ); + } + else { + appendSql( columnAliases.get( offset ) ); + } + offset++; + separator = COMA_SEPARATOR; + } + } + else { appendSql( separator ); - if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { - parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; - } - else { - parameterRenderingMode = defaultRenderingMode; - } - offset += visitSqlSelectExpression( sqlSelection.getExpression(), offset, columnAliases ); - parameterRenderingMode = original; + renderSelectExpression( expression ); appendSql( WHITESPACE ); - appendSql( columnAliases.get( offset - 1 ) ); + if ( columnAliases == null ) { + appendSql( 'c' ); + appendSql( offset ); + } + else { + appendSql( columnAliases.get( offset ) ); + } + offset++; separator = COMA_SEPARATOR; } + parameterRenderingMode = original; } if ( queryPartForRowNumbering != null ) { renderRowNumberingSelectItems( selectClause, queryPartForRowNumbering ); } } - else if ( columnAliases == null ) { + else { + assert columnAliases == null; String separator = NO_SEPARATOR; for ( int i = 0; i < size; i++ ) { final SqlSelection sqlSelection = sqlSelections.get( i ); @@ -4582,25 +4607,6 @@ public abstract class AbstractSqlAstTranslator implemen separator = COMA_SEPARATOR; } } - else { - String separator = NO_SEPARATOR; - int offset = 0; - for ( int i = 0; i < size; i++ ) { - final SqlSelection sqlSelection = sqlSelections.get( i ); - appendSql( separator ); - if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { - parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; - } - else { - parameterRenderingMode = defaultRenderingMode; - } - offset += visitSqlSelectExpression( sqlSelection.getExpression(), offset, columnAliases ); - appendSql( WHITESPACE ); - appendSql( columnAliases.get( offset - 1 ) ); - parameterRenderingMode = original; - separator = COMA_SEPARATOR; - } - } } protected void renderVirtualSelections(SelectClause selectClause) { @@ -4953,32 +4959,6 @@ public abstract class AbstractSqlAstTranslator implemen } } - protected int visitSqlSelectExpression(Expression expression, int offset, List columnAliases) { - final SqlTuple sqlTuple = SqlTupleContainer.getSqlTuple( expression ); - if ( sqlTuple != null ) { - boolean isFirst = true; - final List expressions = sqlTuple.getExpressions(); - int i = 0; - for ( ; i < expressions.size(); i++ ) { - Expression e = expressions.get( i ); - if ( isFirst ) { - isFirst = false; - } - else { - appendSql( WHITESPACE ); - appendSql( columnAliases.get( offset + i - 1 ) ); - appendSql( ',' ); - } - renderSelectExpression( e ); - } - return i; - } - else { - renderSelectExpression( expression ); - return 1; - } - } - protected void renderSelectExpression(Expression expression) { renderExpressionAsClauseItem( expression ); } @@ -5394,7 +5374,9 @@ public abstract class AbstractSqlAstTranslator implemen } protected void emulateQueryPartTableReferenceColumnAliasing(QueryPartTableReference tableReference) { + final boolean needsSelectAliases = this.needsSelectAliases; final List columnAliases = this.columnAliases; + this.needsSelectAliases = true; this.columnAliases = tableReference.getColumnNames(); if ( tableReference.getQueryPart().isRoot() ) { appendSql( OPEN_PARENTHESIS ); @@ -5404,6 +5386,7 @@ public abstract class AbstractSqlAstTranslator implemen else { tableReference.getStatement().accept( this ); } + this.needsSelectAliases = needsSelectAliases; this.columnAliases = columnAliases; renderTableReferenceIdentificationVariable( tableReference ); } @@ -5590,7 +5573,7 @@ public abstract class AbstractSqlAstTranslator implemen // The following optimization only makes sense if the necessary features are supported natively if ( ( columnReferences.size() == 1 || supportsRowValueConstructorSyntax() ) - && supportsDistinctFromPredicate() ) { + && supportsRowValueConstructorDistinctFromSyntax() ) { // Special case for limit 1 sub-queries to avoid double nested sub-query // ... x(c) on x.c is not distinct from (... fetch first 1 rows only) if ( isFetchFirstRowOnly( statement.getQueryPart() ) ) { @@ -5604,7 +5587,7 @@ public abstract class AbstractSqlAstTranslator implemen // Render with exists intersect sub-query if possible as that is shorter and more efficient // ... x(c) on exists(select x.c intersect ...) - if ( supportsIntersect() ) { + if ( shouldEmulateLateralWithIntersect( statement.getQueryPart() ) ) { final QuerySpec lhsReferencesQuery = new QuerySpec( false ); for ( ColumnReference columnReference : columnReferences ) { lhsReferencesQuery.getSelectClause().addSqlSelection( @@ -5698,7 +5681,14 @@ public abstract class AbstractSqlAstTranslator implemen final List subExpressions = new ArrayList<>( columnNames.size() ); for ( SqlSelection sqlSelection : querySpec.getSelectClause().getSqlSelections() ) { - subExpressions.add( sqlSelection.getExpression() ); + final Expression selectionExpression = sqlSelection.getExpression(); + final SqlTuple sqlTuple = SqlTupleContainer.getSqlTuple( selectionExpression ); + if ( sqlTuple == null ) { + subExpressions.add( selectionExpression ); + } + else { + subExpressions.addAll( sqlTuple.getExpressions() ); + } } final QuerySpec existsQuery = new QuerySpec( false, querySpec.getFromClause().getRoots().size() ); existsQuery.getFromClause().getRoots().addAll( querySpec.getFromClause().getRoots() ); @@ -5804,11 +5794,11 @@ public abstract class AbstractSqlAstTranslator implemen ); } final ComparisonOperator comparisonOperator; - if ( sortSpecification.getSortOrder() == SortOrder.ASCENDING ) { - comparisonOperator = ComparisonOperator.LESS_THAN_OR_EQUAL; + if ( sortSpecification.getSortOrder() == SortOrder.DESCENDING ) { + comparisonOperator = ComparisonOperator.GREATER_THAN_OR_EQUAL; } else { - comparisonOperator = ComparisonOperator.GREATER_THAN_OR_EQUAL; + comparisonOperator = ComparisonOperator.LESS_THAN_OR_EQUAL; } countQuery.applyPredicate( new Junction( @@ -5868,6 +5858,10 @@ public abstract class AbstractSqlAstTranslator implemen return null; } + protected boolean shouldEmulateLateralWithIntersect(QueryPart queryPart) { + return supportsIntersect(); + } + private boolean isNullsFirst(SortSpecification sortSpecification) { NullPrecedence nullPrecedence = sortSpecification.getNullPrecedence(); if ( nullPrecedence == null || nullPrecedence == NullPrecedence.NONE ) { @@ -5966,7 +5960,7 @@ public abstract class AbstractSqlAstTranslator implemen private boolean needsLateralSortExpressionVirtualSelections(QuerySpec querySpec) { return !( ( querySpec.getSelectClause().getSqlSelections().size() == 1 || supportsRowValueConstructorSyntax() ) && supportsDistinctFromPredicate() && isFetchFirstRowOnly( querySpec ) ) - && !supportsIntersect() + && !shouldEmulateLateralWithIntersect( querySpec ) && !supportsNestedSubqueryCorrelation() && querySpec.hasOffsetOrFetchClause(); } @@ -6273,11 +6267,22 @@ public abstract class AbstractSqlAstTranslator implemen @Override public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { - appendSql( OPEN_PARENTHESIS ); - arithmeticExpression.getLeftHandOperand().accept( this ); - appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); - arithmeticExpression.getRightHandOperand().accept( this ); - appendSql( CLOSE_PARENTHESIS ); + final BinaryArithmeticOperator operator = arithmeticExpression.getOperator(); + if ( operator == BinaryArithmeticOperator.MODULO ) { + append( "mod" ); + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( ',' ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + else { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } } @Override @@ -7285,6 +7290,9 @@ public abstract class AbstractSqlAstTranslator implemen case GREATER_THAN: case GREATER_THAN_OR_EQUAL: return !supportsRowValueConstructorGtLtSyntax(); + case DISTINCT_FROM: + case NOT_DISTINCT_FROM: + return !supportsRowValueConstructorDistinctFromSyntax(); } return false; } @@ -7342,6 +7350,21 @@ public abstract class AbstractSqlAstTranslator implemen return supportsRowValueConstructorSyntax(); } + /** + * Is this dialect known to support what ANSI-SQL terms "row value + * constructor" syntax; sometimes called tuple syntax with is distinct from + * and is not distinct from operators. + *

+ * Basically, does it support syntax like + * "... where (FIRST_NAME, LAST_NAME) is distinct from ('Steve', 'Ebersole') ...". + * + * @return True if this SQL dialect is known to support "row value + * constructor" syntax with distinct from comparison operators; false otherwise. + */ + protected boolean supportsRowValueConstructorDistinctFromSyntax() { + return supportsRowValueConstructorSyntax() && supportsDistinctFromPredicate(); + } + /** * Is this dialect known to support what ANSI-SQL terms "row value constructor" syntax, * sometimes called tuple syntax, in the SET clause; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java index 29b2d03be0..cabd16543f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java @@ -25,6 +25,9 @@ public class SortSpecification implements SqlAstNode { } public SortSpecification(Expression sortExpression, SortOrder sortOrder, NullPrecedence nullPrecedence) { + assert sortExpression != null; + assert sortOrder != null; + assert nullPrecedence != null; this.sortExpression = sortExpression; this.sortOrder = sortOrder; this.nullPrecedence = nullPrecedence; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytoone/NotNullManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytoone/NotNullManyToOneTest.java index e298adc4e5..20f3800674 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytoone/NotNullManyToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytoone/NotNullManyToOneTest.java @@ -12,6 +12,8 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.beanvalidation.ValidationMode; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -20,6 +22,7 @@ import org.junit.Test; * @author Andrea Boriero */ @TestForIssue(jiraKey = "HHH-13959") +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class NotNullManyToOneTest extends BaseCoreFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/refcolnames/misc/Misc3Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/refcolnames/misc/Misc3Test.java index 6d44a651b8..89d174d54c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/refcolnames/misc/Misc3Test.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/refcolnames/misc/Misc3Test.java @@ -32,7 +32,7 @@ public class Misc3Test { @Table(name = "A") public static final class A { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue private Long id; @Basic private String name; @@ -42,7 +42,7 @@ public class Misc3Test { @Table(name = "B", uniqueConstraints = {@UniqueConstraint(columnNames = {"a_id", "uniqueName"})}) public static final class B { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue private Long id; @Basic @@ -56,7 +56,7 @@ public class Misc3Test { @Table(name = "C", uniqueConstraints = {@UniqueConstraint(columnNames = {"a_id", "uniqueName"})}) public static final class C { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue private Long id; @Basic diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/uniqueconstraint/MySQLDropConstraintThrowsExceptionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/uniqueconstraint/MySQLDropConstraintThrowsExceptionTest.java index 3969f121d9..a9ed55994a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/uniqueconstraint/MySQLDropConstraintThrowsExceptionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/uniqueconstraint/MySQLDropConstraintThrowsExceptionTest.java @@ -121,7 +121,7 @@ public class MySQLDropConstraintThrowsExceptionTest { .filter( sql -> sql.toLowerCase().contains( "alter " ) ).map( String::trim ).collect( Collectors.toList() ); - if ( metadata.getDatabase().getDialect() instanceof MariaDBDialect ) { + if ( metadata.getDatabase().getDialect().supportsIfExistsAfterAlterTable() ) { assertTrue( alterStatements.get( 0 ).matches( "alter table if exists CUSTOMER\\s+drop index .*?" ) ); assertTrue( alterStatements.get( 1 ) .matches( "alter table if exists CUSTOMER\\s+add constraint .*? unique \\(CUSTOMER_ID\\)" ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java index 97030939b8..418bc6430b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java @@ -14,6 +14,8 @@ import org.hibernate.boot.jaxb.spi.Binding; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.cfg.Configuration; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -21,6 +23,7 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; @TestForIssue(jiraKey = {"HHH-14530", "HHH-14529"}) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class PreParsedOrmXmlTest extends BaseCoreFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/hbm/PreParsedHbmXmlTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/hbm/PreParsedHbmXmlTest.java index b5a84967e1..afc27b8112 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/hbm/PreParsedHbmXmlTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/xml/hbm/PreParsedHbmXmlTest.java @@ -13,6 +13,8 @@ import java.io.UncheckedIOException; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest; import org.junit.jupiter.api.Test; @@ -20,6 +22,7 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @TestForIssue(jiraKey = "HHH-14530") +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) public class PreParsedHbmXmlTest extends BaseSessionFactoryFunctionalTest { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchOptimisticLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchOptimisticLockingTest.java index 5d330753be..c6c06f1083 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchOptimisticLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchOptimisticLockingTest.java @@ -17,19 +17,24 @@ import jakarta.persistence.Id; import jakarta.persistence.OptimisticLockException; import jakarta.persistence.Version; +import org.hibernate.StaleObjectStateException; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author Vlad Mihalcea */ +@SkipForDialect(value = CockroachDialect.class, comment = "See https://hibernate.atlassian.net/browse/HHH-15668") public class BatchOptimisticLockingTest extends BaseNonConfigCoreFunctionalTestCase { @@ -101,6 +106,11 @@ public class BatchOptimisticLockingTest extends expected.getMessage() ); } + else if ( getDialect() instanceof OracleDialect && getDialect().getVersion().isBefore( 12 ) ) { + assertTrue( + expected.getCause() instanceof StaleObjectStateException + ); + } else { assertEquals( "Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1; statement executed: update Person set name=?, version=? where id=? and version=?", diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/qualfiedTableNaming/DefaultCatalogAndSchemaTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/qualfiedTableNaming/DefaultCatalogAndSchemaTest.java index fe823e86c3..de508b5a97 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/qualfiedTableNaming/DefaultCatalogAndSchemaTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/qualfiedTableNaming/DefaultCatalogAndSchemaTest.java @@ -55,6 +55,8 @@ import org.hibernate.tool.schema.spi.TargetDescriptor; import org.hibernate.testing.AfterClassOnce; import org.hibernate.testing.BeforeClassOnce; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.junit4.CustomParameterized; @@ -82,6 +84,7 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(CustomParameterized.class) @TestForIssue(jiraKey = { "HHH-14921", "HHH-14922", "HHH-15212" }) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class DefaultCatalogAndSchemaTest { private static final String SQL_QUOTE_CHARACTER_CLASS = "([`\"]|\\[|\\])"; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingNotInDefaultFetchGroupPersistTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingNotInDefaultFetchGroupPersistTest.java index 6de9e18b39..57cb36e4d4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingNotInDefaultFetchGroupPersistTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingNotInDefaultFetchGroupPersistTest.java @@ -13,6 +13,8 @@ import java.util.List; import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; @@ -42,6 +44,7 @@ import static org.junit.Assert.assertFalse; */ @TestForIssue(jiraKey = "HHH-14360") @RunWith(BytecodeEnhancerRunner.class) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class DirtyTrackingNotInDefaultFetchGroupPersistTest extends BaseNonConfigCoreFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingPersistTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingPersistTest.java index 98708c86dd..f3e0681e7a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingPersistTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingPersistTest.java @@ -27,6 +27,8 @@ import jakarta.persistence.TemporalType; import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; @@ -41,6 +43,7 @@ import static org.junit.Assert.assertTrue; */ @TestForIssue(jiraKey = "HHH-14360") @RunWith(BytecodeEnhancerRunner.class) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class DirtyTrackingPersistTest extends BaseNonConfigCoreFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/LoadAndUpdateEntitiesWithCollectionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/LoadAndUpdateEntitiesWithCollectionsTest.java index 832f7f640f..d9118204c5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/LoadAndUpdateEntitiesWithCollectionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/LoadAndUpdateEntitiesWithCollectionsTest.java @@ -19,6 +19,8 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; @@ -34,6 +36,7 @@ import static org.junit.Assert.assertThat; @TestForIssue(jiraKey = "HHH14424") @RunWith(BytecodeEnhancerRunner.class) @CustomEnhancementContext({ DirtyCheckEnhancementContext.class, NoDirtyCheckEnhancementContext.class }) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class LoadAndUpdateEntitiesWithCollectionsTest extends BaseNonConfigCoreFunctionalTestCase { boolean skipTest; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/DynamicUpdateAndCollectionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/DynamicUpdateAndCollectionsTest.java index 9e966653b2..a7a1e80279 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/DynamicUpdateAndCollectionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/DynamicUpdateAndCollectionsTest.java @@ -19,6 +19,8 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; @@ -37,6 +39,7 @@ import static org.junit.Assert.assertThat; @TestForIssue(jiraKey = "HHH14424") @RunWith(BytecodeEnhancerRunner.class) @CustomEnhancementContext({ NoDirtyCheckEnhancementContext.class, DirtyCheckEnhancementContext.class }) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class DynamicUpdateAndCollectionsTest extends BaseNonConfigCoreFunctionalTestCase { boolean skipTest; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cut/CompositeUserTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cut/CompositeUserTypeTest.java index 983f37fa8f..0df67e8d85 100755 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cut/CompositeUserTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cut/CompositeUserTypeTest.java @@ -226,7 +226,7 @@ public class CompositeUserTypeTest { final Transaction t2 = new Transaction(); t2.setDescription( "bar" ); t2.setValue( new MonetoryAmount( new BigDecimal( 1000000 ), Currency.getInstance( "USD" ) ) ); - t1.setTimestamp( new CompositeDateTime( 2014, 8, 22, 14, 23, 0 ) ); + t2.setTimestamp( new CompositeDateTime( 2014, 8, 22, 14, 23, 0 ) ); session.persist( t2 ); final Transaction t3 = new Transaction(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/OracleDialectSequenceInformationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/OracleDialectSequenceInformationTest.java index 61e446eef1..cd849c0d51 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/OracleDialectSequenceInformationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/OracleDialectSequenceInformationTest.java @@ -17,6 +17,8 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.hibernate.testing.transaction.TransactionUtil; + import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorOracleDatabaseImpl; import org.hibernate.tool.schema.extract.spi.ExtractionContext; import org.hibernate.tool.schema.extract.spi.SequenceInformation; @@ -70,33 +72,40 @@ public class OracleDialectSequenceInformationTest extends BaseNonConfigCoreFunct } private SequenceInformation fetchSequenceInformation(String sequenceName) throws SQLException { - try ( Connection connection = sessionFactory().getJdbcServices() - .getBootstrapJdbcConnectionAccess() - .obtainConnection() ) { - JdbcEnvironment jdbcEnvironment = sessionFactory().getJdbcServices().getJdbcEnvironment(); - SequenceInformationExtractorOracleDatabaseImpl sequenceExtractor = SequenceInformationExtractorOracleDatabaseImpl.INSTANCE; - Iterable sequenceInformations = sequenceExtractor.extractMetadata( - new ExtractionContext.EmptyExtractionContext() { + return TransactionUtil.doWithJDBC( + sessionFactory().getServiceRegistry(), + connection -> { + JdbcEnvironment jdbcEnvironment = sessionFactory().getJdbcServices().getJdbcEnvironment(); + SequenceInformationExtractorOracleDatabaseImpl sequenceExtractor = SequenceInformationExtractorOracleDatabaseImpl.INSTANCE; + Iterable sequenceInformations = sequenceExtractor.extractMetadata( + new ExtractionContext.EmptyExtractionContext() { - @Override - public Connection getJdbcConnection() { - return connection; - } + @Override + public Connection getJdbcConnection() { + return connection; + } - @Override - public JdbcEnvironment getJdbcEnvironment() { - return jdbcEnvironment; - } - } ); + @Override + public JdbcEnvironment getJdbcEnvironment() { + return jdbcEnvironment; + } + } ); - // lets skip system sequences - Optional foundSequence = StreamSupport.stream( sequenceInformations.spliterator(), false ) - .filter( sequence -> sequenceName.equals( sequence.getSequenceName().getSequenceName().getText().toUpperCase() ) ) - .findFirst(); + // lets skip system sequences + Optional foundSequence = StreamSupport.stream( + sequenceInformations.spliterator(), + false + ) + .filter( sequence -> sequenceName.equals( sequence.getSequenceName() + .getSequenceName() + .getText() + .toUpperCase() ) ) + .findFirst(); - assertTrue( sequenceName + " not found", foundSequence.isPresent() ); + assertTrue( sequenceName + " not found", foundSequence.isPresent() ); - return foundSequence.get(); - } + return foundSequence.get(); + } + ); } } \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/DB297SubStringFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/DB297SubStringFunctionsTest.java index fe67093514..370d186838 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/DB297SubStringFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/DB297SubStringFunctionsTest.java @@ -11,6 +11,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.PersistenceException; +import org.hibernate.QueryException; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.dialect.DB2Dialect; @@ -115,8 +116,6 @@ public class DB297SubStringFunctionsTest extends BaseCoreFunctionalTestCase { @TestForIssue( jiraKey = "HHH-11957") public void testSubstrWithStringUnits() { - mostRecentStatementInspector.clear(); - try { doInHibernate( this::sessionFactory, session -> { @@ -129,12 +128,9 @@ public class DB297SubStringFunctionsTest extends BaseCoreFunctionalTestCase { ); fail( "Should have failed because substr cannot be used with string units." ); } - catch (PersistenceException expected) { - assertTrue( SQLGrammarException.class.isInstance( expected.getCause() ) ); + catch (IllegalArgumentException expected) { + assertTrue( QueryException.class.isInstance( expected.getCause() ) ); } - - assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "substr(" ) ); - assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "octets" ) ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/identity/hhh9983/SaveEntityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/identity/hhh9983/SaveEntityTest.java index 09c5f09220..25e57d52a4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/identity/hhh9983/SaveEntityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/identity/hhh9983/SaveEntityTest.java @@ -15,8 +15,10 @@ import jakarta.persistence.Table; import org.hibernate.dialect.OracleDialect; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; @@ -27,6 +29,7 @@ import org.junit.jupiter.api.Test; */ @TestForIssue(jiraKey = "HHH-9983") @RequiresDialect( OracleDialect.class ) +@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsIdentityColumns.class ) @DomainModel( annotatedClasses = SaveEntityTest.Company.class ) @SessionFactory public class SaveEntityTest { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/PersistAndQueryingInSameTransactionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/PersistAndQueryingInSameTransactionTest.java index 9567fdaf8c..2e827a4002 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/PersistAndQueryingInSameTransactionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/PersistAndQueryingInSameTransactionTest.java @@ -8,7 +8,9 @@ import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.AfterEach; @@ -35,6 +37,7 @@ import static org.hamcrest.Matchers.notNullValue; ) @SessionFactory @TestForIssue(jiraKey = "HHH-15512") +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) public class PersistAndQueryingInSameTransactionTest { @AfterEach diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryResultTypeAutoDiscoveryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryResultTypeAutoDiscoveryTest.java index fcae469baf..b63b99cb92 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryResultTypeAutoDiscoveryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryResultTypeAutoDiscoveryTest.java @@ -32,11 +32,13 @@ import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.HSQLDialect; +import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.PostgresPlusDialect; import org.hibernate.dialect.SybaseDialect; +import org.hibernate.dialect.TiDBDialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; import org.hibernate.jpa.boot.spi.Bootstrap; @@ -51,7 +53,7 @@ import org.hibernate.type.descriptor.jdbc.NumericJdbcType; import org.hibernate.type.descriptor.jdbc.RealJdbcType; import org.hibernate.testing.RequiresDialect; -import org.hibernate.testing.SkipForDialect; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.CustomRunner; import org.hibernate.testing.orm.jpa.PersistenceUnitDescriptorAdapter; @@ -97,7 +99,7 @@ public class NativeQueryResultTypeAutoDiscoveryTest { } @Test - @SkipForDialect(value=OracleDialect.class, comment="Oracle maps integer types to number") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle maps integer types to number") public void smallintType() { createEntityManagerFactory(SmallintEntity.class); doTest( SmallintEntity.class, (short)32767 ); @@ -121,56 +123,61 @@ public class NativeQueryResultTypeAutoDiscoveryTest { } @Test - @SkipForDialect(value = SybaseDialect.class, comment = "No support for the bit datatype so we use tinyint") - @SkipForDialect(value = OracleDialect.class, comment = "No support for the bit datatype so we use number(1,0)") + @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "No support for the bit datatype so we use tinyint") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "No support for the bit datatype so we use number(1,0)") + @SkipForDialect(dialectClass = DB2Dialect.class, majorVersion = 10, reason = "No support for the bit datatype so we use smallint") public void booleanType() { createEntityManagerFactory( BooleanEntity.class ); doTest( BooleanEntity.class, true ); } @Test - @SkipForDialect(value = SybaseDialect.class, comment = "No support for the bit datatype so we use tinyint") - @SkipForDialect(value = OracleDialect.class, comment = "No support for the bit datatype so we use number(1,0)") + @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "No support for the bit datatype so we use tinyint") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "No support for the bit datatype so we use number(1,0)") + @SkipForDialect(dialectClass = DB2Dialect.class, majorVersion = 10, reason = "No support for the bit datatype so we use smallint") public void bitType() { createEntityManagerFactory( BitEntity.class ); doTest( BitEntity.class, false ); } @Test - @SkipForDialect(value = PostgreSQLDialect.class, comment = "Turns tinyints into shorts in result sets and advertises the type as short in the metadata") - @SkipForDialect(value = CockroachDialect.class, comment = "Turns tinyints into shorts in result sets and advertises the type as short in the metadata") - @SkipForDialect(value = DerbyDialect.class, comment = "No support for the tinyint datatype so we use smallint") - @SkipForDialect(value = DB2Dialect.class, comment = "No support for the tinyint datatype so we use smallint") - @SkipForDialect(value = AbstractTransactSQLDialect.class, comment = "No support for the tinyint datatype so we use smallint") - @SkipForDialect(value = AbstractHANADialect.class, comment = "No support for the tinyint datatype so we use smallint") - @SkipForDialect(value = OracleDialect.class, comment="Oracle maps tinyint to number") + @SkipForDialect(dialectClass = PostgreSQLDialect.class, reason = "Turns tinyints into shorts in result sets and advertises the type as short in the metadata") + @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "Turns tinyints into shorts in result sets and advertises the type as short in the metadata") + @SkipForDialect(dialectClass = CockroachDialect.class, reason = "Turns tinyints into shorts in result sets and advertises the type as short in the metadata") + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "No support for the tinyint datatype so we use smallint") + @SkipForDialect(dialectClass = DB2Dialect.class, reason = "No support for the tinyint datatype so we use smallint") + @SkipForDialect(dialectClass = AbstractTransactSQLDialect.class, matchSubTypes = true, reason = "No support for the tinyint datatype so we use smallint") + @SkipForDialect(dialectClass = AbstractHANADialect.class, matchSubTypes = true, reason = "No support for the tinyint datatype so we use smallint") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle maps tinyint to number") public void tinyintType() { createEntityManagerFactory( TinyintEntity.class ); doTest( TinyintEntity.class, (byte)127 ); } @Test - @SkipForDialect(value = H2Dialect.class, comment = "Turns floats into doubles in result sets and advertises the type as double in the metadata") - @SkipForDialect(value = HSQLDialect.class, comment = "Turns floats into doubles in result sets and advertises the type as double in the metadata") + @SkipForDialect(dialectClass = H2Dialect.class, reason = "Turns floats into doubles in result sets and advertises the type as double in the metadata") + @SkipForDialect(dialectClass = HSQLDialect.class, reason = "Turns floats into doubles in result sets and advertises the type as double in the metadata") public void floatType() { createEntityManagerFactory( FloatEntity.class ); doTest( FloatEntity.class, 15516.125f ); } @Test - @SkipForDialect(value = MySQLDialect.class, comment = "Turns reals into doubles in result sets and advertises the type as double in the metadata") - @SkipForDialect(value = HSQLDialect.class, comment = "Turns reals into doubles in result sets and advertises the type as double in the metadata") + @SkipForDialect(dialectClass = MySQLDialect.class, reason = "Turns reals into doubles in result sets and advertises the type as double in the metadata") + @SkipForDialect(dialectClass = MariaDBDialect.class, reason = "Turns reals into doubles in result sets and advertises the type as double in the metadata") + @SkipForDialect(dialectClass = TiDBDialect.class, reason = "Turns reals into doubles in result sets and advertises the type as double in the metadata") + @SkipForDialect(dialectClass = HSQLDialect.class, reason = "Turns reals into doubles in result sets and advertises the type as double in the metadata") public void realType() { createEntityManagerFactory( RealEntity.class ); doTest( RealEntity.class, 15516.125f ); } @Test - @SkipForDialect(value = DerbyDialect.class, comment = "Value is too big for the maximum allowed precision of Derby") - @SkipForDialect(value = DB2Dialect.class, comment = "Value is too big for the maximum allowed precision of DB2") - @SkipForDialect(value = OracleDialect.class, comment = "Value is too big for the maximum allowed precision of Oracle") - @SkipForDialect(value = AbstractTransactSQLDialect.class, comment = "Value is too big for the maximum allowed precision of SQL Server and Sybase") - @SkipForDialect(value = AbstractHANADialect.class, comment = "Value is too big for the maximum allowed precision of HANA") + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Value is too big for the maximum allowed precision of Derby") + @SkipForDialect(dialectClass = DB2Dialect.class, reason = "Value is too big for the maximum allowed precision of DB2") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Value is too big for the maximum allowed precision of Oracle") + @SkipForDialect(dialectClass = AbstractTransactSQLDialect.class, matchSubTypes = true, reason = "Value is too big for the maximum allowed precision of SQL Server and Sybase") + @SkipForDialect(dialectClass = AbstractHANADialect.class, matchSubTypes = true, reason = "Value is too big for the maximum allowed precision of HANA") public void numericType() { createEntityManagerFactory( NumericEntity.class @@ -179,11 +186,11 @@ public class NativeQueryResultTypeAutoDiscoveryTest { } @Test - @SkipForDialect(value = DerbyDialect.class, comment = "Value is too big for the maximum allowed precision of Derby") - @SkipForDialect(value = DB2Dialect.class, comment = "Value is too big for the maximum allowed precision of DB2") - @SkipForDialect(value = OracleDialect.class, comment = "Value is too big for the maximum allowed precision of Oracle") - @SkipForDialect(value = AbstractTransactSQLDialect.class, comment = "Value is too big for the maximum allowed precision of SQL Server and Sybase") - @SkipForDialect(value = AbstractHANADialect.class, comment = "Value is too big for the maximum allowed precision of HANA") + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Value is too big for the maximum allowed precision of Derby") + @SkipForDialect(dialectClass = DB2Dialect.class, reason = "Value is too big for the maximum allowed precision of DB2") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Value is too big for the maximum allowed precision of Oracle") + @SkipForDialect(dialectClass = AbstractTransactSQLDialect.class, matchSubTypes = true, reason = "Value is too big for the maximum allowed precision of SQL Server and Sybase") + @SkipForDialect(dialectClass = AbstractHANADialect.class, matchSubTypes = true, reason = "Value is too big for the maximum allowed precision of HANA") public void decimalType() { createEntityManagerFactory( DecimalEntity.class ); doTest( DecimalEntity.class, new BigDecimal( "5464384284258458485484848458.48465843584584684" ) ); @@ -201,10 +208,10 @@ public class NativeQueryResultTypeAutoDiscoveryTest { } @Test - @SkipForDialect(value = OracleDialect.class, comment = "Oracle maps LONGVARCHAR to CLOB") - @SkipForDialect(value = DB2Dialect.class, comment = "DB2 maps LONGVARCHAR to CLOB") - @SkipForDialect(value = SybaseDialect.class, comment = "Sybase maps LONGVARCHAR to CLOB") - @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA maps LONGVARCHAR to CLOB") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle maps LONGVARCHAR to CLOB") + @SkipForDialect(dialectClass = DB2Dialect.class, reason = "DB2 maps LONGVARCHAR to CLOB") + @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase maps LONGVARCHAR to CLOB") + @SkipForDialect(dialectClass = AbstractHANADialect.class, matchSubTypes = true, reason = "HANA maps LONGVARCHAR to CLOB") public void longCharType() { createEntityManagerFactory( LongvarcharEntity.class @@ -239,10 +246,10 @@ public class NativeQueryResultTypeAutoDiscoveryTest { } @Test - @SkipForDialect(value = OracleDialect.class, comment = "Oracle maps LONGVARBINARY to BLOB") - @SkipForDialect(value = DB2Dialect.class, comment = "DB2 maps LONGVARBINARY to BLOB") - @SkipForDialect(value = SybaseDialect.class, comment = "Sybase maps LONGVARBINARY to BLOB") - @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA maps LONGVARCHAR to BLOB") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle maps LONGVARBINARY to BLOB") + @SkipForDialect(dialectClass = DB2Dialect.class, reason = "DB2 maps LONGVARBINARY to BLOB") + @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase maps LONGVARBINARY to BLOB") + @SkipForDialect(dialectClass = AbstractHANADialect.class, matchSubTypes = true, reason = "HANA maps LONGVARCHAR to BLOB") public void longBinaryType() { createEntityManagerFactory( LongvarbinaryEntity.class @@ -273,8 +280,8 @@ public class NativeQueryResultTypeAutoDiscoveryTest { } @Test - @SkipForDialect(value = OracleDialect.class, comment = "Oracle maps DATE and TIME to TIMESTAMP") - @SkipForDialect(value = PostgresPlusDialect.class, comment = "EDB maps DATE and TIME to TIMESTAMP") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle maps DATE and TIME to TIMESTAMP") + @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "EDB maps DATE and TIME to TIMESTAMP") public void dateTimeTypes() { createEntityManagerFactory( DateEntity.class, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/always/GeneratedAlwaysTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/always/GeneratedAlwaysTest.java index 45af4bba20..aad07989bf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/always/GeneratedAlwaysTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/always/GeneratedAlwaysTest.java @@ -5,6 +5,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import org.hibernate.annotations.GeneratedColumn; import org.hibernate.dialect.DerbyDialect; +import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SybaseASEDialect; @@ -24,6 +25,7 @@ import static org.junit.Assert.assertEquals; */ @DomainModel(annotatedClasses = GeneratedAlwaysTest.OrderLine.class) @SessionFactory +@SkipForDialect(dialectClass = H2Dialect.class, majorVersion = 1) // 'generated always' was added in 2.0 @SkipForDialect(dialectClass = HSQLDialect.class) @SkipForDialect(dialectClass = DerbyDialect.class) @SkipForDialect(dialectClass = SybaseASEDialect.class) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/IdentityGeneratorWithNaturalIdCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/IdentityGeneratorWithNaturalIdCacheTest.java index 670cbd386a..1eabfb625c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/IdentityGeneratorWithNaturalIdCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/IdentityGeneratorWithNaturalIdCacheTest.java @@ -18,10 +18,10 @@ import org.hibernate.annotations.NaturalIdCache; import org.hibernate.cfg.AvailableSettings; import org.hibernate.stat.spi.StatisticsImplementor; -import org.hibernate.testing.DialectChecks; -import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -38,7 +38,6 @@ import static org.hamcrest.MatcherAssert.assertThat; * @author Alex Burgel */ @TestForIssue( jiraKey = "HHH-11330" ) -@RequiresDialectFeature( value = DialectChecks.SupportsIdentityColumns.class ) @ServiceRegistry( settings = { @Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ), @@ -47,6 +46,7 @@ import static org.hamcrest.MatcherAssert.assertThat; ) @DomainModel( annotatedClasses = IdentityGeneratorWithNaturalIdCacheTest.Person.class ) @SessionFactory +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) public class IdentityGeneratorWithNaturalIdCacheTest { @BeforeEach public void prepareTestData(SessionFactoryScope scope) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/SubqueryPaginationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/SubqueryPaginationTest.java index 91b21a13a2..e346c61f53 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/SubqueryPaginationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/SubqueryPaginationTest.java @@ -8,6 +8,9 @@ package org.hibernate.orm.test.pagination; import java.util.List; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.OracleDialect; + import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.gambit.EntityOfLists; import org.hibernate.testing.orm.domain.gambit.EnumValue; @@ -18,6 +21,7 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -69,6 +73,7 @@ public class SubqueryPaginationTest { } @Test + @SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "Generates nested correlated subquery which is not supported in that version") public void testLimitInSubquery(SessionFactoryScope scope) { scope.inSession( session -> { @@ -83,6 +88,8 @@ public class SubqueryPaginationTest { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsOffsetInSubquery.class) + @SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "Generates nested correlated subquery which is not supported in that version") + @SkipForDialect(dialectClass = DB2Dialect.class, majorVersion = 10, reason = "Generates nested correlated subquery which is not supported in that version") public void testLimitAndOffsetInSubquery(SessionFactoryScope scope) { scope.inSession( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh11877/HHH111877Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh11877/HHH111877Test.java index 65c550d097..c9ead3b769 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh11877/HHH111877Test.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh11877/HHH111877Test.java @@ -7,6 +7,8 @@ import jakarta.persistence.criteria.Root; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.junit.Test; @@ -17,6 +19,7 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; * @author Nathan Xu */ @TestForIssue( jiraKey = "HHH-11877" ) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class HHH111877Test extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14197/HHH14197Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14197/HHH14197Test.java index 481b316996..4c44b6190c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14197/HHH14197Test.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14197/HHH14197Test.java @@ -9,6 +9,8 @@ import jakarta.persistence.criteria.Subquery; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.junit.Test; @@ -19,6 +21,7 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; * @author Nathan Xu */ @TestForIssue( jiraKey = "HHH-14197" ) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class HHH14197Test extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14916/HHH14916Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14916/HHH14916Test.java index 5d2e51e603..e411e25192 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14916/HHH14916Test.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/internal/hhh14916/HHH14916Test.java @@ -1,8 +1,10 @@ package org.hibernate.orm.test.query.criteria.internal.hhh14916; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -19,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @Jpa( annotatedClasses = { Author.class, Book.class, Chapter.class } ) +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) public class HHH14916Test { @BeforeEach diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index f189f26247..71e7de2e34 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -42,6 +42,7 @@ import org.hamcrest.Matchers; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isOneOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -1437,7 +1438,7 @@ public class FunctionTests { .list(); assertThat( session.createQuery("select format(theTime as '''Hello'', hh:mm:ss a') from EntityOfBasics where id=123").getResultList().get(0), - is("Hello, 08:10:08 PM") + isOneOf( "Hello, 08:10:08 PM", "Hello, 08:10:08 pm" ) ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/StandardFunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/StandardFunctionTests.java index 7548c38df9..e29e5f96b1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/StandardFunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/StandardFunctionTests.java @@ -14,6 +14,7 @@ import java.time.LocalTime; import org.hibernate.dialect.CockroachDialect; +import org.hamcrest.Matchers; import org.hamcrest.number.IsCloseTo; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; @@ -30,6 +31,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isOneOf; /** * @author Steve Ebersole @@ -924,7 +926,7 @@ public class StandardFunctionTests { "select format(e.theTime as '''Hello'', hh:mm:ss a') from EntityOfBasics e" ) .getResultList() .get( 0 ), - is( "Hello, 08:10:08 PM" ) + isOneOf( "Hello, 08:10:08 PM", "Hello, 08:10:08 pm" ) ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/RecordingTarget.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/RecordingTarget.java index ecb5c5a280..7f26236384 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/RecordingTarget.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/RecordingTarget.java @@ -11,9 +11,9 @@ import org.hibernate.tool.schema.internal.exec.GenerationTarget; class RecordingTarget implements GenerationTarget { public enum Category { SCHEMA_CREATE( Pattern.compile( "create schema (.*)" ) ), - SCHEMA_DROP( Pattern.compile( "drop schema (.*)" ) ), + SCHEMA_DROP( Pattern.compile( "drop schema(?: if exists)? (.*)" ) ), TABLE_CREATE( Pattern.compile( "create table (\\S+) .*" ) ), - TABLE_DROP( Pattern.compile( "drop table (.*)" ) ), + TABLE_DROP( Pattern.compile( "drop table(?: if exists)? (.*)" ) ), SEQUENCE_CREATE(Pattern.compile( "create sequence (.*) start (.*)" )), SEQUENCE_DROP(Pattern.compile( "drop sequence if exists (.*)" )); diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java index ea3881edf6..a5afb867c1 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java @@ -10,6 +10,8 @@ import org.hibernate.envers.configuration.EnversSettings; import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase; import org.hibernate.orm.test.envers.Priority; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.junit.Test; import java.time.Duration; @@ -27,6 +29,7 @@ import static org.junit.Assert.assertEquals; * @author Luke Chen */ @TestForIssue(jiraKey = "HHH-13945") +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class ManyToOneLazyDeleteTest extends BaseEnversFunctionalTestCase { private Long shipmentId; private User user; diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyFetchTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyFetchTest.java index eeccda5b0f..53d6410784 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyFetchTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyFetchTest.java @@ -15,6 +15,8 @@ import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase; import org.hibernate.orm.test.envers.Priority; import org.junit.Test; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; @@ -27,6 +29,7 @@ import static org.junit.Assert.assertEquals; * @author Chris Cranford */ @TestForIssue(jiraKey = "HHH-13760") +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class ManyToOneLazyFetchTest extends BaseEnversFunctionalTestCase { private Long shipmentId; diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByLimitQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByLimitQuery.java index 47bb8c4b25..bf209ee83b 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByLimitQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByLimitQuery.java @@ -20,7 +20,6 @@ import org.junit.Test; /** * @author Adam Warski (adam at warski dot org) */ -@SuppressWarnings("unchecked") public class OrderByLimitQuery extends BaseEnversJPAFunctionalTestCase { private Integer id1; private Integer id2; diff --git a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java index bfa205a31e..d1d7b56dae 100644 --- a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java +++ b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java @@ -117,6 +117,9 @@ public class HikariCPConnectionProvider implements ConnectionProvider, Configura @Override public void stop() { - hds.close(); + HikariDataSource hds = this.hds; + if ( hds != null ) { + hds.close(); + } } } diff --git a/hibernate-hikaricp/src/test/resources/hibernate.properties b/hibernate-hikaricp/src/test/resources/hibernate.properties index a27b5a46fb..11d487ded5 100644 --- a/hibernate-hikaricp/src/test/resources/hibernate.properties +++ b/hibernate-hikaricp/src/test/resources/hibernate.properties @@ -16,6 +16,4 @@ hibernate.connection.provider_class HikariCPConnectionProvider hibernate.hikari.poolName testPool # Purposefully low and simplisitic. -hibernate.hikari.maximumPoolSize 2 -hibernate.hikari.connectionTimeout 1000 -hibernate.hikari.idleTimeout 3000 \ No newline at end of file +hibernate.hikari.maximumPoolSize 2 \ No newline at end of file diff --git a/hibernate-spatial/hibernate-spatial.gradle b/hibernate-spatial/hibernate-spatial.gradle index 9893eb068d..c68f0c89d2 100644 --- a/hibernate-spatial/hibernate-spatial.gradle +++ b/hibernate-spatial/hibernate-spatial.gradle @@ -40,16 +40,16 @@ sourceSets.test.resources { tasks.test { - enabled = ['pgsql', - 'h2', + enabled = ['h2', + 'pgsql', 'pgsql_ci', 'cockroachdb', 'mariadb', + 'mariadb_ci', + 'mysql', 'mysql_ci', - 'mysql_docker', - 'oracle_docker', + 'oracle', 'oracle_ci', - 'oracle_rds', 'mssql', 'mssql_ci' ].contains( project.db ) diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/OracleSQLMMFunctionTests.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/OracleSQLMMFunctionTests.java index 2f895e6092..0773372d69 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/OracleSQLMMFunctionTests.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/OracleSQLMMFunctionTests.java @@ -22,6 +22,7 @@ import org.hibernate.spatial.testing.dialects.oracle.OracleSTNativeSqlTemplates; import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.Setting; +import org.hibernate.testing.orm.junit.SkipForDialect; /** * Only for Oracle: run the tests in "OGC_STRICT" mode (i.e. using the SQL MultiMedia functions) @@ -30,6 +31,8 @@ import org.hibernate.testing.orm.junit.Setting; @ServiceRegistry(settings = { @Setting(name = HibernateSpatialConfigurationSettings.ORACLE_OGC_STRICT, value = "true") }) +@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 21, reason = "See https://hibernate.atlassian.net/browse/HHH-15669") +@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "See https://hibernate.atlassian.net/browse/HHH-15669") public class OracleSQLMMFunctionTests extends CommonFunctionTests { public OracleSQLMMFunctionTests() { this.templates = new OracleSTNativeSqlTemplates(); diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java index 7622e33b78..ea90040a9f 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java @@ -20,6 +20,7 @@ import java.util.Locale; import java.util.Objects; import java.util.stream.Stream; +import org.hibernate.dialect.OracleDialect; import org.hibernate.spatial.integration.Model; import org.hibernate.spatial.testing.IsSupportedBySpatial; import org.hibernate.spatial.testing.SpatialTestBase; @@ -27,6 +28,7 @@ import org.hibernate.spatial.testing.datareader.TestSupport; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.Executable; @@ -49,6 +51,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @SuppressWarnings("rawtypes") @RequiresDialectFeature(feature = IsSupportedBySpatial.class) @SessionFactory +@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 21, reason = "See https://hibernate.atlassian.net/browse/HHH-15669") +@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "See https://hibernate.atlassian.net/browse/HHH-15669") public class CommonFunctionTests extends SpatialTestBase { public final static TestSupport.TestDataPurpose PURPOSE = TestSupport.TestDataPurpose.SpatialFunctionsData; diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestGeometryConstructionWithParameter.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestGeometryConstructionWithParameter.java index 63e508d092..bae9f90a12 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestGeometryConstructionWithParameter.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestGeometryConstructionWithParameter.java @@ -12,6 +12,7 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; +import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.spatial.CommonSpatialFunction; @@ -22,12 +23,14 @@ import org.hibernate.spatial.testing.domain.GeomEntity; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.Executable; @RequiresDialectFeature(feature = IsSupportedBySpatial.class) @SessionFactory +@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "See https://hibernate.atlassian.net/browse/HHH-15669") public class TestGeometryConstructionWithParameter extends SpatialTestBase { final private Map templates = new HashMap<>(); diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/PredicateSmokeTest.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/PredicateSmokeTest.java index 9c636dbcb7..893992da35 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/PredicateSmokeTest.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/PredicateSmokeTest.java @@ -9,6 +9,7 @@ package org.hibernate.spatial.integration.predicates; import java.util.List; +import org.hibernate.dialect.OracleDialect; import org.hibernate.spatial.predicate.GeolatteSpatialPredicates; import org.hibernate.spatial.testing.IsSupportedBySpatial; import org.hibernate.spatial.testing.SpatialSessionFactoryAware; @@ -18,6 +19,7 @@ import org.hibernate.spatial.testing.domain.SpatialDomainModel; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; import jakarta.persistence.criteria.CriteriaBuilder; @@ -34,6 +36,7 @@ import static org.geolatte.geom.crs.CoordinateReferenceSystems.WGS84; @DomainModel(modelDescriptorClasses = SpatialDomainModel.class) @SessionFactory @RequiresDialectFeature(feature = IsSupportedBySpatial.class) +@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "See https://hibernate.atlassian.net/browse/HHH-15669") public class PredicateSmokeTest extends SpatialSessionFactoryAware { Polygon poly = polygon( diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/SpatialPredicatesTestInlineMode.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/SpatialPredicatesTestInlineMode.java index c75462950c..627a7fa432 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/SpatialPredicatesTestInlineMode.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/SpatialPredicatesTestInlineMode.java @@ -10,6 +10,7 @@ package org.hibernate.spatial.integration.predicates; import java.util.stream.Stream; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.OracleDialect; import org.hibernate.spatial.HibernateSpatialConfigurationSettings; import org.hibernate.spatial.testing.IsSupportedBySpatial; import org.hibernate.spatial.testing.dialects.PredicateRegexes; @@ -18,6 +19,7 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.Setting; +import org.hibernate.testing.orm.junit.SkipForDialect; @RequiresDialectFeature(feature = IsSupportedBySpatial.class) @ServiceRegistry(settings = { @@ -25,6 +27,7 @@ import org.hibernate.testing.orm.junit.Setting; @Setting(name = HibernateSpatialConfigurationSettings.ORACLE_OGC_STRICT, value = "true") }) @SessionFactory +@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "See https://hibernate.atlassian.net/browse/HHH-15669") public class SpatialPredicatesTestInlineMode extends SpatialPredicatesTest{ @Override public Stream getTestRegexes() { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java index 8d11cb2513..dfd89cf4b5 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java @@ -26,9 +26,13 @@ import org.hibernate.testing.Skip; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.SkipForDialects; import org.hibernate.testing.orm.junit.DialectContext; +import org.hibernate.testing.orm.junit.DialectFilterExtension; +import org.hibernate.testing.orm.junit.SkipForDialectGroup; +import org.hibernate.testing.orm.junit.TestingUtil; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; +import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.runner.manipulation.NoTestsRemainException; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; @@ -264,6 +268,48 @@ public class CustomRunner extends BlockJUnit4ClassRunner { } } + for ( org.hibernate.testing.orm.junit.SkipForDialect effectiveSkipForDialect : Helper.collectAnnotations( + org.hibernate.testing.orm.junit.SkipForDialect.class, SkipForDialectGroup.class, frameworkMethod, getTestClass() + ) ) { + final boolean versionsMatch; + final int matchingMajorVersion = effectiveSkipForDialect.majorVersion(); + + if ( matchingMajorVersion >= 0 ) { + versionsMatch = DialectFilterExtension.versionsMatch( + matchingMajorVersion, + effectiveSkipForDialect.minorVersion(), + effectiveSkipForDialect.microVersion(), + dialect, + effectiveSkipForDialect.matchSubTypes() + ); + + if ( versionsMatch ) { + if ( effectiveSkipForDialect.matchSubTypes() ) { + if ( effectiveSkipForDialect.dialectClass().isInstance( dialect ) ) { + return buildIgnore( effectiveSkipForDialect ); + } + } + else { + if ( effectiveSkipForDialect.dialectClass().equals( dialect.getClass() ) ) { + return buildIgnore( effectiveSkipForDialect ); + } + } + } + } + else { + if ( effectiveSkipForDialect.matchSubTypes() ) { + if ( effectiveSkipForDialect.dialectClass().isInstance( dialect ) ) { + return buildIgnore( effectiveSkipForDialect ); + } + } + else { + if ( effectiveSkipForDialect.dialectClass().equals( dialect.getClass() ) ) { + return buildIgnore( effectiveSkipForDialect ); + } + } + } + } + // @RequiresDialects & @RequiresDialect final List requiresDialects = Helper.collectAnnotations( @@ -328,6 +374,10 @@ public class CustomRunner extends BlockJUnit4ClassRunner { return buildIgnore( "@SkipForDialect match", skip.comment(), skip.jiraKey() ); } + private Ignore buildIgnore(org.hibernate.testing.orm.junit.SkipForDialect skip) { + return buildIgnore( "@SkipForDialect match", skip.reason(), null ); + } + private Ignore buildIgnore(String reason, String comment, String jiraKey) { return new IgnoreImpl( getIgnoreMessage( reason, comment, jiraKey ) ); } 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 40704c9043..5b0903af22 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 @@ -396,7 +396,7 @@ abstract public class DialectFeatureChecks { || dialect instanceof PostgreSQLDialect || dialect instanceof AbstractHANADialect || dialect instanceof CockroachDialect - || dialect instanceof DB2Dialect + || dialect instanceof DB2Dialect && ( (DB2Dialect) dialect ).getDB2Version().isSameOrAfter( 11 ) || dialect instanceof OracleDialect || dialect instanceof SpannerDialect || dialect instanceof SQLServerDialect; @@ -409,7 +409,7 @@ abstract public class DialectFeatureChecks { || dialect instanceof PostgreSQLDialect || dialect instanceof AbstractHANADialect || dialect instanceof CockroachDialect - || dialect instanceof DB2Dialect + || dialect instanceof DB2Dialect && ( (DB2Dialect) dialect ).getDB2Version().isSameOrAfter( 11 ) || dialect instanceof OracleDialect || dialect instanceof SpannerDialect || dialect instanceof SQLServerDialect; @@ -451,7 +451,8 @@ abstract public class DialectFeatureChecks { public boolean apply(Dialect dialect) { return !( dialect instanceof MySQLDialect || dialect instanceof SybaseDialect - || dialect instanceof DerbyDialect ) + || dialect instanceof DerbyDialect + || dialect instanceof DB2Dialect && ( (DB2Dialect) dialect ).getDB2Version().isBefore( 11 ) ) || dialect instanceof MariaDBDialect; } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFilterExtension.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFilterExtension.java index 513ae5ff85..f4bab50790 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFilterExtension.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFilterExtension.java @@ -111,7 +111,7 @@ public class DialectFilterExtension implements ExecutionCondition { return buffer.toString(); } - private boolean versionsMatch( + public static boolean versionsMatch( int matchingMajorVersion, int matchingMinorVersion, int matchingMicroVersion, 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 261591fcc0..df2f68b5f2 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 @@ -652,16 +652,30 @@ public class TransactionUtil { StandardServiceRegistry ssr = ssrb.build(); try { - try (Connection connection = ssr.getService( JdbcServices.class ) - .getBootstrapJdbcConnectionAccess() - .obtainConnection(); - Statement statement = connection.createStatement()) { + final JdbcConnectionAccess connectionAccess = ssr.getService( JdbcServices.class ) + .getBootstrapJdbcConnectionAccess(); + final Connection connection; + try { + connection = connectionAccess.obtainConnection(); + } + catch (SQLException e) { + throw new RuntimeException( e ); + } + try (Statement statement = connection.createStatement()) { connection.setAutoCommit( true ); consumer.accept( statement ); } catch (SQLException e) { log.debug( e.getMessage() ); } + finally { + try { + connectionAccess.releaseConnection( connection ); + } + catch (SQLException e) { + // ignore + } + } } finally { StandardServiceRegistryBuilder.destroy( ssr ); @@ -717,7 +731,13 @@ public class TransactionUtil { public static void doWithJDBC(ServiceRegistry serviceRegistry, JDBCTransactionVoidFunction function) throws SQLException { final JdbcConnectionAccess connectionAccess = serviceRegistry.getService( JdbcServices.class ) .getBootstrapJdbcConnectionAccess(); - Connection connection = connectionAccess.obtainConnection(); + final Connection connection; + try { + connection = connectionAccess.obtainConnection(); + } + catch (SQLException e) { + throw new RuntimeException( e ); + } try { function.accept( connection ); } @@ -735,7 +755,13 @@ public class TransactionUtil { public static T doWithJDBC(ServiceRegistry serviceRegistry, JDBCTransactionFunction function) throws SQLException { final JdbcConnectionAccess connectionAccess = serviceRegistry.getService( JdbcServices.class ) .getBootstrapJdbcConnectionAccess(); - Connection connection = connectionAccess.obtainConnection(); + final Connection connection; + try { + connection = connectionAccess.obtainConnection(); + } + catch (SQLException e) { + throw new RuntimeException( e ); + } try { return function.accept( connection ); } diff --git a/nightly.Jenkinsfile b/nightly.Jenkinsfile index 70b96ce64e..f79dd103dd 100644 --- a/nightly.Jenkinsfile +++ b/nightly.Jenkinsfile @@ -26,7 +26,21 @@ this.helper = new JobHelper(this) helper.runWithNotification { stage('Configure') { this.environments = [ + // Minimum supported versions + new BuildEnvironment( dbName: 'h2_1_4' ), + new BuildEnvironment( dbName: 'hsqldb_2_6' ), + new BuildEnvironment( dbName: 'derby_10_14' ), + new BuildEnvironment( dbName: 'mysql_5_7' ), + new BuildEnvironment( dbName: 'mariadb_10_3' ), + new BuildEnvironment( dbName: 'postgresql_10' ), + new BuildEnvironment( dbName: 'edb_10' ), + new BuildEnvironment( dbName: 'oracle_11_2' ), + new BuildEnvironment( dbName: 'db2_10_5', longRunning: true ), + new BuildEnvironment( dbName: 'mssql_2017' ), // Unfortunately there is no SQL Server 2008 image, so we have to test with 2017 +// new BuildEnvironment( dbName: 'sybase_16' ), // There only is a Sybase ASE 16 image, so no pint in testing that nightly + // Long running databases new BuildEnvironment( dbName: 'cockroachdb', node: 'cockroachdb', longRunning: true ), + new BuildEnvironment( dbName: 'cockroachdb_21_2', node: 'cockroachdb', longRunning: true ), new BuildEnvironment( dbName: 'hana_cloud', dbLockableResource: 'hana-cloud', dbLockResourceAsHost: true ) ]; @@ -94,55 +108,84 @@ stage('Build') { try { stage('Start database') { switch (buildEnv.dbName) { - case "cockroachdb": - docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('cockroachdb/cockroach:v21.1.21').pull() - } - sh "./docker_db.sh cockroachdb" - state[buildEnv.tag]['containerName'] = "cockroach" + case "h2_1_4": + state[buildEnv.tag]['additionalOptions'] = state[buildEnv.tag]['additionalOptions'] + + " -Pgradle.libs.versions.h2=1.4.197 -Pgradle.libs.versions.h2gis=1.5.0" + break; + case "hsqldb_2_6": + state[buildEnv.tag]['additionalOptions'] = state[buildEnv.tag]['additionalOptions'] + + " -Pgradle.libs.versions.hsqldb=2.6.1" + break; + case "derby_10_14": + state[buildEnv.tag]['additionalOptions'] = state[buildEnv.tag]['additionalOptions'] + + " -Pgradle.libs.versions.derby=10.14.2.0" break; case "mysql": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('mysql:5.7').pull() + docker.image('mysql:8.0.31').pull() } sh "./docker_db.sh mysql" state[buildEnv.tag]['containerName'] = "mysql" break; - case "mysql8": + case "mysql_5_7": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('mysql:8.0.21').pull() + docker.image('mysql:5.7.40').pull() } - sh "./docker_db.sh mysql_8_0" + sh "./docker_db.sh mysql_5_7" state[buildEnv.tag]['containerName'] = "mysql" break; case "mariadb": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('mariadb:10.7.5').pull() + docker.image('mariadb:10.9.3').pull() } sh "./docker_db.sh mariadb" state[buildEnv.tag]['containerName'] = "mariadb" break; + case "mariadb_10_3": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('mariadb:10.3.36').pull() + } + sh "./docker_db.sh mariadb_10_3" + state[buildEnv.tag]['containerName'] = "mariadb" + break; case "postgresql": // use the postgis image to enable the PGSQL GIS (spatial) extension docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('postgis/postgis:9.5-2.5').pull() + docker.image('postgis/postgis:14-3.3').pull() } sh "./docker_db.sh postgresql" state[buildEnv.tag]['containerName'] = "postgres" break; - case "postgresql_14": + case "postgresql_10": // use the postgis image to enable the PGSQL GIS (spatial) extension docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('postgis/postgis:14-3.3').pull() + docker.image('postgis/postgis:10-2.5').pull() } - sh "./docker_db.sh postgresql_14" + sh "./docker_db.sh postgresql_10" state[buildEnv.tag]['containerName'] = "postgres" break; + case "edb": + docker.image('quay.io/enterprisedb/edb-postgres-advanced:14.5-3.2-postgis').pull() + sh "./docker_db.sh edb" + state[buildEnv.tag]['containerName'] = "edb" + break; + case "edb_10": + docker.image('quay.io/enterprisedb/edb-postgres-advanced:10.22').pull() + sh "./docker_db.sh edb_10" + state[buildEnv.tag]['containerName'] = "edb" + break; case "oracle": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { - docker.image('gvenzl/oracle-xe:18.4.0-full').pull() + docker.image('gvenzl/oracle-xe:21.3.0-full').pull() } - sh "./docker_db.sh oracle_18" + sh "./docker_db.sh oracle" + state[buildEnv.tag]['containerName'] = "oracle" + break; + case "oracle_11_2": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('gvenzl/oracle-xe:11.2.0.2-full').pull() + } + sh "./docker_db.sh oracle_11" state[buildEnv.tag]['containerName'] = "oracle" break; case "db2": @@ -152,11 +195,23 @@ stage('Build') { sh "./docker_db.sh db2" state[buildEnv.tag]['containerName'] = "db2" break; + case "db2_10_5": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('ibmoms/db2express-c@sha256:a499afd9709a1f69fb41703e88def9869955234c3525547e2efc3418d1f4ca2b').pull() + } + sh "./docker_db.sh db2_10_5" + state[buildEnv.tag]['containerName'] = "db2" + break; case "mssql": docker.image('mcr.microsoft.com/mssql/server@sha256:f54a84b8a802afdfa91a954e8ddfcec9973447ce8efec519adf593b54d49bedf').pull() sh "./docker_db.sh mssql" state[buildEnv.tag]['containerName'] = "mssql" break; + case "mssql_2017": + docker.image('mcr.microsoft.com/mssql/server@sha256:7d194c54e34cb63bca083542369485c8f4141596805611e84d8c8bab2339eede').pull() + sh "./docker_db.sh mssql_2017" + state[buildEnv.tag]['containerName'] = "mssql" + break; case "sybase": docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { docker.image('nguoianphu/docker-sybase').pull() @@ -164,10 +219,19 @@ stage('Build') { sh "./docker_db.sh sybase" state[buildEnv.tag]['containerName'] = "sybase" break; - case "edb": - docker.image('quay.io/enterprisedb/edb-postgres-advanced:10.22').pull() - sh "./docker_db.sh edb" - state[buildEnv.tag]['containerName'] = "edb" + case "cockroachdb": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('cockroachdb/cockroach:v22.1.10').pull() + } + sh "./docker_db.sh cockroachdb" + state[buildEnv.tag]['containerName'] = "cockroach" + break; + case "cockroachdb_21_2": + docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') { + docker.image('cockroachdb/cockroach:v21.2.16').pull() + } + sh "./docker_db.sh cockroachdb_21_2" + state[buildEnv.tag]['containerName'] = "cockroach" break; } } @@ -176,7 +240,7 @@ stage('Build') { withEnv(["RDBMS=${buildEnv.dbName}"]) { try { if (buildEnv.dbLockableResource == null) { - timeout( [time: buildEnv.longRunning ? 240 : 120, unit: 'MINUTES'] ) { + timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { sh cmd } } @@ -185,7 +249,7 @@ stage('Build') { if ( buildEnv.dbLockResourceAsHost ) { cmd += " -DdbHost=${LOCKED_RESOURCE}" } - timeout( [time: buildEnv.longRunning ? 240 : 120, unit: 'MINUTES'] ) { + timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { sh cmd } } diff --git a/settings.gradle b/settings.gradle index 2e8e9d56c7..76ee5eca72 100644 --- a/settings.gradle +++ b/settings.gradle @@ -160,15 +160,35 @@ dependencyResolutionManagement { alias( "weld" ).to( "org.jboss.weld.se", "weld-se-shaded" ).version( "4.0.1.SP1" ) } dbLibs { - version( "h2", "2.1.210" ) + String h2Version = settings.ext.find( "gradle.libs.versions.h2" ) + if ( h2Version == null ) { + h2Version = "2.1.214" + } + String h2gisVersion = settings.ext.find( "gradle.libs.versions.h2gis" ) + if ( h2gisVersion == null ) { + h2gisVersion = "2.1.0" + } + String hsqldbVersion = settings.ext.find( "gradle.libs.versions.hsqldb" ) + if ( hsqldbVersion == null ) { + hsqldbVersion = "2.7.1" + } + String derbyVersion = settings.ext.find( "gradle.libs.versions.derby" ) + if ( derbyVersion == null ) { + // Latest Derby version 10.16.1.1 only supports JDK 17+, but 10.15.2 should be compatible + derbyVersion = "10.15.2.0" + } + version( "h2", h2Version ) + version( "h2gis", h2gisVersion ) + version( "hsqldb", hsqldbVersion ) + version( "derby", derbyVersion ) version( "pgsql", "42.5.0" ) version( "mysql", "8.0.27" ) version( "oracle", "21.3.0.0" ) alias( "h2" ).to( "com.h2database", "h2" ).versionRef( "h2" ) - alias( "h2gis" ).to( "org.orbisgis", "h2gis" ).version( "2.0.0" ) - alias( "hsqldb" ).to( "org.hsqldb", "hsqldb" ).version( "2.6.1" ) - alias( "derby" ).to( "org.apache.derby", "derby" ).version( "10.14.2.0" ) + alias( "h2gis" ).to( "org.orbisgis", "h2gis" ).versionRef( "h2gis" ) + alias( "hsqldb" ).to( "org.hsqldb", "hsqldb" ).versionRef( "hsqldb" ) + alias( "derby" ).to( "org.apache.derby", "derby" ).versionRef( "derby" ) alias( "postgresql" ).to( "org.postgresql", "postgresql" ).versionRef( "pgsql" ) alias( "cockroachdb" ).to( "org.postgresql", "postgresql" ).version( "42.2.8" ) alias( "mysql" ).to( "mysql", "mysql-connector-java" ).versionRef( "mysql" )