From 1d5b1cf9e91a05a7cb931351f1d76dbb8929a223 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 4 Jul 2025 13:37:25 +0200 Subject: [PATCH 1/2] HHH-18639 Remove support for DB2 versions older than 11.1 and start testing with DB2 12.1 --- ci/build.sh | 4 +- docker_db.sh | 19 +- .../community/dialect/DB2LegacyDialect.java | 144 ++++++++++- .../org/hibernate/dialect/DB2Dialect.java | 240 +++--------------- .../org/hibernate/dialect/DB2iDialect.java | 5 - .../org/hibernate/dialect/DB2zDialect.java | 6 - .../dialect/sql/ast/DB2SqlAstTranslator.java | 40 +-- .../orm/test/dialect/DB2DialectTestCase.java | 10 +- .../src/main/groovy/local.databases.gradle | 10 - nightly.Jenkinsfile | 2 +- 10 files changed, 191 insertions(+), 289 deletions(-) diff --git a/ci/build.sh b/ci/build.sh index 24e80d5cb739..5ecef0cb7612 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -58,10 +58,8 @@ elif [ "$RDBMS" == "oracle_db23c" ]; then export SERVICE=$(echo $INFO | jq -r '.database' | jq -r '.service') # I have no idea why, but these tests don't seem to work on CI... goal="-Pdb=oracle_cloud_db23c -DrunID=$RUNID -DdbHost=$HOST -DdbService=$SERVICE" -elif [ "$RDBMS" == "db2" ]; then +elif [ "$RDBMS" == "db2" ] || [ "$RDBMS" == "db2_11_5" ]; then goal="-Pdb=db2_ci" -elif [ "$RDBMS" == "db2_10_5" ]; then - goal="-Pdb=db2" elif [ "$RDBMS" == "mssql" ] || [ "$RDBMS" == "mssql_2017" ]; then goal="-Pdb=mssql_ci" # Exclude some Sybase tests on CI because they use `xmltable` function which has a memory leak on the DB version in CI diff --git a/docker_db.sh b/docker_db.sh index a409c0b864b7..3d5a530d16a3 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -290,7 +290,7 @@ edb_17() { } db2() { - db2_11_5 + db2_12_1 } db2_11_5() { @@ -306,26 +306,17 @@ db2_11_5() { $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() { +db2_12_1() { $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 ${DB_IMAGE_DB2_10_5:-quay.io/hibernate/db2express-c@sha256:a499afd9709a1f69fb41703e88def9869955234c3525547e2efc3418d1f4ca2b} db2start + $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 ${DB_IMAGE_DB2_11_5:-icr.io/db2_community/db2:12.1.2.0} # Give the container some time to start OUTPUT= - while [[ $OUTPUT != *"DB2START"* ]]; do + while [[ $OUTPUT != *"INSTANCE"* ]]; do echo "Waiting for DB2 to start..." sleep 10 OUTPUT=$($PRIVILEGED_CLI $CONTAINER_CLI logs db2 2>&1) 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'" + $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_spatial() { 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 da0bea1227ff..e52db4d1f4fc 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 @@ -10,7 +10,6 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.community.dialect.sequence.LegacyDB2SequenceSupport; -import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2GetObjectExtractor; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; @@ -484,6 +483,12 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio if ( supportsRecursiveCTE() ) { functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, true ); } + + functionFactory.hex( "hex(?1)" ); + if ( getDB2Version().isSameOrAfter( 11 ) ) { + functionFactory.sha( "hash(?1, 2)" ); + functionFactory.md5( "hash(?1, 0)" ); + } } /** @@ -522,7 +527,7 @@ public long getFractionalSecondPrecisionInNanos() { @Override public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { if ( getDB2Version().isBefore( 11 ) ) { - return DB2Dialect.timestampdiffPatternV10( unit, fromTemporalType, toTemporalType ); + return timestampdiffPatternV10( unit, fromTemporalType, toTemporalType ); } final StringBuilder pattern = new StringBuilder(); final String fromExpression; @@ -558,7 +563,11 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT switch ( unit ) { case NATIVE: case NANOSECOND: - pattern.append( "(seconds_between(" ); + pattern.append( "(seconds_between(date_trunc('second'," ); + pattern.append( toExpression ); + pattern.append( "),date_trunc('second'," ); + pattern.append( fromExpression ); + pattern.append( "))" ); break; //note: DB2 does have weeks_between() case MONTH: @@ -566,14 +575,18 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT // the months_between() function results // in a non-integral value, so trunc() it pattern.append( "trunc(months_between(" ); + pattern.append( toExpression ); + pattern.append( ',' ); + pattern.append( fromExpression ); + pattern.append( ')' ); break; default: pattern.append( "?1s_between(" ); + pattern.append( toExpression ); + pattern.append( ',' ); + pattern.append( fromExpression ); + pattern.append( ')' ); } - pattern.append( toExpression ); - pattern.append( ',' ); - pattern.append( fromExpression ); - pattern.append( ')' ); switch ( unit ) { case NATIVE: pattern.append( "+(microsecond("); @@ -599,6 +612,97 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT return pattern.toString(); } + @SuppressWarnings("deprecation") + public static String timestampdiffPatternV10(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { + final boolean isTime = fromTemporalType == TemporalType.TIME || toTemporalType == TemporalType.TIME; + final String fromExpression; + final String toExpression; + if ( unit.isDateUnit() ) { + if ( fromTemporalType == TemporalType.TIME ) { + fromExpression = "timestamp('1970-01-01',?2)"; + } + else { + fromExpression = "?2"; + } + if ( toTemporalType == TemporalType.TIME ) { + toExpression = "timestamp('1970-01-01',?3)"; + } + else { + toExpression = "?3"; + } + } + else { + if ( fromTemporalType == TemporalType.DATE ) { + fromExpression = "cast(?2 as timestamp)"; + } + else { + fromExpression = "?2"; + } + if ( toTemporalType == TemporalType.DATE ) { + toExpression = "cast(?3 as timestamp)"; + } + else { + toExpression = "?3"; + } + } + switch ( unit ) { + case NATIVE: + if ( isTime ) { + return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))"; + } + else { + 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: + if ( isTime ) { + return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))*1e9"; + } + else { + 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: + if ( isTime ) { + return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))"; + } + else { + return "(select (days(t2)-days(t1))*86400+(midnight_seconds(t2)-midnight_seconds(t1)) " + + "from lateral(values(" + fromExpression + ',' + toExpression + ")) as temp(t1,t2))"; + } + case MINUTE: + if ( isTime ) { + return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))/60"; + } + else { + 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: + if ( isTime ) { + return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))/3600"; + } + else { + 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(); @@ -902,6 +1006,22 @@ public boolean supportsCommentOn() { return true; } + @Override + public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) { + // would need multiple statements to 'set not null'/'drop not null', 'set default'/'drop default', 'set generated', etc + return "alter column " + columnName + " set data type " + columnType; + } + + @Override + public boolean supportsAlterColumnType() { + return getVersion().isSameOrAfter( 10, 5 ); + } + + @Override + public boolean supportsIfExistsBeforeTableName() { + return getVersion().isSameOrAfter( 11, 5 ); + } + @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, @@ -909,6 +1029,11 @@ public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( return new CteMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } + @Override + public boolean supportsIsTrue() { + return getDB2Version().isSameOrAfter( 11 ); + } + @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, @@ -950,6 +1075,11 @@ public boolean supportsLobValueChangePropagation() { return false; } + @Override + public boolean useInputStreamToInsertBlob() { + return false; + } + @Override public boolean doesReadCommittedCauseWritersToBlockReaders() { return true; 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 19bbe7af91d2..35b304630cb7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -15,13 +15,11 @@ 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.function.TrimFunction; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.DB2LimitHandler; -import org.hibernate.dialect.pagination.LegacyDB2LimitHandler; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.DB2SequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; @@ -86,8 +84,6 @@ import org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType; import org.hibernate.type.descriptor.jdbc.OffsetDateTimeJdbcType; import org.hibernate.type.descriptor.jdbc.OffsetTimeJdbcType; -import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType; -import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType; import org.hibernate.type.descriptor.jdbc.XmlJdbcType; import org.hibernate.type.descriptor.jdbc.ZonedDateTimeJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; @@ -118,7 +114,6 @@ import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode; import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BLOB; -import static org.hibernate.type.SqlTypes.BOOLEAN; import static org.hibernate.type.SqlTypes.CLOB; import static org.hibernate.type.SqlTypes.DECIMAL; import static org.hibernate.type.SqlTypes.NUMERIC; @@ -135,7 +130,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos; /** - * A {@linkplain Dialect SQL dialect} for Db2 for LUW (Linux, Unix, and Windows) version 10.5 and above. + * A {@linkplain Dialect SQL dialect} for Db2 for LUW (Linux, Unix, and Windows) version 11.1 and above. *

* Please refer to the * Db2 documentation. @@ -147,7 +142,7 @@ */ public class DB2Dialect extends Dialect { - final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 10, 5 ); + final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 11, 1 ); private static final int BIND_PARAMETERS_NUMBER_LIMIT = 32_767; private static final String FOR_READ_ONLY_SQL = " for read only with rs"; @@ -157,10 +152,7 @@ public class DB2Dialect extends Dialect { private static final String FOR_SHARE_SKIP_LOCKED_SQL = FOR_SHARE_SQL + SKIP_LOCKED_SQL; private static final String FOR_UPDATE_SKIP_LOCKED_SQL = FOR_UPDATE_SQL + SKIP_LOCKED_SQL; - private final LimitHandler limitHandler = - getDB2Version().isBefore( 11, 1 ) - ? LegacyDB2LimitHandler.INSTANCE - : DB2LimitHandler.INSTANCE; + private final LimitHandler limitHandler = DB2LimitHandler.INSTANCE; private final UniqueDelegate uniqueDelegate = createUniqueDelegate(); private final StandardTableExporter db2TableExporter = new StandardTableExporter( this ) { @Override @@ -210,13 +202,6 @@ public int getDefaultStatementBatchSize() { @Override protected String columnType(int sqlTypeCode) { return switch (sqlTypeCode) { - case BOOLEAN -> - // prior to DB2 11, the 'boolean' type existed, - // but was not allowed as a column type - getDB2Version().isBefore( 11 ) - ? "smallint" - : super.columnType( sqlTypeCode ); - case TINYINT -> "smallint"; // no tinyint // HHH-12827: map them both to the same type to avoid problems with schema update @@ -229,17 +214,6 @@ protected String columnType(int sqlTypeCode) { case TIMESTAMP_WITH_TIMEZONE -> "timestamp($p)"; case TIME, TIME_WITH_TIMEZONE -> "time"; - case BINARY -> - // should use 'binary' since version 11 - getDB2Version().isBefore( 11 ) - ? "char($l) for bit data" - : super.columnType( sqlTypeCode ); - case VARBINARY -> - // should use 'varbinary' since version 11 - getDB2Version().isBefore( 11 ) - ? "varchar($l) for bit data" - : super.columnType( sqlTypeCode ); - default -> super.columnType( sqlTypeCode ); }; } @@ -285,14 +259,9 @@ public boolean supportsUserDefinedTypes() { return true; } - @Override - protected boolean supportsPredicateAsExpression() { - return getDB2Version().isSameOrAfter( 11 ); - } - @Override public boolean supportsDistinctFromPredicate() { - return getDB2Version().isSameOrAfter( 11, 1 ); + return true; } @Override @@ -351,33 +320,15 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.regrLinearRegressionAggregates(); functionFactory.variance(); functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); - if ( getDB2Version().isSameOrAfter( 11 ) ) { - functionFactory.position(); - functionFactory.overlayLength_overlay( false ); - functionFactory.median(); - functionFactory.inverseDistributionOrderedSetAggregates(); - functionFactory.stddevPopSamp(); - functionFactory.varPopSamp(); - functionFactory.varianceSamp(); - functionFactory.dateTrunc(); - functionFactory.trunc_dateTrunc(); - } - else { - // Before version 11, the position function required the use of the code units - functionContributions.getFunctionRegistry().register( - "position", - new DB2PositionFunction( functionContributions.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 - functionContributions.getFunctionRegistry().registerAlternateKey( "stddev_pop", "stddev" ); - functionFactory.stddevSamp_sumCount(); - functionContributions.getFunctionRegistry().registerAlternateKey( "var_pop", "variance" ); - functionFactory.varSamp_sumCount(); - functionFactory.trunc_dateTrunc_trunc(); - } + functionFactory.position(); + functionFactory.overlayLength_overlay( false ); + functionFactory.median(); + functionFactory.inverseDistributionOrderedSetAggregates(); + functionFactory.stddevPopSamp(); + functionFactory.varPopSamp(); + functionFactory.varianceSamp(); + functionFactory.dateTrunc(); + functionFactory.trunc_dateTrunc(); functionFactory.addYearsMonthsDaysHoursMinutesSeconds(); functionFactory.yearsMonthsDaysHoursMinutesSecondsBetween(); @@ -446,30 +397,22 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.windowFunctions(); functionFactory.listagg( null ); - if ( getDB2Version().isSameOrAfter( 11 ) ) { - functionFactory.jsonValue_db2(); - functionFactory.jsonQuery_no_passing(); - functionFactory.jsonExists_no_passing(); - functionFactory.jsonObject_db2(); - functionFactory.jsonArray_db2(); - functionFactory.jsonArrayAgg_db2(); - functionFactory.jsonObjectAgg_db2(); - functionFactory.jsonTable_db2( getMaximumSeriesSize() ); - } + functionFactory.jsonValue_db2(); + functionFactory.jsonQuery_no_passing(); + functionFactory.jsonExists_no_passing(); + functionFactory.jsonObject_db2(); + functionFactory.jsonArray_db2(); + functionFactory.jsonArrayAgg_db2(); + functionFactory.jsonObjectAgg_db2(); + functionFactory.jsonTable_db2( getMaximumSeriesSize() ); functionFactory.xmlelement(); functionFactory.xmlcomment(); functionFactory.xmlforest(); functionFactory.xmlconcat(); functionFactory.xmlpi(); - if ( getDB2Version().isSameOrAfter( 11 ) ) { - functionFactory.xmlquery_db2(); - functionFactory.xmlexists(); - } - else { - functionFactory.xmlquery_db2_legacy(); - functionFactory.xmlexists_db2_legacy(); - } + functionFactory.xmlquery_db2(); + functionFactory.xmlexists(); functionFactory.xmlagg(); functionFactory.xmltable_db2(); @@ -477,10 +420,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, true ); functionFactory.hex( "hex(?1)" ); - if ( getDB2Version().isSameOrAfter( 11 ) ) { - functionFactory.sha( "hash(?1, 2)" ); - functionFactory.md5( "hash(?1, 0)" ); - } + functionFactory.sha( "hash(?1, 2)" ); + functionFactory.md5( "hash(?1, 0)" ); } /** @@ -518,9 +459,6 @@ public long getFractionalSecondPrecisionInNanos() { @Override @SuppressWarnings("deprecation") public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { - if ( getDB2Version().isBefore( 11 ) ) { - return timestampdiffPatternV10( unit, fromTemporalType, toTemporalType ); - } final StringBuilder pattern = new StringBuilder(); final String fromExpression; final String toExpression; @@ -592,97 +530,6 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT return pattern.toString(); } - @SuppressWarnings("deprecation") - public static String timestampdiffPatternV10(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { - final boolean isTime = fromTemporalType == TemporalType.TIME || toTemporalType == TemporalType.TIME; - final String fromExpression; - final String toExpression; - if ( unit.isDateUnit() ) { - if ( fromTemporalType == TemporalType.TIME ) { - fromExpression = "timestamp('1970-01-01',?2)"; - } - else { - fromExpression = "?2"; - } - if ( toTemporalType == TemporalType.TIME ) { - toExpression = "timestamp('1970-01-01',?3)"; - } - else { - toExpression = "?3"; - } - } - else { - if ( fromTemporalType == TemporalType.DATE ) { - fromExpression = "cast(?2 as timestamp)"; - } - else { - fromExpression = "?2"; - } - if ( toTemporalType == TemporalType.DATE ) { - toExpression = "cast(?3 as timestamp)"; - } - else { - toExpression = "?3"; - } - } - switch ( unit ) { - case NATIVE: - if ( isTime ) { - return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))"; - } - else { - 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: - if ( isTime ) { - return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))*1e9"; - } - else { - 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: - if ( isTime ) { - return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))"; - } - else { - return "(select (days(t2)-days(t1))*86400+(midnight_seconds(t2)-midnight_seconds(t1)) " + - "from lateral(values(" + fromExpression + ',' + toExpression + ")) as temp(t1,t2))"; - } - case MINUTE: - if ( isTime ) { - return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))/60"; - } - else { - 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: - if ( isTime ) { - return "(midnight_seconds(" + toExpression + ")-midnight_seconds(" + fromExpression + "))/3600"; - } - else { - 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 @SuppressWarnings("deprecation") public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { final StringBuilder pattern = new StringBuilder(); @@ -847,7 +694,7 @@ public String getForUpdateString() { @Override public boolean supportsSkipLocked() { - // Introduced in 11.5: https://www.ibm.com/docs/en/db2/11.5?topic=statement-concurrent-access-resolution-clause + // Introduced in 11.5.4: https://www.ibm.com/docs/en/db2/11.5?topic=statement-concurrent-access-resolution-clause return getDB2Version().isSameOrAfter( 11, 5 ); } @@ -1004,7 +851,7 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( @Override public boolean supportsIsTrue() { - return getDB2Version().isSameOrAfter( 11 ); + return true; } @Override @@ -1062,12 +909,6 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry(); - if ( getDB2Version().isBefore( 11 ) ) { - jdbcTypeRegistry.addDescriptor( Types.BOOLEAN, SmallIntJdbcType.INSTANCE ); - // Binary literals were only added in 11. See https://www.ibm.com/support/knowledgecenter/SSEPGG_11.1.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000731.html#d79816e393 - jdbcTypeRegistry.addDescriptor( Types.VARBINARY, VarbinaryJdbcType.INSTANCE_WITHOUT_LITERALS ); - } - jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( DB2StructJdbcType.INSTANCE ); @@ -1130,9 +971,7 @@ public ValueExtractor getExtractor(JavaType javaType) { @Override public AggregateSupport getAggregateSupport() { - return getDB2Version().isSameOrAfter( 11 ) - ? DB2AggregateSupport.JSON_INSTANCE - : DB2AggregateSupport.INSTANCE; + return DB2AggregateSupport.JSON_INSTANCE; } @Override @@ -1142,13 +981,7 @@ public CallableStatementSupport getCallableStatementSupport() { @Override public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { - if ( getDB2Version().isSameOrAfter( 11 ) ) { - appender.appendSql( "BX'" ); - } - else { - // This should be fine on DB2 prior to 10 - appender.appendSql( "X'" ); - } + appender.appendSql( "BX'" ); PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes ); appender.appendSql( '\'' ); } @@ -1297,12 +1130,7 @@ public String translateExtractField(TemporalUnit unit) { @Override public void appendBooleanValueString(SqlAppender appender, boolean bool) { - if ( getDB2Version().isBefore( 11 ) ) { - appender.appendSql( bool ? '1' : '0' ); - } - else { - appender.appendSql( bool ); - } + appender.appendSql( bool ); } @Override @@ -1318,12 +1146,6 @@ public String extractPattern(TemporalUnit unit) { return "dayofweek(?2)"; case QUARTER: return "quarter(?2)"; - case EPOCH: - if ( getDB2Version().isBefore( 11 ) ) { - return timestampdiffPattern( TemporalUnit.SECOND, TemporalType.TIMESTAMP, TemporalType.TIMESTAMP ) - .replace( "?2", "'1970-01-01 00:00:00'" ) - .replace( "?3", "?2" ); - } } return super.extractPattern( unit ); } @@ -1403,7 +1225,7 @@ public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { @Override public boolean supportsFromClauseInUpdate() { - return getDB2Version().isSameOrAfter( 11 ); + return true; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java index a8f5468bb139..cddfab1504df 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java @@ -96,11 +96,6 @@ public boolean supportsIfExistsBeforeTableName() { return false; } - @Override - public boolean supportsDistinctFromPredicate() { - return true; - } - @Override public boolean supportsUpdateReturning() { // Only supported for insert statements on DB2 for i: https://www.ibm.com/docs/en/i/7.1?topic=clause-table-reference diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 53bb25592a1d..13f9b8c6b1b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -111,12 +111,6 @@ public String getCreateIndexTail(boolean unique, List columns) { return ""; } - @Override - public boolean supportsDistinctFromPredicate() { - // Supported at least since DB2 z/OS 9.0 - return true; - } - @Override public TimeZoneSupport getTimeZoneSupport() { return getVersion().isAfter(10) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2SqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2SqlAstTranslator.java index 6bf6bdf0d8cb..390bc7f188de 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2SqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2SqlAstTranslator.java @@ -29,7 +29,6 @@ import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.from.DerivedTableReference; import org.hibernate.sql.ast.tree.from.FunctionTableReference; @@ -41,7 +40,6 @@ import org.hibernate.sql.ast.tree.insert.ConflictClause; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; 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; @@ -130,12 +128,7 @@ protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List 254", - "varchar(255) for bit data", + "varbinary(255)", actual ); } @@ -84,7 +84,7 @@ public void testIntegerOverflowForMaxResults() { String sql = dialect.getLimitHandler().processSql( "select a.id from tbl_a a order by a.id", rowSelection ); assertTrue( "Integer overflow for max rows in: " + sql, - sql.contains("fetch first 2147483647 rows only") + sql.contains("fetch next ? rows only") ); } } diff --git a/local-build-plugins/src/main/groovy/local.databases.gradle b/local-build-plugins/src/main/groovy/local.databases.gradle index 973b1c36338b..53f68252362e 100644 --- a/local-build-plugins/src/main/groovy/local.databases.gradle +++ b/local-build-plugins/src/main/groovy/local.databases.gradle @@ -309,16 +309,6 @@ ext { // 'jdbc.datasource' : 'com.informix.jdbcx.IfxDataSource', 'connection.init_sql' : '' ], - db2 : [ - 'db.dialect' : 'org.hibernate.dialect.DB2Dialect', - 'jdbc.driver': 'com.ibm.db2.jcc.DB2Driver', - 'jdbc.user' : 'db2inst1', - 'jdbc.pass' : 'db2inst1-pwd', - 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test', - 'jdbc.datasource' : 'com.ibm.db2.jcc.DB2Driver', -// 'jdbc.datasource' : 'com.ibm.db2.jcc.DB2SimpleDataSource', - 'connection.init_sql' : '' - ], db2_ci : [ 'db.dialect' : 'org.hibernate.dialect.DB2Dialect', 'jdbc.driver': 'com.ibm.db2.jcc.DB2Driver', diff --git a/nightly.Jenkinsfile b/nightly.Jenkinsfile index b9720972f1ab..808c5d7a232d 100644 --- a/nightly.Jenkinsfile +++ b/nightly.Jenkinsfile @@ -30,7 +30,7 @@ stage('Configure') { new BuildEnvironment( dbName: 'mariadb_10_6' ), new BuildEnvironment( dbName: 'postgresql_13' ), new BuildEnvironment( dbName: 'edb_13' ), - new BuildEnvironment( dbName: 'db2_10_5', longRunning: true ), + new BuildEnvironment( dbName: 'db2_11_5' ), // Unfortunately there is no SQL Server 11.1 image, but 11.5 should mostly have feature parity 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 new BuildEnvironment( dbName: 'sybase_jconn' ), From 4f1e55abd4ab4f55a484e10eb234c11b76542d07 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 4 Jul 2025 15:20:46 +0200 Subject: [PATCH 2/2] HHH-18641 Remove support for DB2i versions older than 7.2 and modernize some DB2z stuff --- .../dialect/DB2iLegacySqlAstTranslator.java | 6 ++-- .../org/hibernate/dialect/DB2iDialect.java | 21 +++----------- .../org/hibernate/dialect/DB2zDialect.java | 29 ++++--------------- .../dialect/sql/ast/DB2iSqlAstTranslator.java | 23 --------------- .../dialect/sql/ast/DB2zSqlAstTranslator.java | 5 ---- 5 files changed, 13 insertions(+), 71 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacySqlAstTranslator.java index 23403b587513..0b317ad48713 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacySqlAstTranslator.java @@ -40,15 +40,15 @@ protected boolean shouldEmulateFetchClause(QueryPart queryPart) { if ( useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart ) ) { return true; } - // According to LegacyDB2LimitHandler, variable limit also isn't supported before 7.10 - return version.isBefore(7, 10) + // According to LegacyDB2LimitHandler, variable limit also isn't supported before 7.1 + return version.isBefore(7, 1) && queryPart.getFetchClauseExpression() != null && !( queryPart.getFetchClauseExpression() instanceof Literal ); } @Override protected boolean supportsOffsetClause() { - return version.isSameOrAfter(7, 10); + return version.isSameOrAfter(7, 1); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java index cddfab1504df..f3172739b474 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java @@ -6,8 +6,6 @@ import jakarta.persistence.Timeout; import org.hibernate.Timeouts; -import org.hibernate.boot.model.FunctionContributions; -import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.DB2zIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; @@ -32,14 +30,14 @@ import static org.hibernate.type.SqlTypes.ROWID; /** - * A SQL dialect for DB2 for IBM i version 7.1 and above, previously known as "DB2/400". + * A SQL dialect for DB2 for IBM i version 7.2 and above, previously known as "DB2/400". * * @author Peter DeGregorio (pdegregorio) * @author Christian Beikov */ public class DB2iDialect extends DB2Dialect { - private final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 7, 1 ); + private final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 7, 2 ); public final static DatabaseVersion DB2_LUW_VERSION = DB2Dialect.MINIMUM_VERSION; private static final String FOR_UPDATE_SQL = " for update with rs"; @@ -63,17 +61,6 @@ protected DatabaseVersion getMinimumSupportedVersion() { return MINIMUM_VERSION; } - @Override - public void initializeFunctionRegistry(FunctionContributions functionContributions) { - super.initializeFunctionRegistry( functionContributions ); - if ( getVersion().isSameOrAfter( 7, 2 ) ) { - final var functionFactory = new CommonFunctionFactory( functionContributions ); - functionFactory.listagg( null ); - functionFactory.inverseDistributionOrderedSetAggregates(); - functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); - } - } - @Override public DatabaseVersion getDB2Version() { return DB2_LUW_VERSION; @@ -98,8 +85,8 @@ public boolean supportsIfExistsBeforeTableName() { @Override public boolean supportsUpdateReturning() { - // Only supported for insert statements on DB2 for i: https://www.ibm.com/docs/en/i/7.1?topic=clause-table-reference - return false; + // Only supported as of version 7.6: https://www.ibm.com/docs/en/i/7.6.0?topic=clause-table-reference + return getVersion().isSameOrAfter( 7, 6 ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 13f9b8c6b1b2..0d63026c66c7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -5,8 +5,6 @@ package org.hibernate.dialect; -import org.hibernate.boot.model.FunctionContributions; -import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.identity.DB2zIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; @@ -65,28 +63,13 @@ protected DatabaseVersion getMinimumSupportedVersion() { return MINIMUM_VERSION; } - @Override - public void initializeFunctionRegistry(FunctionContributions functionContributions) { - super.initializeFunctionRegistry( functionContributions ); - if ( getVersion().isSameOrAfter( 12 ) ) { - final var functionFactory = new CommonFunctionFactory( functionContributions ); - functionFactory.listagg( null ); - functionFactory.inverseDistributionOrderedSetAggregates(); - functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); - } - } - @Override protected String columnType(int sqlTypeCode) { - if ( getVersion().isAfter( 10 ) ) { - switch ( sqlTypeCode ) { - case TIME_WITH_TIMEZONE: - case TIMESTAMP_WITH_TIMEZONE: - // See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html - return "timestamp with time zone"; - } - } - return super.columnType( sqlTypeCode ); + return switch ( sqlTypeCode ) { + // See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html + case TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE -> "timestamp with time zone"; + default -> super.columnType( sqlTypeCode ); + }; } @Override @@ -113,7 +96,7 @@ public String getCreateIndexTail(boolean unique, List columns) { @Override public TimeZoneSupport getTimeZoneSupport() { - return getVersion().isAfter(10) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; + return TimeZoneSupport.NATIVE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2iSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2iSqlAstTranslator.java index b16659cd09a5..064408fcb80a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2iSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2iSqlAstTranslator.java @@ -9,8 +9,6 @@ import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.exec.spi.JdbcOperation; import static org.hibernate.dialect.DB2iDialect.DB2_LUW_VERSION; @@ -29,27 +27,6 @@ public DB2iSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement this.version = version; } - @Override - protected boolean shouldEmulateFetchClause(QueryPart queryPart) { - // Check if current query part is already row numbering to avoid infinite recursion - 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 7.10 - return version.isBefore(7, 10) - && queryPart.getFetchClauseExpression() != null - && !( queryPart.getFetchClauseExpression() instanceof Literal ); - } - - @Override - protected boolean supportsOffsetClause() { - return version.isSameOrAfter(7, 10); - } - @Override protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { renderComparisonStandard( lhs, operator, rhs ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2zSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2zSqlAstTranslator.java index eb1791c4678c..aafa7174ebf4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2zSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/DB2zSqlAstTranslator.java @@ -36,11 +36,6 @@ protected boolean shouldEmulateFetchClause(QueryPart queryPart) { return getQueryPartForRowNumbering() != queryPart && ( useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart ) ); } - @Override - protected boolean supportsOffsetClause() { - return version.isSameOrAfter(12); - } - @Override protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { // Supported at least since DB2 z/OS 9.0