From 91e62d1268650357d391e6d5637e702c44a2e046 Mon Sep 17 00:00:00 2001 From: liubao Date: Fri, 25 Apr 2025 10:29:56 +0800 Subject: [PATCH 01/33] [HHH-19365]merge works done in https://github.com/SweetWuXiaoMei/hibernate-orm/tree/dev_HHH-19365 --- .github/workflows/ci.yml | 1 + ci/build.sh | 2 + ci/database-start.sh | 2 + docker_db.sh | 44 + .../AbstractGaussDBStructJdbcType.java | 1585 +++++++++++++++++ .../java/org/hibernate/dialect/Database.java | 15 + .../dialect/GaussDBArrayJdbcType.java | 101 ++ .../GaussDBArrayJdbcTypeConstructor.java | 48 + .../dialect/GaussDBCastingInetJdbcType.java | 112 ++ .../GaussDBCastingIntervalSecondJdbcType.java | 154 ++ .../GaussDBCastingJsonArrayJdbcType.java | 41 + ...DBCastingJsonArrayJdbcTypeConstructor.java | 54 + .../dialect/GaussDBCastingJsonJdbcType.java | 60 + .../org/hibernate/dialect/GaussDBDialect.java | 1507 ++++++++++++++++ .../dialect/GaussDBEnumJdbcType.java | 168 ++ .../dialect/GaussDBOrdinalEnumJdbcType.java | 37 + .../dialect/GaussDBSqlAstTranslator.java | 303 ++++ .../dialect/GaussDBStructCastingJdbcType.java | 100 ++ .../dialect/GaussDBUUIDJdbcType.java | 57 + .../aggregate/GaussDBAggregateSupport.java | 630 +++++++ .../function/CommonFunctionFactory.java | 214 +++ .../function/GaussDBFormatFunction.java | 791 ++++++++ .../function/GaussDBMinMaxFunction.java | 116 ++ .../function/GaussDBTruncFunction.java | 64 + .../function/GaussDBTruncRoundFunction.java | 124 ++ .../array/ArrayRemoveIndexUnnestFunction.java | 2 +- .../GaussDBArrayConcatElementFunction.java | 99 + .../array/GaussDBArrayConcatFunction.java | 46 + .../GaussDBArrayConstructorFunction.java | 59 + .../GaussDBArrayContainsOperatorFunction.java | 86 + .../array/GaussDBArrayFillFunction.java | 66 + .../array/GaussDBArrayPositionFunction.java | 45 + .../array/GaussDBArrayPositionsFunction.java | 44 + .../array/GaussDBArrayRemoveFunction.java | 63 + .../GaussDBArrayRemoveIndexFunction.java | 67 + .../array/GaussDBArrayReplaceFunction.java | 53 + .../array/GaussDBArraySetFunction.java | 65 + .../array/GaussDBArrayTrimFunction.java | 39 + .../json/GaussDBJsonArrayAggFunction.java | 63 + .../json/GaussDBJsonArrayAppendFunction.java | 47 + .../json/GaussDBJsonArrayFunction.java | 84 + .../json/GaussDBJsonArrayInsertFunction.java | 48 + .../json/GaussDBJsonExistsFunction.java | 58 + .../json/GaussDBJsonInsertFunction.java | 47 + .../json/GaussDBJsonMergepatchFunction.java | 44 + .../json/GaussDBJsonObjectAggFunction.java | 51 + .../json/GaussDBJsonObjectFunction.java | 73 + .../json/GaussDBJsonQueryFunction.java | 62 + .../json/GaussDBJsonRemoveFunction.java | 50 + .../json/GaussDBJsonReplaceFunction.java | 47 + .../function/json/GaussDBJsonSetFunction.java | 47 + .../json/GaussDBJsonValueFunction.java | 70 + .../function/xml/GaussDBXmlQueryFunction.java | 50 + .../GaussDBIdentityColumnSupport.java | 31 + .../sequence/GaussDBSequenceSupport.java | 41 + .../GaussDBCallableStatementSupport.java | 184 ++ .../org/hibernate/sql/ast/SqlAstWalker.java | 2 + .../sql/ast/spi/AbstractSqlAstTranslator.java | 9 + .../sql/ast/spi/AbstractSqlAstWalker.java | 7 + .../ast/spi/ExpressionReplacementWalker.java | 7 + .../ast/tree/predicate/LessThanPredicate.java | 41 + .../type/descriptor/jdbc/JsonHelper.java | 28 + ...bleWithEntityWithEntityCollectionTest.java | 5 + .../annotations/query/QueryAndSQLTest.java | 4 + .../scanning/PackagedEntityManagerTest.java | 3 + .../test/bootstrap/scanning/ScannerTest.java | 3 + .../lazy/LazyBasicFieldMergeTest.java | 3 + ...izationWithoutInlineDirtyTrackingTest.java | 3 + .../merge/MergeUnsavedEntitiesTest.java | 4 + .../orm/test/cid/CompositeIdAndMergeTest.java | 4 + .../orm/test/filter/FilterParameterTests.java | 2 + .../hql/bitwise/BitwiseFunctionsTest.java | 2 + .../test/jpa/compliance/NamedQueryTest.java | 3 + .../jpa/criteria/basic/ExpressionsTest.java | 2 + .../hibernate/orm/test/jpa/lock/LockTest.java | 3 + ...ativeQueryResultTypeAutoDiscoveryTest.java | 3 + .../query/NativeQueryWithDatetimesTest.java | 2 + .../orm/test/jpa/query/QueryTest.java | 7 + ...hemaDatabaseFileGenerationFailureTest.java | 2 + ...SchemaScriptFileGenerationFailureTest.java | 2 + .../orm/test/lob/LobStringFunctionsTest.java | 6 + .../orm/test/lob/LongByteArrayTest.java | 4 + .../locking/paging/PagingAndLockingTest.java | 4 + .../test/mapping/array/ArrayOfArraysTest.java | 2 + .../test/mapping/basic/BlobByteArrayTest.java | 3 + .../mapping/basic/ByteArrayMappingTests.java | 3 + .../WrapperArrayHandlingLegacyTests.java | 3 + .../test/mapping/basic/XmlMappingTests.java | 4 + .../BidirectionalOneToManyMergeTest.java | 3 + .../orm/test/query/SubQueryInFromTests.java | 3 + ...iteriaBuilderNonStandardFunctionsTest.java | 3 + .../orm/test/query/hql/FunctionTests.java | 14 + .../test/query/hql/InsertConflictTests.java | 8 + ...flictWithCriteriaCopyTreeEnabledTests.java | 3 + .../test/query/hql/IntegerDivisionTest.java | 3 + .../orm/test/query/hql/JsonFunctionTests.java | 7 + .../test/query/hql/LikeEscapeDefaultTest.java | 5 + .../test/query/hql/StandardFunctionTests.java | 2 + .../orm/test/query/hql/XmlFunctionTests.java | 3 + .../hql/set/UnionOfPartitionResultsTest.java | 3 + .../StatelessSessionVersioningTest.java | 6 +- .../orm/test/type/DateArrayTest.java | 7 + .../DetachedEntityWithNullVersionTest.java | 3 + .../BaseTransactionIsolationConfigTest.java | 6 + .../orm/junit/DialectFeatureChecks.java | 2 + .../src/main/groovy/local.databases.gradle | 11 + .../src/main/groovy/local.java-module.gradle | 1 + settings.gradle | 2 + 108 files changed, 8434 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/AbstractGaussDBStructJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcTypeConstructor.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingInetJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingIntervalSecondJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcTypeConstructor.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBEnumJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBOrdinalEnumJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBStructCastingJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/GaussDBUUIDJdbcType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/aggregate/GaussDBAggregateSupport.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBMinMaxFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncRoundFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatElementFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConstructorFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayFillFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionsFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/sequence/GaussDBSequenceSupport.java create mode 100644 hibernate-core/src/main/java/org/hibernate/procedure/internal/GaussDBCallableStatementSupport.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 762469a85469..6f12a415ba5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,7 @@ jobs: - rdbms: mysql - rdbms: mariadb - rdbms: postgresql + - rdbms: gaussdb - rdbms: edb - rdbms: oracle - rdbms: db2 diff --git a/ci/build.sh b/ci/build.sh index 26cc4a067f86..836d4f49e630 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -14,6 +14,8 @@ elif [ "$RDBMS" == "mariadb" ] || [ "$RDBMS" == "mariadb_10_4" ]; then goal="-Pdb=mariadb_ci" elif [ "$RDBMS" == "postgresql" ] || [ "$RDBMS" == "postgresql_13" ]; then goal="-Pdb=pgsql_ci" +elif [ "$RDBMS" == "gaussdb" ]; then + goal="-Pdb=gaussdb -DdbHost=localhost:5432" elif [ "$RDBMS" == "edb" ] || [ "$RDBMS" == "edb_13" ]; then goal="-Pdb=edb_ci -DdbHost=localhost:5444" elif [ "$RDBMS" == "oracle" ]; then diff --git a/ci/database-start.sh b/ci/database-start.sh index da8f3a3e78b6..a6949226d6e8 100755 --- a/ci/database-start.sh +++ b/ci/database-start.sh @@ -8,6 +8,8 @@ elif [ "$RDBMS" == 'mariadb' ]; then bash $DIR/../docker_db.sh mariadb elif [ "$RDBMS" == 'postgresql' ]; then bash $DIR/../docker_db.sh postgresql +elif [ "$RDBMS" == 'gaussdb' ]; then + bash $DIR/../docker_db.sh gaussdb elif [ "$RDBMS" == 'edb' ]; then bash $DIR/../docker_db.sh edb elif [ "$RDBMS" == 'db2' ]; then diff --git a/docker_db.sh b/docker_db.sh index e280694fb29f..3f2c047ee533 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -211,6 +211,49 @@ postgresql_17() { $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-17-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' } +gaussdb() { + $CONTAINER_CLI rm -f opengauss || true + + # config param + CONTAINER_NAME=opengauss + IMAGE=opengauss/opengauss:7.0.0-RC1 + PORT=5432 + DB_USER=hibernate_orm_test + DB_PASSWORD=Hibernate_orm_test@1234 + DB_NAME=hibernate_orm_test + PSQL_IMAGE=postgres:14 + + echo "start OpenGauss container..." + $CONTAINER_CLI run --name ${CONTAINER_NAME} \ + --privileged \ + -e GS_PASSWORD=${DB_PASSWORD} \ + -e GS_NODENAME=opengauss \ + -e GS_PORT=${PORT} \ + -e GS_CGROUP_DISABLE=YES \ + -p ${PORT}:5432 \ + -d ${IMAGE} + + echo "wait OpenGauss starting..." + sleep 20 + + echo " Initialize the database using the PostgreSQL client container..." + + $CONTAINER_CLI run --rm --network=host ${PSQL_IMAGE} \ + bash -c " + PGPASSWORD='${DB_PASSWORD}' psql -h localhost -p ${PORT} -U gaussdb -d postgres -c \"CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';\" && + PGPASSWORD='${DB_PASSWORD}' psql -h localhost -p ${PORT} -U gaussdb -d postgres -c \"CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};\" && + PGPASSWORD='${DB_PASSWORD}' psql -h localhost -p ${PORT} -U gaussdb -d ${DB_NAME} -c \"CREATE SCHEMA test AUTHORIZATION ${DB_USER};\" + " + + echo "Initialization completed" + echo "connection information£º" + echo " Host: localhost" + echo " Port: ${PORT}" + echo " Username: ${DB_USER}" + echo " Password: ${DB_PASSWORD}" + echo " Database: ${DB_NAME}" +} + edb() { edb_17 } @@ -1089,6 +1132,7 @@ if [ -z ${1} ]; then echo -e "\toracle" echo -e "\toracle_23" echo -e "\toracle_21" + echo -e "\tgauss" echo -e "\tpostgresql" echo -e "\tpostgresql_17" echo -e "\tpostgresql_16" diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractGaussDBStructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractGaussDBStructJdbcType.java new file mode 100644 index 000000000000..e0c65ccefd6c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractGaussDBStructJdbcType.java @@ -0,0 +1,1585 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; +import java.lang.reflect.Array; +import java.sql.CallableStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; +import java.util.TimeZone; + +import org.hibernate.internal.util.CharSequenceHelper; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.MappingType; +import org.hibernate.metamodel.mapping.SelectableMapping; +import org.hibernate.metamodel.mapping.ValuedModelPart; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.spi.StringBuilderSqlAppender; +import org.hibernate.type.BasicPluralType; +import org.hibernate.type.BasicType; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.IntegerJavaType; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType; +import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.StructAttributeValues; +import org.hibernate.type.descriptor.jdbc.StructHelper; +import org.hibernate.type.descriptor.jdbc.StructuredJdbcType; +import org.hibernate.type.spi.TypeConfiguration; + +import static org.hibernate.type.descriptor.jdbc.StructHelper.getEmbeddedPart; +import static org.hibernate.type.descriptor.jdbc.StructHelper.instantiate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis; + +/** + * Implementation for serializing/deserializing an embeddable aggregate to/from the GaussDB component format. + * For regular queries, we select the individual struct elements because the GaussDB component format encoding + * is probably not very efficient. + * + * @author liubao + * + * Notes: Original code of this class is based on AbstractPostgreSQLStructJdbcType. + */ +public abstract class AbstractGaussDBStructJdbcType implements StructuredJdbcType { + + private static final DateTimeFormatter LOCAL_DATE_TIME; + static { + LOCAL_DATE_TIME = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(DateTimeFormatter.ISO_LOCAL_DATE) + .appendLiteral(' ') + .append(DateTimeFormatter.ISO_LOCAL_TIME) + .optionalStart() + .appendOffset( "+HH:mm", "+00" ) + .toFormatter(); + } + + // Need a custom formatter for parsing what PostgresPlus/EDB produces + private static final DateTimeFormatter LOCAL_DATE; + static { + LOCAL_DATE = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(DateTimeFormatter.ISO_LOCAL_DATE) + .optionalStart() + .appendLiteral(' ') + .append(DateTimeFormatter.ISO_LOCAL_TIME) + .optionalStart() + .appendOffset( "+HH:mm", "+00" ) + .toFormatter(); + } + private final String typeName; + private final int[] orderMapping; + private final int[] inverseOrderMapping; + private final EmbeddableMappingType embeddableMappingType; + + protected AbstractGaussDBStructJdbcType( + EmbeddableMappingType embeddableMappingType, + String typeName, + int[] orderMapping) { + this.typeName = typeName; + this.embeddableMappingType = embeddableMappingType; + this.orderMapping = orderMapping; + if ( orderMapping == null ) { + this.inverseOrderMapping = null; + } + else { + final int[] inverseOrderMapping = new int[orderMapping.length]; + for ( int i = 0; i < orderMapping.length; i++ ) { + inverseOrderMapping[orderMapping[i]] = i; + } + this.inverseOrderMapping = inverseOrderMapping; + } + } + + @Override + public int getJdbcTypeCode() { + return SqlTypes.STRUCT; + } + + @Override + public String getStructTypeName() { + return typeName; + } + + @Override + public EmbeddableMappingType getEmbeddableMappingType() { + return embeddableMappingType; + } + + @Override + public JavaType getJdbcRecommendedJavaTypeMapping( + Integer precision, + Integer scale, + TypeConfiguration typeConfiguration) { + if ( embeddableMappingType == null ) { + return typeConfiguration.getJavaTypeRegistry().getDescriptor( Object[].class ); + } + else { + //noinspection unchecked + return (JavaType) embeddableMappingType.getMappedJavaType(); + } + } + + @Override + public ValueExtractor getExtractor(JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return getObject( rs.getObject( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return getObject( statement.getObject( index ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) + throws SQLException { + return getObject( statement.getObject( name ), options ); + } + + private X getObject(Object object, WrapperOptions options) throws SQLException { + if ( object == null ) { + return null; + } + return ( (AbstractGaussDBStructJdbcType) getJdbcType() ).fromString( + object.toString(), + getJavaType(), + options + ); + } + }; + } + + protected X fromString(String string, JavaType javaType, WrapperOptions options) throws SQLException { + if ( string == null ) { + return null; + } + final boolean returnEmbeddable = javaType.getJavaTypeClass() != Object[].class; + final int end; + final Object[] array; + if ( embeddableMappingType == null ) { + assert !returnEmbeddable; + final ArrayList values = new ArrayList<>( 8 ); + end = deserializeStruct( string, 0, string.length() - 1, values ); + array = values.toArray(); + } + else { + array = new Object[embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 )]; + end = deserializeStruct( string, 0, 0, array, returnEmbeddable, options ); + } + assert end == string.length(); + if ( returnEmbeddable ) { + final StructAttributeValues attributeValues = getAttributeValues( embeddableMappingType, orderMapping, array, options ); + //noinspection unchecked + return (X) instantiate( embeddableMappingType, attributeValues ); + } + else if ( inverseOrderMapping != null ) { + StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, array.clone(), array ); + } + //noinspection unchecked + return (X) array; + } + + private int deserializeStruct( + String string, + int begin, + int end, + ArrayList values) { + int column = 0; + boolean inQuote = false; + boolean hasEscape = false; + assert string.charAt( begin ) == '('; + int start = begin + 1; + int element = 1; + for ( int i = start; i < string.length(); i++ ) { + final char c = string.charAt( i ); + switch ( c ) { + case '"': + if ( inQuote ) { + if ( i + 1 != end && string.charAt( i + 1 ) == '"' ) { + // Skip double quotes as that will be unescaped later + i++; + hasEscape = true; + continue; + } + if ( hasEscape ) { + values.add( unescape( string, start, i ) ); + } + else { + values.add( string.substring( start, i ) ); + } + column++; + inQuote = false; + } + else { + inQuote = true; + } + hasEscape = false; + start = i + 1; + break; + case ',': + if ( !inQuote ) { + if ( column < element ) { + if ( start == i ) { + values.add( null ); + } + else { + values.add( string.substring( start, i ) ); + } + column++; + } + start = i + 1; + element++; + } + break; + case ')': + if ( !inQuote ) { + if ( column < element ) { + if ( start == i ) { + values.add( null ); + } + else { + values.add( string.substring( start, i ) ); + } + } + return i + 1; + } + break; + } + } + + throw new IllegalArgumentException( "Struct not properly formed: " + string.subSequence( start, end ) ); + } + + private int deserializeStruct( + String string, + int begin, + int quotes, + Object[] values, + boolean returnEmbeddable, + WrapperOptions options) throws SQLException { + int column = 0; + boolean inQuote = false; + StringBuilder escapingSb = null; + assert string.charAt( begin ) == '('; + int start = begin + 1; + for ( int i = start; i < string.length(); i++ ) { + final char c = string.charAt( i ); + switch ( c ) { + case '\\': + if ( inQuote ) { + final int expectedQuoteCount = 1 << quotes; + if ( repeatsChar( string, i, expectedQuoteCount, '\\' ) ) { + if ( isDoubleQuote( string, i + expectedQuoteCount, expectedQuoteCount ) ) { + // Skip quote escaping as that will be unescaped later + if ( escapingSb == null ) { + escapingSb = new StringBuilder(); + } + escapingSb.append( string, start, i ); + escapingSb.append( '"' ); + // Move forward to the last quote + i += expectedQuoteCount + expectedQuoteCount - 1; + start = i + 1; + continue; + } + else { + assert repeatsChar( string, i + expectedQuoteCount, expectedQuoteCount, '\\' ); + // Don't create an escaping string builder for binary literals + if ( i != start || !isBinary( column ) ) { + // Skip quote escaping as that will be unescaped later + if ( escapingSb == null ) { + escapingSb = new StringBuilder(); + } + escapingSb.append( string, start, i ); + escapingSb.append( '\\' ); + start = i + expectedQuoteCount + expectedQuoteCount; + } + // Move forward to the last backslash + i += expectedQuoteCount + expectedQuoteCount - 1; + continue; + } + } + } + // Fall-through since a backslash is an escaping mechanism for a start quote within arrays + case '"': + if ( inQuote ) { + if ( isDoubleQuote( string, i, 1 << ( quotes + 1 ) ) ) { + // Skip quote escaping as that will be unescaped later + if ( escapingSb == null ) { + escapingSb = new StringBuilder(); + } + escapingSb.append( string, start, i ); + escapingSb.append( '"' ); + // Move forward to the last quote + i += ( 1 << ( quotes + 1 ) ) - 1; + start = i + 1; + continue; + } + assert isDoubleQuote( string, i, 1 << quotes ); + final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { + case SqlTypes.DATE: + values[column] = fromRawObject( + jdbcMapping, + parseDate( + CharSequenceHelper.subSequence( + string, + start, + i + ) + ), + options + ); + break; + case SqlTypes.TIME: + case SqlTypes.TIME_WITH_TIMEZONE: + case SqlTypes.TIME_UTC: + values[column] = fromRawObject( + jdbcMapping, + parseTime( + CharSequenceHelper.subSequence( + string, + start, + i + ) + ), + options + ); + break; + case SqlTypes.TIMESTAMP: + values[column] = fromRawObject( + jdbcMapping, + parseTimestamp( + CharSequenceHelper.subSequence( + string, + start, + i + ), + jdbcMapping.getJdbcJavaType() + ), + options + ); + break; + case SqlTypes.TIMESTAMP_WITH_TIMEZONE: + case SqlTypes.TIMESTAMP_UTC: + values[column] = fromRawObject( + jdbcMapping, + parseTimestampWithTimeZone( + CharSequenceHelper.subSequence( + string, + start, + i + ), + jdbcMapping.getJdbcJavaType() + ), + options + ); + break; + case SqlTypes.BINARY: + case SqlTypes.VARBINARY: + case SqlTypes.LONGVARBINARY: + case SqlTypes.LONG32VARBINARY: + final int backslashes = 1 << ( quotes + 1 ); + assert repeatsChar( string, start, backslashes, '\\' ); + final int xCharPosition = start + backslashes; + assert string.charAt( xCharPosition ) == 'x'; + values[column] = fromString( + jdbcMapping, + string, + xCharPosition + 1, + i + ); + break; + default: + if ( escapingSb == null || escapingSb.length() == 0 ) { + values[column] = fromString( + jdbcMapping, + string, + start, + i + ); + } + else { + escapingSb.append( string, start, i ); + values[column] = fromString( + jdbcMapping, + escapingSb, + 0, + escapingSb.length() + ); + escapingSb.setLength( 0 ); + } + break; + } + column++; + inQuote = false; + // move forward the index by 2 ^ quoteLevel to point to the next char after the quote + i += 1 << quotes; + if ( string.charAt( i ) == ')' ) { + // Return the end position if this is the last element + assert column == values.length; + return i + 1; + } + // at this point, we must see a comma to indicate the next element + assert string.charAt( i ) == ','; + } + else { + // This is a start quote, so move forward the index to the last quote + final int expectedQuotes = 1 << quotes; + assert isDoubleQuote( string, i, expectedQuotes ); + i += expectedQuotes - 1; + if ( string.charAt( i + 1 ) == '(' ) { + // This could be a nested struct + final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + if ( jdbcMapping.getJdbcType() instanceof AbstractGaussDBStructJdbcType structJdbcType ) { + final Object[] subValues = new Object[structJdbcType.embeddableMappingType.getJdbcValueCount()]; + final int subEnd = structJdbcType.deserializeStruct( + string, + i + 1, + quotes + 1, + subValues, + returnEmbeddable, + options + ); + if ( returnEmbeddable ) { + final StructAttributeValues attributeValues = structJdbcType.getAttributeValues( + structJdbcType.embeddableMappingType, + structJdbcType.orderMapping, + subValues, + options + ); + values[column] = instantiate( structJdbcType.embeddableMappingType, attributeValues ); + } + else { + if ( structJdbcType.inverseOrderMapping != null ) { + StructHelper.orderJdbcValues( + structJdbcType.embeddableMappingType, + structJdbcType.inverseOrderMapping, + subValues.clone(), + subValues + ); + } + values[column] = subValues; + } + column++; + // The subEnd points to the first character after the ')', + // so move forward the index to point to the next char after quotes + assert isDoubleQuote( string, subEnd, expectedQuotes ); + i = subEnd + expectedQuotes; + if ( string.charAt( i ) == ')' ) { + // Return the end position if this is the last element + assert column == values.length; + return i + 1; + } + // at this point, we must see a comma to indicate the next element + assert string.charAt( i ) == ','; + } + else { + inQuote = true; + } + } + else if ( string.charAt( i + 1 ) == '{' ) { + // This could be a quoted array + final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + if ( jdbcMapping instanceof BasicPluralType pluralType ) { + final ArrayList arrayList = new ArrayList<>(); + //noinspection unchecked + final int subEnd = deserializeArray( + string, + i + 1, + quotes + 1, + arrayList, + (BasicType) pluralType.getElementType(), + returnEmbeddable, + options + ); + assert string.charAt( subEnd - 1 ) == '}'; + values[column] = pluralType.getJdbcJavaType().wrap( arrayList, options ); + column++; + // The subEnd points to the first character after the ')', + // so move forward the index to point to the next char after quotes + assert isDoubleQuote( string, subEnd, expectedQuotes ); + i = subEnd + expectedQuotes; + if ( string.charAt( i ) == ')' ) { + // Return the end position if this is the last element + assert column == values.length; + return i + 1; + } + // at this point, we must see a comma to indicate the next element + assert string.charAt( i ) == ','; + } + else { + inQuote = true; + } + } + else { + inQuote = true; + } + } + start = i + 1; + break; + case ',': + if ( !inQuote ) { + if ( start == i ) { + values[column] = null; + } + else { + final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + if ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() == SqlTypes.BOOLEAN ) { + values[column] = fromRawObject( + jdbcMapping, + string.charAt( start ) == 't', + options + ); + } + else if ( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass().isEnum() + && jdbcMapping.getJdbcType().isInteger() ) { + values[column] = fromRawObject( + jdbcMapping, + IntegerJavaType.INSTANCE.fromEncodedString( string, start, i ), + options + ); + } + else { + values[column] = fromString( + jdbcMapping, + string, + start, + i + ); + } + } + column++; + start = i + 1; + } + break; + case ')': + if ( !inQuote ) { + if ( column < values.length ) { + if ( start == i ) { + values[column] = null; + } + else { + final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + if ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() == SqlTypes.BOOLEAN ) { + values[column] = fromRawObject( + jdbcMapping, + string.charAt( start ) == 't', + options + ); + } + else if ( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass().isEnum() + && jdbcMapping.getJdbcType().isInteger() ) { + values[column] = fromRawObject( + jdbcMapping, + IntegerJavaType.INSTANCE.fromEncodedString( string, start, i ), + options + ); + } + else { + values[column] = fromString( + jdbcMapping, + string, + start, + i + ); + } + } + } + return i + 1; + } + break; + case '{': + if ( !inQuote ) { + final BasicPluralType pluralType = (BasicPluralType) getJdbcValueSelectable( column ).getJdbcMapping(); + final ArrayList arrayList = new ArrayList<>(); + //noinspection unchecked + i = deserializeArray( + string, + i, + quotes + 1, + arrayList, + (BasicType) pluralType.getElementType(), + returnEmbeddable, + options + ); + assert string.charAt( i - 1 ) == '}'; + values[column] = pluralType.getJdbcJavaType().wrap( arrayList, options ); + column++; + if ( string.charAt( i ) == ')' ) { + // Return the end position if this is the last element + assert column == values.length; + return i + 1; + } + // at this point, we must see a comma to indicate the next element + assert string.charAt( i ) == ','; + start = i + 1; + } + break; + } + } + + throw new IllegalArgumentException( "Struct not properly formed: " + string.substring( start ) ); + } + + private boolean isBinary(int column) { + return isBinary( getJdbcValueSelectable( column ).getJdbcMapping() ); + } + + private static boolean isBinary(JdbcMapping jdbcMapping) { + switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { + case SqlTypes.BINARY: + case SqlTypes.VARBINARY: + case SqlTypes.LONGVARBINARY: + case SqlTypes.LONG32VARBINARY: + return true; + } + return false; + } + + private int deserializeArray( + String string, + int begin, + int quotes, + ArrayList values, + BasicType elementType, + boolean returnEmbeddable, + WrapperOptions options) throws SQLException { + boolean inQuote = false; + StringBuilder escapingSb = null; + assert string.charAt( begin ) == '{'; + int start = begin + 1; + for ( int i = start; i < string.length(); i++ ) { + final char c = string.charAt( i ); + switch ( c ) { + case '\\': + if ( inQuote ) { + final int expectedQuoteCount = 1 << quotes; + if ( repeatsChar( string, i, expectedQuoteCount, '\\' ) ) { + if ( isDoubleQuote( string, i + expectedQuoteCount, expectedQuoteCount ) ) { + // Skip quote escaping as that will be unescaped later + if ( escapingSb == null ) { + escapingSb = new StringBuilder(); + } + escapingSb.append( string, start, i ); + escapingSb.append( '"' ); + // Move forward to the last quote + i += expectedQuoteCount + expectedQuoteCount - 1; + start = i + 1; + continue; + } + else { + assert repeatsChar( string, i + expectedQuoteCount, expectedQuoteCount, '\\' ); + // Don't create an escaping string builder for binary literals + if ( i != start || !isBinary( elementType ) ) { + // Skip quote escaping as that will be unescaped later + if ( escapingSb == null ) { + escapingSb = new StringBuilder(); + } + escapingSb.append( string, start, i ); + escapingSb.append( '\\' ); + start = i + expectedQuoteCount + expectedQuoteCount; + } + // Move forward to the last backslash + i += expectedQuoteCount + expectedQuoteCount - 1; + continue; + } + } + } + // Fall-through since a backslash is an escaping mechanism for a start quote within arrays + case '"': + if ( inQuote ) { + if ( isDoubleQuote( string, i, 1 << ( quotes + 1 ) ) ) { + // Skip quote escaping as that will be unescaped later + if ( escapingSb == null ) { + escapingSb = new StringBuilder(); + } + escapingSb.append( string, start, i ); + escapingSb.append( '"' ); + // Move forward to the last quote + i += ( 1 << ( quotes + 1 ) ) - 1; + start = i + 1; + continue; + } + assert isDoubleQuote( string, i, 1 << quotes ); + switch ( elementType.getJdbcType().getDefaultSqlTypeCode() ) { + case SqlTypes.DATE: + values.add( + fromRawObject( + elementType, + parseDate( + CharSequenceHelper.subSequence( + string, + start, + i + ) + ), + options + ) + ); + break; + case SqlTypes.TIME: + case SqlTypes.TIME_WITH_TIMEZONE: + case SqlTypes.TIME_UTC: + values.add( + fromRawObject( + elementType, + parseTime( + CharSequenceHelper.subSequence( + string, + start, + i + ) + ), + options + ) + ); + break; + case SqlTypes.TIMESTAMP: + values.add( + fromRawObject( + elementType, + parseTimestamp( + CharSequenceHelper.subSequence( + string, + start, + i + ), + elementType.getJdbcJavaType() + ), + options + ) + ); + break; + case SqlTypes.TIMESTAMP_WITH_TIMEZONE: + case SqlTypes.TIMESTAMP_UTC: + values.add( + fromRawObject( + elementType, + parseTimestampWithTimeZone( + CharSequenceHelper.subSequence( + string, + start, + i + ), + elementType.getJdbcJavaType() + ), + options + ) + ); + break; + case SqlTypes.BINARY: + case SqlTypes.VARBINARY: + case SqlTypes.LONGVARBINARY: + case SqlTypes.LONG32VARBINARY: + final int backslashes = 1 << ( quotes + 1 ); + assert repeatsChar( string, start, backslashes, '\\' ); + final int xCharPosition = start + backslashes; + assert string.charAt( xCharPosition ) == 'x'; + values.add( + fromString( + elementType, + string, + xCharPosition + 1, + i + ) + ); + break; + default: + if ( escapingSb == null || escapingSb.length() == 0 ) { + values.add( + fromString( + elementType, + string, + start, + i + ) + ); + } + else { + escapingSb.append( string, start, i ); + values.add( + fromString( + elementType, + escapingSb, + 0, + escapingSb.length() + ) + ); + escapingSb.setLength( 0 ); + } + break; + } + inQuote = false; + // move forward the index by 2 ^ quotes to point to the next char after the quote + i += 1 << quotes; + if ( string.charAt( i ) == '}' ) { + // Return the end position if this is the last element + return i + 1; + } + // at this point, we must see a comma to indicate the next element + assert string.charAt( i ) == ','; + } + else { + // This is a start quote, so move forward the index to the last quote + final int expectedQuotes = 1 << quotes; + assert isDoubleQuote( string, i, expectedQuotes ); + i += expectedQuotes - 1; + if ( string.charAt( i + 1 ) == '(' ) { + // This could be a nested struct + if ( elementType.getJdbcType() instanceof AbstractGaussDBStructJdbcType structJdbcType ) { + final Object[] subValues = new Object[structJdbcType.embeddableMappingType.getJdbcValueCount()]; + final int subEnd = structJdbcType.deserializeStruct( + string, + i + 1, + quotes + 1, + subValues, + returnEmbeddable, + options + ); + if ( returnEmbeddable ) { + final StructAttributeValues attributeValues = structJdbcType.getAttributeValues( + structJdbcType.embeddableMappingType, + structJdbcType.orderMapping, + subValues, + options + ); + values.add( instantiate( structJdbcType.embeddableMappingType, attributeValues ) ); + } + else { + if ( structJdbcType.inverseOrderMapping != null ) { + StructHelper.orderJdbcValues( + structJdbcType.embeddableMappingType, + structJdbcType.inverseOrderMapping, + subValues.clone(), + subValues + ); + } + values.add( subValues ); + } + // The subEnd points to the first character after the '}', + // so move forward the index to point to the next char after quotes + assert isDoubleQuote( string, subEnd, expectedQuotes ); + i = subEnd + expectedQuotes; + if ( string.charAt( i ) == '}' ) { + // Return the end position if this is the last element + return i + 1; + } + // at this point, we must see a comma to indicate the next element + assert string.charAt( i ) == ','; + } + else { + inQuote = true; + } + } + else { + inQuote = true; + } + } + start = i + 1; + switch ( elementType.getJdbcType().getDefaultSqlTypeCode() ) { + case SqlTypes.BINARY: + case SqlTypes.VARBINARY: + case SqlTypes.LONGVARBINARY: + case SqlTypes.LONG32VARBINARY: + // Skip past the backslashes in the binary literal, this will be handled later + final int backslashes = 1 << ( quotes + 1 ); + assert repeatsChar( string, start, backslashes, '\\' ); + i += backslashes; + break; + } + break; + case ',': + if ( !inQuote ) { + if ( start == i ) { + values.add( null ); + } + else { + if ( elementType.getJdbcType().getDefaultSqlTypeCode() == SqlTypes.BOOLEAN ) { + values.add( + fromRawObject( + elementType, + string.charAt( start ) == 't', + options + ) + ); + } + else if ( elementType.getJavaTypeDescriptor().getJavaTypeClass().isEnum() + && elementType.getJdbcType().isInteger() ) { + values.add( + fromRawObject( + elementType, + IntegerJavaType.INSTANCE.fromEncodedString( string, start, i ), + options + ) + ); + } + else { + values.add( + fromString( + elementType, + string, + start, + i + ) + ); + } + } + start = i + 1; + } + break; + case '}': + if ( !inQuote ) { + if ( start == i ) { + values.add( null ); + } + else { + if ( elementType.getJdbcType().getDefaultSqlTypeCode() == SqlTypes.BOOLEAN ) { + values.add( + fromRawObject( + elementType, + string.charAt( start ) == 't', + options + ) + ); + } + else if ( elementType.getJavaTypeDescriptor().getJavaTypeClass().isEnum() + && elementType.getJdbcType().isInteger() ) { + values.add( + fromRawObject( + elementType, + IntegerJavaType.INSTANCE.fromEncodedString( string, start, i ), + options + ) + ); + } + else { + values.add( + fromString( + elementType, + string, + start, + i + ) + ); + } + } + return i + 1; + } + break; + } + } + + throw new IllegalArgumentException( "Array not properly formed: " + string.substring( start ) ); + } + + private SelectableMapping getJdbcValueSelectable(int jdbcValueSelectableIndex) { + if ( orderMapping != null ) { + final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); + final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); + int count = 0; + for ( int i = 0; i < size; i++ ) { + final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, orderMapping[i] ); + if ( modelPart.getMappedType() instanceof EmbeddableMappingType embeddableMappingType ) { + final SelectableMapping aggregateMapping = embeddableMappingType.getAggregateMapping(); + if ( aggregateMapping == null ) { + final SelectableMapping subSelectable = embeddableMappingType.getJdbcValueSelectable( jdbcValueSelectableIndex - count ); + if ( subSelectable != null ) { + return subSelectable; + } + count += embeddableMappingType.getJdbcValueCount(); + } + else { + if ( count == jdbcValueSelectableIndex ) { + return aggregateMapping; + } + count++; + } + } + else { + if ( count == jdbcValueSelectableIndex ) { + return (SelectableMapping) modelPart; + } + count += modelPart.getJdbcTypeCount(); + } + } + return null; + } + return embeddableMappingType.getJdbcValueSelectable( jdbcValueSelectableIndex ); + } + + private static boolean repeatsChar(String string, int start, int times, char expectedChar) { + final int end = start + times; + if ( end < string.length() ) { + for ( ; start < end; start++ ) { + if ( string.charAt( start ) != expectedChar ) { + return false; + } + } + return true; + } + return false; + } + + private static boolean isDoubleQuote(String string, int start, int escapes) { + if ( escapes == 1 ) { + return string.charAt( start ) == '"'; + } + assert ( escapes & 1 ) == 0 : "Only an even number of escapes allowed"; + final int end = start + escapes; + if ( end < string.length() ) { + for ( ; start < end; start += 2 ) { + final char c1 = string.charAt( start ); + final char c2 = string.charAt( start + 1 ); + switch ( c1 ) { + case '\\': + // After a backslash, another backslash or a double quote may follow + if ( c2 != '\\' && c2 != '"' ) { + return false; + } + break; + case '"': + // After a double quote, only another double quote may follow + if ( c2 != '"' ) { + return false; + } + break; + default: + return false; + } + } + return string.charAt( end - 1 ) == '"'; + } + return false; + } + + private Object fromString( + int selectableIndex, + String string, + int start, + int end) { + return fromString( + getJdbcValueSelectable( selectableIndex ).getJdbcMapping(), + string, + start, + end + ); + } + + private static Object fromString(JdbcMapping jdbcMapping, CharSequence charSequence, int start, int end) { + return jdbcMapping.getJdbcJavaType().fromEncodedString( + charSequence, + start, + end + ); + } + + private static Object fromRawObject(JdbcMapping jdbcMapping, Object raw, WrapperOptions options) { + return jdbcMapping.getJdbcJavaType().wrap( + raw, + options + ); + } + + private Object parseDate(CharSequence subSequence) { + return LOCAL_DATE.parse( subSequence, LocalDate::from ); + } + + private Object parseTime(CharSequence subSequence) { + return DateTimeFormatter.ISO_LOCAL_TIME.parse( subSequence, LocalTime::from ); + } + + private Object parseTimestamp(CharSequence subSequence, JavaType jdbcJavaType) { + final TemporalAccessor temporalAccessor = LOCAL_DATE_TIME.parse( subSequence ); + final LocalDateTime localDateTime = LocalDateTime.from( temporalAccessor ); + final Timestamp timestamp = Timestamp.valueOf( localDateTime ); + timestamp.setNanos( temporalAccessor.get( ChronoField.NANO_OF_SECOND ) ); + return timestamp; + } + + private Object parseTimestampWithTimeZone(CharSequence subSequence, JavaType jdbcJavaType) { + final TemporalAccessor temporalAccessor = LOCAL_DATE_TIME.parse( subSequence ); + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + if ( jdbcJavaType.getJavaTypeClass() == Instant.class ) { + return Instant.from( temporalAccessor ); + } + else { + return OffsetDateTime.from( temporalAccessor ); + } + } + return LocalDateTime.from( temporalAccessor ); + } + + private static String unescape(CharSequence string, int start, int end) { + StringBuilder sb = new StringBuilder( end - start ); + for ( int i = start; i < end; i++ ) { + final char c = string.charAt( i ); + if ( c == '\\' || c == '"' ) { + i++; + sb.append( string.charAt( i ) ); + continue; + } + sb.append( c ); + } + return sb.toString(); + } + + @Override + public Object createJdbcValue(Object domainValue, WrapperOptions options) throws SQLException { + assert embeddableMappingType != null; + final StringBuilder sb = new StringBuilder(); + serializeStructTo( new PostgreSQLAppender( sb ), domainValue, options ); + return sb.toString(); + } + + @Override + public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) throws SQLException { + assert embeddableMappingType != null; + final Object[] array = new Object[embeddableMappingType.getJdbcValueCount()]; + deserializeStruct( getRawStructFromJdbcValue( rawJdbcValue ), 0, 0, array, true, options ); + if ( inverseOrderMapping != null ) { + StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, array.clone(), array ); + } + return array; + } + + protected String getRawStructFromJdbcValue(Object rawJdbcValue) { + return rawJdbcValue.toString(); + } + + protected String toString(X value, JavaType javaType, WrapperOptions options) throws SQLException { + if ( value == null ) { + return null; + } + final StringBuilder sb = new StringBuilder(); + serializeStructTo( new PostgreSQLAppender( sb ), value, options ); + return sb.toString(); + } + + private void serializeStructTo(PostgreSQLAppender appender, Object value, WrapperOptions options) throws SQLException { + serializeDomainValueTo( appender, options, value, '(' ); + appender.append( ')' ); + } + + private void serializeDomainValueTo( + PostgreSQLAppender appender, + WrapperOptions options, + Object domainValue, + char separator) throws SQLException { + serializeJdbcValuesTo( + appender, + options, + StructHelper.getJdbcValues( embeddableMappingType, orderMapping, domainValue, options ), + separator + ); + } + + private void serializeJdbcValuesTo( + PostgreSQLAppender appender, + WrapperOptions options, + Object[] jdbcValues, + char separator) throws SQLException { + for ( int i = 0; i < jdbcValues.length; i++ ) { + appender.append( separator ); + separator = ','; + final Object jdbcValue = jdbcValues[i]; + if ( jdbcValue == null ) { + continue; + } + final SelectableMapping selectableMapping = orderMapping == null ? + embeddableMappingType.getJdbcValueSelectable( i ) : + embeddableMappingType.getJdbcValueSelectable( orderMapping[i] ); + final JdbcMapping jdbcMapping = selectableMapping.getJdbcMapping(); + if ( jdbcMapping.getJdbcType() instanceof AbstractGaussDBStructJdbcType structJdbcType ) { + appender.quoteStart(); + structJdbcType.serializeJdbcValuesTo( + appender, + options, + (Object[]) jdbcValue, + '(' + ); + appender.append( ')' ); + appender.quoteEnd(); + } + else { + serializeConvertedBasicTo( appender, options, jdbcMapping, jdbcValue ); + } + } + } + + private void serializeConvertedBasicTo( + PostgreSQLAppender appender, + WrapperOptions options, + JdbcMapping jdbcMapping, + Object subValue) throws SQLException { + //noinspection unchecked + final JavaType jdbcJavaType = (JavaType) jdbcMapping.getJdbcJavaType(); + switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { + case SqlTypes.TINYINT: + case SqlTypes.SMALLINT: + case SqlTypes.INTEGER: + if ( subValue instanceof Boolean booleanValue ) { + // BooleanJavaType has this as an implicit conversion + appender.append( booleanValue ? '1' : '0' ); + break; + } + if ( subValue instanceof Enum enumValue ) { + appender.appendSql( enumValue.ordinal() ); + break; + } + case SqlTypes.BOOLEAN: + case SqlTypes.BIT: + case SqlTypes.BIGINT: + case SqlTypes.FLOAT: + case SqlTypes.REAL: + case SqlTypes.DOUBLE: + case SqlTypes.DECIMAL: + case SqlTypes.NUMERIC: + case SqlTypes.DURATION: + appender.append( subValue.toString() ); + break; + case SqlTypes.CHAR: + case SqlTypes.NCHAR: + case SqlTypes.VARCHAR: + case SqlTypes.NVARCHAR: + if ( subValue instanceof Boolean booleanValue ) { + // BooleanJavaType has this as an implicit conversion + appender.append( booleanValue ? 'Y' : 'N' ); + break; + } + case SqlTypes.LONGVARCHAR: + case SqlTypes.LONGNVARCHAR: + case SqlTypes.LONG32VARCHAR: + case SqlTypes.LONG32NVARCHAR: + case SqlTypes.ENUM: + case SqlTypes.NAMED_ENUM: + appender.quoteStart(); + appender.append( (String) subValue ); + appender.quoteEnd(); + break; + case SqlTypes.DATE: + case SqlTypes.TIME: + case SqlTypes.TIME_WITH_TIMEZONE: + case SqlTypes.TIME_UTC: + case SqlTypes.TIMESTAMP: + case SqlTypes.TIMESTAMP_WITH_TIMEZONE: + case SqlTypes.TIMESTAMP_UTC: + appendTemporal( appender, jdbcMapping, subValue, options ); + break; + case SqlTypes.BINARY: + case SqlTypes.VARBINARY: + case SqlTypes.LONGVARBINARY: + case SqlTypes.LONG32VARBINARY: + final byte[] bytes = jdbcJavaType.unwrap( + subValue, + byte[].class, + options + ); + appender.ensureCanFit( appender.quote + 1 + ( bytes.length << 1 ) ); + appender.append( '\\' ); + appender.append( '\\' ); + appender.append( 'x' ); + PrimitiveByteArrayJavaType.INSTANCE.appendString( + appender, + bytes + ); + break; + case SqlTypes.UUID: + appender.append( subValue.toString() ); + break; + case SqlTypes.ARRAY: + if ( subValue != null ) { + final int length = Array.getLength( subValue ); + if ( length == 0 ) { + appender.append( "{}" ); + } + else { + //noinspection unchecked + final BasicType elementType = ((BasicPluralType) jdbcMapping).getElementType(); + appender.quoteStart(); + appender.append( '{' ); + Object arrayElement = Array.get( subValue, 0 ); + if ( arrayElement == null ) { + appender.appendNull(); + } + else { + serializeConvertedBasicTo( appender, options, elementType, arrayElement ); + } + for ( int i = 1; i < length; i++ ) { + arrayElement = Array.get( subValue, i ); + appender.append( ',' ); + if ( arrayElement == null ) { + appender.appendNull(); + } + else { + serializeConvertedBasicTo( appender, options, elementType, arrayElement ); + } + } + + appender.append( '}' ); + appender.quoteEnd(); + } + } + break; + case SqlTypes.STRUCT: + if ( subValue != null ) { + final AbstractGaussDBStructJdbcType structJdbcType = (AbstractGaussDBStructJdbcType) jdbcMapping.getJdbcType(); + appender.quoteStart(); + structJdbcType.serializeJdbcValuesTo( appender, options, (Object[]) subValue, '(' ); + appender.append( ')' ); + appender.quoteEnd(); + } + break; + default: + throw new UnsupportedOperationException( "Unsupported JdbcType nested in struct: " + jdbcMapping.getJdbcType() ); + } + } + + private StructAttributeValues getAttributeValues( + EmbeddableMappingType embeddableMappingType, + int[] orderMapping, + Object[] rawJdbcValues, + WrapperOptions options) throws SQLException { + final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); + final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); + final StructAttributeValues attributeValues = new StructAttributeValues( + numberOfAttributeMappings, + orderMapping != null ? + null : + rawJdbcValues + ); + int jdbcIndex = 0; + for ( int i = 0; i < size; i++ ) { + final int attributeIndex; + if ( orderMapping == null ) { + attributeIndex = i; + } + else { + attributeIndex = orderMapping[i]; + } + jdbcIndex += injectAttributeValue( + getEmbeddedPart( embeddableMappingType, attributeIndex ), + attributeValues, + attributeIndex, + rawJdbcValues, + jdbcIndex, + options + ); + } + return attributeValues; + } + + private int injectAttributeValue( + ValuedModelPart modelPart, + StructAttributeValues attributeValues, + int attributeIndex, + Object[] rawJdbcValues, + int jdbcIndex, + WrapperOptions options) throws SQLException { + final MappingType mappedType = modelPart.getMappedType(); + final int jdbcValueCount; + final Object rawJdbcValue = rawJdbcValues[jdbcIndex]; + if ( mappedType instanceof EmbeddableMappingType embeddableMappingType ) { + if ( embeddableMappingType.getAggregateMapping() != null ) { + jdbcValueCount = 1; + attributeValues.setAttributeValue( attributeIndex, rawJdbcValue ); + } + else { + jdbcValueCount = embeddableMappingType.getJdbcValueCount(); + final Object[] subJdbcValues = new Object[jdbcValueCount]; + System.arraycopy( rawJdbcValues, jdbcIndex, subJdbcValues, 0, subJdbcValues.length ); + final StructAttributeValues subValues = getAttributeValues( + embeddableMappingType, + null, + subJdbcValues, + options + ); + attributeValues.setAttributeValue( attributeIndex, instantiate( embeddableMappingType, subValues ) ); + } + } + else { + assert modelPart.getJdbcTypeCount() == 1; + jdbcValueCount = 1; + final JdbcMapping jdbcMapping = modelPart.getSingleJdbcMapping(); + final Object jdbcValue = jdbcMapping.getJdbcJavaType().wrap( + rawJdbcValue, + options + ); + attributeValues.setAttributeValue( attributeIndex, jdbcMapping.convertToDomainValue( jdbcValue ) ); + } + return jdbcValueCount; + } + + private void appendTemporal(SqlAppender appender, JdbcMapping jdbcMapping, Object value, WrapperOptions options) { + final TimeZone jdbcTimeZone = getJdbcTimeZone( options ); + //noinspection unchecked + final JavaType javaType = (JavaType) jdbcMapping.getJdbcJavaType(); + appender.append( '"' ); + switch ( jdbcMapping.getJdbcType().getJdbcTypeCode() ) { + case SqlTypes.DATE: + if ( value instanceof java.util.Date date ) { + appendAsDate( appender, date ); + } + else if ( value instanceof java.util.Calendar calendar ) { + appendAsDate( appender, calendar ); + } + else if ( value instanceof TemporalAccessor temporalAccessor ) { + appendAsDate( appender, temporalAccessor ); + } + else { + appendAsDate( + appender, + javaType.unwrap( value, java.util.Date.class, options ) + ); + } + break; + case SqlTypes.TIME: + case SqlTypes.TIME_WITH_TIMEZONE: + case SqlTypes.TIME_UTC: + if ( value instanceof java.util.Date date ) { + appendAsTime( appender, date, jdbcTimeZone ); + } + else if ( value instanceof java.util.Calendar calendar ) { + appendAsTime( appender, calendar, jdbcTimeZone ); + } + else if ( value instanceof TemporalAccessor temporalAccessor ) { + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appendAsTime( appender, temporalAccessor, true, jdbcTimeZone ); + } + else { + appendAsLocalTime( appender, temporalAccessor ); + } + } + else { + appendAsTime( + appender, + javaType.unwrap( value, java.sql.Time.class, options ), + jdbcTimeZone + ); + } + break; + case SqlTypes.TIMESTAMP: + case SqlTypes.TIMESTAMP_WITH_TIMEZONE: + case SqlTypes.TIMESTAMP_UTC: + if ( value instanceof java.util.Date date ) { + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + } + else if ( value instanceof java.util.Calendar calendar ) { + appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone ); + } + else if ( value instanceof TemporalAccessor temporalAccessor ) { + appendAsTimestampWithMicros( appender, temporalAccessor, temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ), jdbcTimeZone ); + } + else { + appendAsTimestampWithMicros( + appender, + javaType.unwrap( value, java.util.Date.class, options ), + jdbcTimeZone + ); + } + break; + default: + throw new IllegalArgumentException(); + } + + appender.append( '"' ); + } + private static TimeZone getJdbcTimeZone(WrapperOptions options) { + return options == null || options.getJdbcTimeZone() == null + ? TimeZone.getDefault() + : options.getJdbcTimeZone(); + } + + protected Object getBindValue(X value, WrapperOptions options) throws SQLException { + return StructHelper.getJdbcValues( embeddableMappingType, orderMapping, value, options ); + } + + private static class PostgreSQLAppender extends StringBuilderSqlAppender { + + private int quote = 1; + + public PostgreSQLAppender(StringBuilder sb) { + super( sb ); + } + + public void quoteStart() { + append( '"' ); + quote = quote << 1; + } + + public void quoteEnd() { + quote = quote >> 1; + append( '"' ); + } + + public void appendNull() { + sb.append( "NULL" ); + } + + @Override + public PostgreSQLAppender append(char fragment) { + if ( quote != 1 ) { + appendWithQuote( fragment ); + } + else { + sb.append( fragment ); + } + return this; + } + + @Override + public PostgreSQLAppender append(CharSequence csq) { + return append( csq, 0, csq.length() ); + } + + @Override + public PostgreSQLAppender append(CharSequence csq, int start, int end) { + if ( quote != 1 ) { + int len = end - start; + sb.ensureCapacity( sb.length() + len ); + for ( int i = start; i < end; i++ ) { + appendWithQuote( csq.charAt( i ) ); + } + } + else { + sb.append( csq, start, end ); + } + return this; + } + + private void appendWithQuote(char fragment) { + if ( fragment == '"' || fragment == '\\' ) { + sb.ensureCapacity( sb.length() + quote ); + for ( int i = 1; i < quote; i++ ) { + sb.append( '\\' ); + } + } + sb.append( fragment ); + } + + public void ensureCanFit(int lengthIncrease) { + sb.ensureCapacity( sb.length() + lengthIncrease ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Database.java b/hibernate-core/src/main/java/org/hibernate/dialect/Database.java index cb24d2ea2839..3f727ac4c82f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Database.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Database.java @@ -79,6 +79,21 @@ public String getUrlPrefix() { } }, + GAUSSDB { + @Override + public Dialect createDialect(DialectResolutionInfo info) { + return new GaussDBDialect(info); + } + @Override + public boolean productNameMatches(String databaseName) { + return "GaussDB".equals( databaseName ); + } + @Override + public String getDriverClassName(String jdbcUrl) { + return "com.huawei.gaussdb.jdbc.Driver"; + } + }, + H2 { @Override public Dialect createDialect(DialectResolutionInfo info) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcType.java new file mode 100644 index 000000000000..c6f531ec3f7a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcType.java @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.BasicPluralJavaType; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; +import org.hibernate.type.descriptor.jdbc.ArrayJdbcType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +/** + * Descriptor for {@link Types#ARRAY ARRAY} handling. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayJdbcType. + */ +public class GaussDBArrayJdbcType extends ArrayJdbcType { + + public GaussDBArrayJdbcType(JdbcType elementJdbcType) { + super( elementJdbcType ); + } + + @Override + public ValueBinder getBinder(final JavaType javaTypeDescriptor) { + @SuppressWarnings("unchecked") + final BasicPluralJavaType pluralJavaType = (BasicPluralJavaType) javaTypeDescriptor; + final ValueBinder elementBinder = getElementJdbcType().getBinder( pluralJavaType.getElementJavaType() ); + return new BasicBinder<>( javaTypeDescriptor, this ) { + + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { + st.setArray( index, getArray( value, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + final java.sql.Array arr = getArray( value, options ); + try { + st.setObject( name, arr, java.sql.Types.ARRAY ); + } + catch (SQLException ex) { + throw new HibernateException( "JDBC driver does not support named parameters for setArray. Use positional.", ex ); + } + } + + @Override + public Object getBindValue(X value, WrapperOptions options) throws SQLException { + return ( (GaussDBArrayJdbcType) getJdbcType() ).getArray( this, elementBinder, value, options ); + } + + private java.sql.Array getArray(X value, WrapperOptions options) throws SQLException { + final GaussDBArrayJdbcType arrayJdbcType = (GaussDBArrayJdbcType) getJdbcType(); + final Object[] objects; + + final JdbcType elementJdbcType = arrayJdbcType.getElementJdbcType(); + if ( elementJdbcType instanceof AggregateJdbcType ) { + // The GaussDB JDBC driver does not support arrays of structs, which contain byte[] + final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) elementJdbcType; + final Object[] domainObjects = getJavaType().unwrap( + value, + Object[].class, + options + ); + objects = new Object[domainObjects.length]; + for ( int i = 0; i < domainObjects.length; i++ ) { + if ( domainObjects[i] != null ) { + objects[i] = aggregateJdbcType.createJdbcValue( domainObjects[i], options ); + } + } + } + else { + objects = arrayJdbcType.getArray( this, elementBinder, value, options ); + } + + final SharedSessionContractImplementor session = options.getSession(); + final String typeName = arrayJdbcType.getElementTypeName( getJavaType(), session ); + return session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection() + .createArrayOf( typeName, objects ); + } + }; + } + + @Override + public String toString() { + return "GaussDBArrayTypeDescriptor(" + getElementJdbcType().toString() + ")"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcTypeConstructor.java new file mode 100644 index 000000000000..b37553dc3657 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBArrayJdbcTypeConstructor.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import java.sql.Types; + +import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; +import org.hibernate.type.BasicType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * Factory for {@link GaussDBArrayJdbcType}. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayJdbcTypeConstructor. + */ +public class GaussDBArrayJdbcTypeConstructor implements JdbcTypeConstructor { + + public static final GaussDBArrayJdbcTypeConstructor INSTANCE = new GaussDBArrayJdbcTypeConstructor(); + + @Override + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + BasicType elementType, + ColumnTypeInformation columnTypeInformation) { + return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation ); + } + + @Override + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + JdbcType elementType, + ColumnTypeInformation columnTypeInformation) { + return new GaussDBArrayJdbcType( elementType ); + } + + @Override + public int getDefaultSqlTypeCode() { + return Types.ARRAY; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingInetJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingInetJdbcType.java new file mode 100644 index 000000000000..f76ecd18bfc9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingInetJdbcType.java @@ -0,0 +1,112 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import java.net.InetAddress; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +/** + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLCastingInetJdbcType. + */ +public class GaussDBCastingInetJdbcType implements JdbcType { + + public static final GaussDBCastingInetJdbcType INSTANCE = new GaussDBCastingInetJdbcType(); + + @Override + public void appendWriteExpression( + String writeExpression, + SqlAppender appender, + Dialect dialect) { + appender.append( "cast(" ); + appender.append( writeExpression ); + appender.append( " as inet)" ); + } + + @Override + public int getJdbcTypeCode() { + return SqlTypes.VARBINARY; + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.INET; + } + + @Override + public String toString() { + return "InetSecondJdbcType"; + } + + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + // No literal support for now + return null; + } + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setString( index, getStringValue( value, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setString( name, getStringValue( value, options ) ); + } + + private String getStringValue(X value, WrapperOptions options) { + return getJavaType().unwrap( value, InetAddress.class, options ).getHostAddress(); + } + }; + } + + @Override + public ValueExtractor getExtractor(JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return getObject( rs.getString( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return getObject( statement.getString( index ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException { + return getObject( statement.getString( name ), options ); + } + + private X getObject(String inetString, WrapperOptions options) throws SQLException { + if ( inetString == null ) { + return null; + } + return getJavaType().wrap( inetString, options ); + } + }; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingIntervalSecondJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingIntervalSecondJdbcType.java new file mode 100644 index 000000000000..d64a82cfe8d3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingIntervalSecondJdbcType.java @@ -0,0 +1,154 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +/** + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLCastingIntervalSecondJdbcType. + */ +public class GaussDBCastingIntervalSecondJdbcType implements AdjustableJdbcType { + + public static final GaussDBCastingIntervalSecondJdbcType INSTANCE = new GaussDBCastingIntervalSecondJdbcType(); + + @Override + public JdbcType resolveIndicatedType(JdbcTypeIndicators indicators, JavaType domainJtd) { + final int scale = indicators.getColumnScale() == JdbcTypeIndicators.NO_COLUMN_SCALE + ? domainJtd.getDefaultSqlScale( indicators.getDialect(), this ) + : indicators.getColumnScale(); + if ( scale > 6 ) { + // Since the maximum allowed scale on GaussDB is 6 (microsecond precision), + // we have to switch to the numeric type if the value is greater + return indicators.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( SqlTypes.NUMERIC ); + } + else { + return this; + } + } + + @Override + public Expression wrapTopLevelSelectionExpression(Expression expression) { + return new SelfRenderingExpression() { + @Override + public void renderToSql( + SqlAppender sqlAppender, + SqlAstTranslator walker, + SessionFactoryImplementor sessionFactory) { + sqlAppender.append( "extract(epoch from " ); + expression.accept( walker ); + sqlAppender.append( ')' ); + } + + @Override + public JdbcMappingContainer getExpressionType() { + return expression.getExpressionType(); + } + }; + } + + @Override + public void appendWriteExpression( + String writeExpression, + SqlAppender appender, + Dialect dialect) { + appender.append( '(' ); + appender.append( writeExpression ); + appender.append( "*interval'1 second')" ); + } + + @Override + public int getJdbcTypeCode() { + return SqlTypes.NUMERIC; + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.INTERVAL_SECOND; + } + + @Override + public String toString() { + return "IntervalSecondJdbcType"; + } + + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + // No literal support for now + return null; + } + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setBigDecimal( index, getBigDecimalValue( value, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setBigDecimal( name, getBigDecimalValue( value, options ) ); + } + + private BigDecimal getBigDecimalValue(X value, WrapperOptions options) { + return getJavaType().unwrap( value, BigDecimal.class, options ).movePointLeft( 9 ); + } + }; + } + + @Override + public ValueExtractor getExtractor(JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return getObject( rs.getBigDecimal( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return getObject( statement.getBigDecimal( index ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException { + return getObject( statement.getBigDecimal( name ), options ); + } + + private X getObject(BigDecimal bigDecimal, WrapperOptions options) throws SQLException { + if ( bigDecimal == null ) { + return null; + } + return getJavaType().wrap( bigDecimal.movePointRight( 9 ), options ); + } + }; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcType.java new file mode 100644 index 000000000000..dc7e855a1eac --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcType.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType; + +/** + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLCastingJsonArrayJdbcType. + */ +public class GaussDBCastingJsonArrayJdbcType extends JsonArrayJdbcType { + + private final boolean jsonb; + + public GaussDBCastingJsonArrayJdbcType(JdbcType elementJdbcType, boolean jsonb) { + super( elementJdbcType ); + this.jsonb = jsonb; + } + + @Override + public void appendWriteExpression( + String writeExpression, + SqlAppender appender, + Dialect dialect) { + appender.append( "cast(" ); + appender.append( writeExpression ); + appender.append( " as " ); + if ( jsonb ) { + appender.append( "jsonb)" ); + } + else { + appender.append( "json)" ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcTypeConstructor.java new file mode 100644 index 000000000000..e0ec3c638ec4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonArrayJdbcTypeConstructor.java @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; +import org.hibernate.type.BasicType; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * Factory for {@link GaussDBCastingJsonArrayJdbcType}. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLCastingJsonArrayJdbcTypeConstructor. + */ +public class GaussDBCastingJsonArrayJdbcTypeConstructor implements JdbcTypeConstructor { + + public static final GaussDBCastingJsonArrayJdbcTypeConstructor JSONB_INSTANCE = new GaussDBCastingJsonArrayJdbcTypeConstructor( true ); + public static final GaussDBCastingJsonArrayJdbcTypeConstructor JSON_INSTANCE = new GaussDBCastingJsonArrayJdbcTypeConstructor( false ); + + private final boolean jsonb; + + public GaussDBCastingJsonArrayJdbcTypeConstructor(boolean jsonb) { + this.jsonb = jsonb; + } + + @Override + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + BasicType elementType, + ColumnTypeInformation columnTypeInformation) { + return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation ); + } + + @Override + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + JdbcType elementType, + ColumnTypeInformation columnTypeInformation) { + return new GaussDBCastingJsonArrayJdbcType( elementType, jsonb ); + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.JSON_ARRAY; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonJdbcType.java new file mode 100644 index 000000000000..00f22fe669c2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBCastingJsonJdbcType.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; +import org.hibernate.type.descriptor.jdbc.JsonJdbcType; + +/** + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLCastingJsonJdbcType. + */ +public class GaussDBCastingJsonJdbcType extends JsonJdbcType { + + public static final GaussDBCastingJsonJdbcType JSON_INSTANCE = new GaussDBCastingJsonJdbcType( false, null ); + public static final GaussDBCastingJsonJdbcType JSONB_INSTANCE = new GaussDBCastingJsonJdbcType( true, null ); + + private final boolean jsonb; + + public GaussDBCastingJsonJdbcType(boolean jsonb, EmbeddableMappingType embeddableMappingType) { + super( embeddableMappingType ); + this.jsonb = jsonb; + } + + @Override + public int getDdlTypeCode() { + return SqlTypes.JSON; + } + + @Override + public AggregateJdbcType resolveAggregateJdbcType( + EmbeddableMappingType mappingType, + String sqlType, + RuntimeModelCreationContext creationContext) { + return new GaussDBCastingJsonJdbcType( jsonb, mappingType ); + } + + @Override + public void appendWriteExpression( + String writeExpression, + SqlAppender appender, + Dialect dialect) { + appender.append( "cast(" ); + appender.append( writeExpression ); + appender.append( " as " ); + if ( jsonb ) { + appender.append( "jsonb)" ); + } + else { + appender.append( "json)" ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java new file mode 100644 index 000000000000..ac399819b5ea --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -0,0 +1,1507 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.Length; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.PessimisticLockException; +import org.hibernate.QueryTimeoutException; +import org.hibernate.boot.model.FunctionContributions; +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.dialect.aggregate.AggregateSupport; +import org.hibernate.dialect.aggregate.GaussDBAggregateSupport; +import org.hibernate.dialect.function.CommonFunctionFactory; +import org.hibernate.dialect.function.GaussDBMinMaxFunction; +import org.hibernate.dialect.function.GaussDBTruncFunction; +import org.hibernate.dialect.function.GaussDBTruncRoundFunction; +import org.hibernate.dialect.identity.IdentityColumnSupport; +import org.hibernate.dialect.identity.GaussDBIdentityColumnSupport; +import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; +import org.hibernate.dialect.sequence.GaussDBSequenceSupport; +import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.dialect.unique.CreateTableUniqueDelegate; +import org.hibernate.dialect.unique.UniqueDelegate; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; +import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.exception.LockAcquisitionException; +import org.hibernate.exception.spi.SQLExceptionConversionDelegate; +import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; +import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; +import org.hibernate.internal.util.JdbcExceptionHelper; +import org.hibernate.mapping.AggregateColumn; +import org.hibernate.mapping.Table; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.persister.entity.mutation.EntityMutationTarget; +import org.hibernate.procedure.internal.GaussDBCallableStatementSupport; +import org.hibernate.procedure.spi.CallableStatementSupport; +import org.hibernate.query.SemanticException; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.sqm.CastType; +import org.hibernate.query.common.FetchClauseType; +import org.hibernate.query.sqm.IntervalType; +import org.hibernate.query.common.TemporalUnit; +import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.exec.spi.JdbcOperation; +import org.hibernate.sql.model.MutationOperation; +import org.hibernate.sql.model.internal.OptionalTableUpdate; +import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation; +import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; +import org.hibernate.tool.schema.internal.StandardTableExporter; +import org.hibernate.tool.schema.spi.Exporter; +import org.hibernate.type.JavaObjectType; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; +import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType; +import org.hibernate.type.descriptor.jdbc.XmlJdbcType; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; +import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl; +import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType; +import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl; +import org.hibernate.type.descriptor.sql.internal.NamedNativeEnumDdlTypeImpl; +import org.hibernate.type.descriptor.sql.internal.NamedNativeOrdinalEnumDdlTypeImpl; +import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType; +import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; +import org.hibernate.type.spi.TypeConfiguration; + +import jakarta.persistence.GenerationType; +import jakarta.persistence.TemporalType; + +import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; +import static org.hibernate.query.common.TemporalUnit.DAY; +import static org.hibernate.query.common.TemporalUnit.EPOCH; +import static org.hibernate.type.SqlTypes.ARRAY; +import static org.hibernate.type.SqlTypes.BINARY; +import static org.hibernate.type.SqlTypes.CHAR; +import static org.hibernate.type.SqlTypes.FLOAT; +import static org.hibernate.type.SqlTypes.GEOGRAPHY; +import static org.hibernate.type.SqlTypes.GEOMETRY; +import static org.hibernate.type.SqlTypes.INET; +import static org.hibernate.type.SqlTypes.JSON; +import static org.hibernate.type.SqlTypes.LONG32NVARCHAR; +import static org.hibernate.type.SqlTypes.LONG32VARBINARY; +import static org.hibernate.type.SqlTypes.LONG32VARCHAR; +import static org.hibernate.type.SqlTypes.NCHAR; +import static org.hibernate.type.SqlTypes.NCLOB; +import static org.hibernate.type.SqlTypes.NVARCHAR; +import static org.hibernate.type.SqlTypes.OTHER; +import static org.hibernate.type.SqlTypes.SQLXML; +import static org.hibernate.type.SqlTypes.STRUCT; +import static org.hibernate.type.SqlTypes.TIME; +import static org.hibernate.type.SqlTypes.TIMESTAMP; +import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC; +import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE; +import static org.hibernate.type.SqlTypes.TIME_UTC; +import static org.hibernate.type.SqlTypes.TINYINT; +import static org.hibernate.type.SqlTypes.UUID; +import static org.hibernate.type.SqlTypes.VARBINARY; +import static org.hibernate.type.SqlTypes.VARCHAR; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis; + +/** + * A {@linkplain Dialect SQL dialect} for GaussDB V2.0-8.201 and above. + *

+ * Please refer to the + * GaussDB documentation. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLDialect. + */ +public class GaussDBDialect extends Dialect { + protected final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 2 ); + + private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this); + private final StandardTableExporter gaussDBTableExporter = new StandardTableExporter( this ) { + @Override + protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggregateColumn) { + final JdbcType jdbcType = aggregateColumn.getType().getJdbcType(); + if ( jdbcType.isXml() ) { + // Requires the use of xmltable which is not supported in check constraints + return; + } + super.applyAggregateColumnCheck( buf, aggregateColumn ); + } + }; + + private final OptionalTableUpdateStrategy optionalTableUpdateStrategy; + + public GaussDBDialect() { + this(MINIMUM_VERSION); + } + + public GaussDBDialect(DialectResolutionInfo info) { + this( info.makeCopyOrDefault( MINIMUM_VERSION )); + registerKeywords( info ); + } + + public GaussDBDialect(DatabaseVersion version) { + super( version ); + this.optionalTableUpdateStrategy = determineOptionalTableUpdateStrategy( version ); + } + + public boolean supportsColumnCheck() { + return false; + } + + private static OptionalTableUpdateStrategy determineOptionalTableUpdateStrategy(DatabaseVersion version) { + return version.isSameOrAfter( DatabaseVersion.make( 15, 0 ) ) + ? GaussDBDialect::usingMerge + : GaussDBDialect::withoutMerge; + } + + @Override + protected DatabaseVersion getMinimumSupportedVersion() { + return MINIMUM_VERSION; + } + + @Override + public boolean getDefaultNonContextualLobCreation() { + return true; + } + + @Override + protected String columnType(int sqlTypeCode) { + return switch (sqlTypeCode) { + // no tinyint + case TINYINT -> "smallint"; + + // there are no nchar/nvarchar types + case NCHAR -> columnType( CHAR ); + case NVARCHAR -> columnType( VARCHAR ); + + case LONG32VARCHAR, LONG32NVARCHAR -> "text"; + case NCLOB -> "clob"; + + case BINARY, VARBINARY, LONG32VARBINARY -> "bytea"; + + case TIMESTAMP_UTC -> columnType( TIMESTAMP_WITH_TIMEZONE ); + + default -> super.columnType( sqlTypeCode ); + }; + } + + @Override + protected String castType(int sqlTypeCode) { + return switch (sqlTypeCode) { + case CHAR, NCHAR, VARCHAR, NVARCHAR -> "varchar"; + case LONG32VARCHAR, LONG32NVARCHAR -> "text"; + case NCLOB -> "clob"; + case BINARY, VARBINARY, LONG32VARBINARY -> "bytea"; + default -> super.castType( sqlTypeCode ); + }; + } + + @Override + protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + super.registerColumnTypes( typeContributions, serviceRegistry ); + final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry(); + + // We need to configure that the array type uses the raw element type for casts + ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, true ) ); + + // Register this type to be able to support Float[] + // The issue is that the JDBC driver can't handle createArrayOf( "float(24)", ... ) + // It requires the use of "real" or "float4" + // Alternatively we could introduce a new API in Dialect for creating such base names + ddlTypeRegistry.addDescriptor( + CapacityDependentDdlType.builder( FLOAT, columnType( FLOAT ), castType( FLOAT ), this ) + .withTypeCapacity( 24, "float4" ) + .build() + ); + + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uuid", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) ); + ddlTypeRegistry.addDescriptor( new Scale6IntervalSecondDdlType( this ) ); + + // Prefer jsonb if possible + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) ); + + ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) ); + ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) ); + } + + @Override + public int getMaxVarcharLength() { + return 10_485_760; + } + + @Override + public int getMaxVarcharCapacity() { + // 1GB-85-4 according to GaussDB docs + return 1_073_741_727; + } + + @Override + public int getMaxVarbinaryLength() { + //has no varbinary-like type + return Length.LONG32; + } + + @Override + public int getDefaultStatementBatchSize() { + return 15; + } + + @Override + public JdbcType resolveSqlTypeDescriptor( + String columnTypeName, + int jdbcTypeCode, + int precision, + int scale, + JdbcTypeRegistry jdbcTypeRegistry) { + switch ( jdbcTypeCode ) { + case OTHER: + switch ( columnTypeName ) { + case "uuid": + jdbcTypeCode = UUID; + break; + case "json": + case "jsonb": + jdbcTypeCode = JSON; + break; + case "xml": + jdbcTypeCode = SQLXML; + break; + case "inet": + jdbcTypeCode = INET; + break; + case "geometry": + jdbcTypeCode = GEOMETRY; + break; + case "geography": + jdbcTypeCode = GEOGRAPHY; + break; + } + break; + case TIME: + // The GaussDB JDBC driver reports TIME for timetz, but we use it only for mapping OffsetTime to UTC + if ( "timetz".equals( columnTypeName ) ) { + jdbcTypeCode = TIME_UTC; + } + break; + case TIMESTAMP: + // The GaussDB JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant + if ( "timestamptz".equals( columnTypeName ) ) { + jdbcTypeCode = TIMESTAMP_UTC; + } + break; + case ARRAY: + // GaussDB names array types by prepending an underscore to the base name + if ( columnTypeName.charAt( 0 ) == '_' ) { + final String componentTypeName = columnTypeName.substring( 1 ); + final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() ); + if ( sqlTypeCode != null ) { + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, + jdbcTypeRegistry.getDescriptor( sqlTypeCode ), + ColumnTypeInformation.EMPTY + ); + } + final SqlTypedJdbcType elementDescriptor = jdbcTypeRegistry.findSqlTypedDescriptor( componentTypeName ); + if ( elementDescriptor != null ) { + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, + elementDescriptor, + ColumnTypeInformation.EMPTY + ); + } + } + break; + case STRUCT: + final SqlTypedJdbcType descriptor = jdbcTypeRegistry.findSqlTypedDescriptor( + // Skip the schema + columnTypeName.substring( columnTypeName.indexOf( '.' ) + 1 ) + ); + if ( descriptor != null ) { + return descriptor; + } + break; + } + return jdbcTypeRegistry.getDescriptor( jdbcTypeCode ); + } + + @Override + protected Integer resolveSqlTypeCode(String columnTypeName, TypeConfiguration typeConfiguration) { + return switch (columnTypeName) { + case "bool" -> Types.BOOLEAN; + case "float4" -> Types.REAL; // Use REAL instead of FLOAT to get Float as recommended Java type + case "float8" -> Types.DOUBLE; + case "int2" -> Types.SMALLINT; + case "int4" -> Types.INTEGER; + case "int8" -> Types.BIGINT; + default -> super.resolveSqlTypeCode( columnTypeName, typeConfiguration ); + }; + } + + @Override + public String getEnumTypeDeclaration(String name, String[] values) { + return name; + } + + @Override + public String[] getCreateEnumTypeCommand(String name, String[] values) { + StringBuilder type = new StringBuilder(); + type.append( "create type " ) + .append( name ) + .append( " as enum (" ); + String separator = ""; + for ( String value : values ) { + type.append( separator ).append('\'').append( value ).append('\''); + separator = ","; + } + type.append( ')' ); + String cast1 = "create cast (varchar as " + + name + + ") with inout as implicit"; + String cast2 = "create cast (" + + name + + " as varchar) with inout as implicit"; + return new String[] { type.toString(), cast1, cast2 }; + } + + @Override + public String[] getDropEnumTypeCommand(String name) { + return new String[] { "drop type if exists " + name + " cascade" }; + } + + @Override + public String currentTime() { + return "localtime"; + } + + @Override + public String currentTimestamp() { + return "localtimestamp"; + } + + @Override + public String currentTimestampWithTimeZone() { + return "current_timestamp"; + } + + /** + * The {@code extract()} function returns {@link TemporalUnit#DAY_OF_WEEK} + * numbered from 0 to 6. This isn't consistent with what most other + * databases do, so here we adjust the result by generating + * {@code (extract(dow,arg)+1))}. + */ + @Override + public String extractPattern(TemporalUnit unit) { + return switch (unit) { + case DAY_OF_WEEK -> "(" + super.extractPattern( unit ) + "+1)"; + default -> super.extractPattern(unit); + }; + } + + @Override + public String castPattern(CastType from, CastType to) { + if ( from == CastType.STRING && to == CastType.BOOLEAN ) { + return "cast(?1 as ?2)"; + } + else { + return super.castPattern( from, to ); + } + } + + /** + * {@code microsecond} is the smallest unit for an {@code interval}, + * and the highest precision for a {@code timestamp}, so we could + * use it as the "native" precision, but it's more convenient to use + * whole seconds (with the fractional part), since we want to use + * {@code extract(epoch from ...)} in our emulation of + * {@code timestampdiff()}. + */ + @Override + public long getFractionalSecondPrecisionInNanos() { + return 1_000_000_000; //seconds + } + + @Override @SuppressWarnings("deprecation") + public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { + return intervalType != null + ? "(?2+?3)" + : "cast(?3+" + intervalPattern( unit ) + " as " + temporalType.name().toLowerCase() + ")"; + } + + private static String intervalPattern(TemporalUnit unit) { + return switch (unit) { + case NANOSECOND -> "(?2)/1e3*interval '1 microsecond'"; + case NATIVE -> "(?2)*interval '1 second'"; + case QUARTER -> "(?2)*interval '3 month'"; // quarter is not supported in interval literals + case WEEK -> "(?2)*interval '7 day'"; // week is not supported in interval literals + default -> "(?2)*interval '1 " + unit + "'"; + }; + } + + @Override @SuppressWarnings("deprecation") + public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) { + if ( unit == null ) { + return "(?3-?2)"; + } + if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) { + // special case: subtraction of two dates + // results in an integer number of days + // instead of an INTERVAL + return switch (unit) { + case YEAR, MONTH, QUARTER -> "extract(" + translateDurationField( unit ) + " from age(?3,?2))"; + default -> "(?3-?2)" + DAY.conversionFactor( unit, this ); + }; + } + else { + return switch (unit) { + case YEAR -> "extract(year from ?3-?2)"; + case QUARTER -> "(extract(year from ?3-?2)*4+extract(month from ?3-?2)/3)"; + case MONTH -> "(extract(year from ?3-?2)*12+extract(month from ?3-?2))"; + case WEEK -> "(extract(day from ?3-?2)/7)"; // week is not supported by extract() when the argument is a duration + case DAY -> "extract(day from ?3-?2)"; + // in order to avoid multiple calls to extract(), + // we use extract(epoch from x - y) * factor for + // all the following units: + case HOUR, MINUTE, SECOND, NANOSECOND, NATIVE -> + "extract(epoch from ?3-?2)" + EPOCH.conversionFactor( unit, this ); + default -> throw new SemanticException( "Unrecognized field: " + unit ); + }; + } + } + + @Override + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NORMALIZE; + } + + @Override + public void initializeFunctionRegistry(FunctionContributions functionContributions) { + super.initializeFunctionRegistry(functionContributions); + + CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions); + + functionFactory.cot(); + functionFactory.radians(); + functionFactory.degrees(); + functionFactory.log(); + functionFactory.mod_operator(); + functionFactory.log10(); + functionFactory.tanh(); + functionFactory.sinh(); + functionFactory.cosh(); + functionFactory.moreHyperbolic(); + functionFactory.cbrt(); + functionFactory.pi(); + functionFactory.trim2(); + functionFactory.repeat(); + functionFactory.initcap(); + functionFactory.substr(); + functionFactory.substring_substr(); + //also natively supports ANSI-style substring() + functionFactory.translate(); + functionFactory.toCharNumberDateTimestamp(); + functionFactory.localtimeLocaltimestamp(); + functionFactory.bitLength_pattern( "bit_length(?1)", "length(?1)*8" ); + functionFactory.octetLength_pattern( "octet_length(?1)", "length(?1)" ); + functionFactory.ascii(); + functionFactory.char_chr(); + functionFactory.position(); + functionFactory.bitandorxornot_operator(); + functionFactory.bitAndOr(); + functionFactory.everyAny_boolAndOr(); + functionFactory.median_percentileCont( false ); + functionFactory.stddev(); + functionFactory.stddevPopSamp(); + functionFactory.variance(); + functionFactory.varPopSamp(); + functionFactory.covarPopSamp(); + functionFactory.corr(); + functionFactory.regrLinearRegressionAggregates(); + functionFactory.insert_overlay(); + functionFactory.overlay(); + functionFactory.soundex(); //was introduced apparently + functionFactory.format_toChar_gauss(); + + functionFactory.locate_positionSubstring(); + functionFactory.windowFunctions(); + functionFactory.listagg_stringAgg( "varchar" ); + functionFactory.array_gaussdb(); + functionFactory.arrayAggregate(); + functionFactory.arrayRemoveIndex_gaussdb(); + functionFactory.arrayLength_cardinality(); + functionFactory.arrayConcat_gaussdb(); + functionFactory.arrayPrepend_gaussdb(); + functionFactory.arrayAppend_gaussdb(); + functionFactory.arrayContains_gaussdb(); + functionFactory.arrayIntersects_gaussdb(); + functionFactory.arrayGet_bracket(); + functionFactory.arrayRemove_gaussdb(); + functionFactory.arraySlice_operator(); + functionFactory.arrayReplace_gaussdb(); + functionFactory.arraySet_gaussdb(); + functionFactory.arrayTrim_gaussdb(); + functionFactory.arrayFill_gaussdb(); + functionFactory.arrayPosition_gaussdb(); + + functionFactory.jsonValue_gaussdb(true); + functionFactory.jsonQuery_gaussdb(); + functionFactory.jsonExists_gaussdb(); + functionFactory.jsonArray(); + functionFactory.jsonObject_gaussdb(); + functionFactory.jsonArrayAgg_gaussdb( true ); + functionFactory.jsonObjectAgg_gaussdb( true ); + functionFactory.jsonTable(); + + functionFactory.jsonSet_gaussdb(); + functionFactory.jsonRemove_gaussdb(); + functionFactory.jsonReplace_gaussdb(); + functionFactory.jsonInsert_gaussdb(); + functionFactory.jsonArray_gaussdb(); + functionFactory.jsonMergepatch_gaussdb(); + functionFactory.jsonArrayInsert_gauss(); + + functionFactory.xmlelement(); + functionFactory.xmlcomment(); + functionFactory.xmlforest(); + functionFactory.xmlconcat(); + functionFactory.xmlpi(); + functionFactory.xmlquery_gaussdb(); + functionFactory.xmlexists(); + functionFactory.xmlagg(); + functionFactory.xmltable( true ); + + functionFactory.makeDateTimeTimestamp(); + // Note that GaussDB doesn't support the OVER clause for ordered set-aggregate functions + functionFactory.inverseDistributionOrderedSetAggregates(); + functionFactory.hypotheticalOrderedSetAggregates(); + + if ( !supportsMinMaxOnUuid() ) { + functionContributions.getFunctionRegistry().register( "min", new GaussDBMinMaxFunction( "min" ) ); + functionContributions.getFunctionRegistry().register( "max", new GaussDBMinMaxFunction( "max" ) ); + } + + // uses # instead of ^ for XOR + functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1 # ?2)" ) + .setExactArgumentCount( 2 ) + .setArgumentTypeResolver( StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE ) + .register(); + + functionContributions.getFunctionRegistry().register( + "round", new GaussDBTruncRoundFunction( "round", true ) + ); + functionContributions.getFunctionRegistry().register( + "trunc", + new GaussDBTruncFunction( true, functionContributions.getTypeConfiguration() ) + ); + functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" ); + functionFactory.dateTrunc(); + + functionFactory.unnest( null, "ordinality" ); + + functionFactory.hex( "encode(?1, 'hex')" ); + functionFactory.sha( "sha256(?1)" ); + functionFactory.md5( "decode(md5(?1), 'hex')" ); + } + + @Override + public @Nullable String getDefaultOrdinalityColumnName() { + return "ordinality"; + } + + /** + * Whether GaussDB supports {@code min(uuid)}/{@code max(uuid)}, + * which it doesn't by default. Since the emulation does not perform well, + * this method may be overridden by any user who ensures that aggregate + * functions for handling uuids exist in the database. + *

+ * The following definitions can be used for this purpose: + *

+	 * create or replace function min(uuid, uuid)
+	 *     returns uuid
+	 *     immutable parallel safe
+	 *     language plpgsql as
+	 * $$
+	 * begin
+	 *     return least($1, $2);
+	 * end
+	 * $$;
+	 *
+	 * create aggregate min(uuid) (
+	 *     sfunc = min,
+	 *     stype = uuid,
+	 *     combinefunc = min,
+	 *     parallel = safe,
+	 *     sortop = operator (<)
+	 *     );
+	 *
+	 * create or replace function max(uuid, uuid)
+	 *     returns uuid
+	 *     immutable parallel safe
+	 *     language plpgsql as
+	 * $$
+	 * begin
+	 *     return greatest($1, $2);
+	 * end
+	 * $$;
+	 *
+	 * create aggregate max(uuid) (
+	 *     sfunc = max,
+	 *     stype = uuid,
+	 *     combinefunc = max,
+	 *     parallel = safe,
+	 *     sortop = operator (>)
+	 *     );
+	 * 
+ */ + protected boolean supportsMinMaxOnUuid() { + return false; + } + + @Override + public NameQualifierSupport getNameQualifierSupport() { + // This method is overridden so the correct value will be returned when + // DatabaseMetaData is not available. + return NameQualifierSupport.SCHEMA; + } + + @Override + public String getCurrentSchemaCommand() { + return "select current_schema()"; + } + + @Override + public boolean supportsDistinctFromPredicate() { + return true; + } + + @Override + public boolean supportsIfExistsBeforeTableName() { + return true; + } + + @Override + public boolean supportsIfExistsBeforeTypeName() { + return true; + } + + @Override + public boolean supportsIfExistsBeforeConstraintName() { + return true; + } + + @Override + public boolean supportsIfExistsAfterAlterTable() { + return true; + } + + @Override + public String getBeforeDropStatement() { + // NOTICE: table "nonexistent" does not exist, skipping + // as a JDBC SQLWarning + return "set client_min_messages = WARNING"; + } + + @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 true; + } + + @Override + public boolean supportsValuesList() { + return true; + } + + @Override + public boolean supportsPartitionBy() { + return true; + } + + @Override + public boolean supportsNonQueryWithCTE() { + return true; + } + @Override + public boolean supportsConflictClauseForInsertCTE() { + return true; + } + + @Override + public SequenceSupport getSequenceSupport() { + return GaussDBSequenceSupport.INSTANCE; + } + + @Override + public String getCascadeConstraintsString() { + return " cascade"; + } + + @Override + public String getQuerySequencesString() { + return "select * from information_schema.sequences"; + } + + @Override + public LimitHandler getLimitHandler() { + return OffsetFetchLimitHandler.INSTANCE; + } + + @Override + public String getForUpdateString(String aliases) { + return getForUpdateString() + " of " + aliases; + } + + @Override + public String getForUpdateString(String aliases, LockOptions lockOptions) { + // parent's implementation for (aliases, lockOptions) ignores aliases + if ( aliases.isEmpty() ) { + LockMode lockMode = lockOptions.getLockMode(); + for ( Map.Entry entry : lockOptions.getAliasSpecificLocks() ) { + // seek the highest lock mode + if ( entry.getValue().greaterThan(lockMode) ) { + aliases = entry.getKey(); + } + } + } + LockMode lockMode = lockOptions.getAliasSpecificLockMode( aliases ); + if ( lockMode == null ) { + lockMode = lockOptions.getLockMode(); + } + return switch (lockMode) { + case PESSIMISTIC_READ -> getReadLockString( aliases, lockOptions.getTimeOut() ); + case PESSIMISTIC_WRITE -> getWriteLockString( aliases, lockOptions.getTimeOut() ); + case UPGRADE_NOWAIT, PESSIMISTIC_FORCE_INCREMENT -> getForUpdateNowaitString( aliases ); + case UPGRADE_SKIPLOCKED -> getForUpdateSkipLockedString( aliases ); + default -> ""; + }; + } + + @Override + public String getNoColumnsInsertString() { + return "default values"; + } + + @Override + public String getCaseInsensitiveLike(){ + return "ilike"; + } + + @Override + public boolean supportsCaseInsensitiveLike() { + return true; + } + + @Override + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.SEQUENCE; + } + + @Override + public boolean supportsOuterJoinForUpdate() { + return false; + } + + @Override + public boolean useInputStreamToInsertBlob() { + return false; + } + + @Override + public boolean useConnectionToCreateLob() { + return false; + } + + @Override + public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) { + // TODO: adapt this to handle named enum types! + return "cast(null as " + typeConfiguration.getDdlTypeRegistry().getDescriptor( sqlType ).getRawTypeName() + ")"; + } + + @Override + public String quoteCollation(String collation) { + return '\"' + collation + '\"'; + } + + @Override + public boolean supportsCommentOn() { + return true; + } + + @Override + public boolean supportsCurrentTimestampSelection() { + return true; + } + + @Override + public boolean isCurrentTimestampSelectStringCallable() { + return false; + } + + @Override + public String getCurrentTimestampSelectString() { + return "select now()"; + } + + @Override + public boolean supportsTupleCounts() { + return true; + } + + @Override + public boolean supportsIsTrue() { + return true; + } + + @Override + public boolean requiresParensForTupleDistinctCounts() { + return true; + } + + @Override + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ); + } + + @Override + public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) + throws SQLException { + + if ( dbMetaData == null ) { + builder.setUnquotedCaseStrategy( IdentifierCaseStrategy.LOWER ); + builder.setQuotedCaseStrategy( IdentifierCaseStrategy.MIXED ); + } + + return super.buildIdentifierHelper( builder, dbMetaData ); + } + + @Override + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( + EntityMappingType rootEntityDescriptor, + RuntimeModelCreationContext runtimeModelCreationContext) { + return new CteMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); + } + + @Override + public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( + EntityMappingType rootEntityDescriptor, + RuntimeModelCreationContext runtimeModelCreationContext) { + return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); + } + + @Override + public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { + return new StandardSqlAstTranslatorFactory() { + @Override + protected SqlAstTranslator buildTranslator( + SessionFactoryImplementor sessionFactory, Statement statement) { + return new GaussDBSqlAstTranslator<>( sessionFactory, statement ); + } + }; + } + + @Override + public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { + return EXTRACTOR; + } + + /** + * Constraint-name extractor for constraint violation exceptions. + * Originally contributed by Denny Bartelt. + */ + private static final ViolatedConstraintNameExtractor EXTRACTOR = + new TemplatedViolatedConstraintNameExtractor( sqle -> { + final String sqlState = JdbcExceptionHelper.extractSqlState( sqle ); + if ( sqlState != null ) { + switch ( Integer.parseInt( sqlState ) ) { + // CHECK VIOLATION + case 23514: + return extractUsingTemplate( "violates check constraint \"", "\"", sqle.getMessage() ); + // UNIQUE VIOLATION + case 23505: + return extractUsingTemplate( "violates unique constraint \"", "\"", sqle.getMessage() ); + // FOREIGN KEY VIOLATION + case 23503: + return extractUsingTemplate( "violates foreign key constraint \"", "\"", sqle.getMessage() ); + // NOT NULL VIOLATION + case 23502: + return extractUsingTemplate( + "null value in column \"", + "\" violates not-null constraint", + sqle.getMessage() + ); + // TODO: RESTRICT VIOLATION + case 23001: + return null; + } + } + return null; + } ); + + @Override + public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { + return (sqlException, message, sql) -> { + final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException ); + if ( sqlState != null ) { + switch ( sqlState ) { + case "40P01": + // DEADLOCK DETECTED + return new LockAcquisitionException( message, sqlException, sql ); + case "55P03": + // LOCK NOT AVAILABLE + return new PessimisticLockException( message, sqlException, sql ); + case "57014": + return new QueryTimeoutException( message, sqlException, sql ); + } + } + return null; + }; + } + + @Override + public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException { + // Register the type of the out param - GaussDB uses Types.OTHER + statement.registerOutParameter( col++, Types.OTHER ); + return col; + } + + @Override + public ResultSet getResultSet(CallableStatement ps) throws SQLException { + ps.execute(); + return (ResultSet) ps.getObject( 1 ); + } + + // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public boolean supportsLobValueChangePropagation() { + return false; + } + + @Override + public boolean supportsUnboundedLobLocatorMaterialization() { + return false; + } + + @Override + public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() { + return SelectItemReferenceStrategy.POSITION; + } + + @Override + public CallableStatementSupport getCallableStatementSupport() { + return GaussDBCallableStatementSupport.INSTANCE; + } + + @Override + public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException { + if ( position != 1 ) { + throw new UnsupportedOperationException( "GaussDB only supports REF_CURSOR parameters as the first parameter" ); + } + return (ResultSet) statement.getObject( 1 ); + } + + @Override + public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException { + throw new UnsupportedOperationException( "GaussDB only supports accessing REF_CURSOR parameters by position" ); + } + + @Override + public boolean qualifyIndexName() { + return false; + } + + @Override + public IdentityColumnSupport getIdentityColumnSupport() { + return GaussDBIdentityColumnSupport.INSTANCE; + } + + @Override + public NationalizationSupport getNationalizationSupport() { + return NationalizationSupport.IMPLICIT; + } + + @Override + public int getMaxIdentifierLength() { + return 63; + } + + @Override + public boolean supportsStandardArrays() { + return true; + } + + @Override + public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaData) { + return false; + } + + @Override + public boolean supportsMaterializedLobAccess() { + // Prefer using text and bytea over oid (LOB), because oid is very restricted. + // If someone really wants a type bigger than 1GB, they should ask for it by using @Lob explicitly + return false; + } + + @Override + public boolean supportsTemporalLiteralOffset() { + return true; + } + + @Override + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( datetimeFormat( format ).result() ); + } + + public Replacer datetimeFormat(String format) { + return OracleDialect.datetimeFormat( format, true, false ) + .replace("SSSSSS", "US") + .replace("SSSSS", "US") + .replace("SSSS", "US") + .replace("SSS", "MS") + .replace("SS", "MS") + .replace("S", "MS") + //use ISO day in week, as per DateTimeFormatter + .replace("ee", "ID") + .replace("e", "fmID") + //TZR is TZ + .replace("zzz", "TZ") + .replace("zz", "TZ") + .replace("z", "TZ") + .replace("xxx", "OF") + .replace("xx", "OF") + .replace("x", "OF"); + } + + @Override + public String translateExtractField(TemporalUnit unit) { + return switch (unit) { + //WEEK means the ISO week number + case DAY_OF_MONTH -> "day"; + case DAY_OF_YEAR -> "doy"; + case DAY_OF_WEEK -> "dow"; + default -> super.translateExtractField( unit ); + }; + } + + @Override + public AggregateSupport getAggregateSupport() { + return GaussDBAggregateSupport.valueOf( this ); + } + + @Override + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { + appender.appendSql( "bytea '\\x" ); + PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes ); + appender.appendSql( '\'' ); + } + + @Override + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + @SuppressWarnings("deprecation") + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( '\'' ); + break; + case TIME: + if ( supportsTemporalLiteralOffset() && temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, temporalAccessor, true, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, temporalAccessor ); + } + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + if ( supportsTemporalLiteralOffset() && temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, temporalAccessor, true, jdbcTimeZone ); + appender.appendSql( '\'' ); + } + else { + appender.appendSql( "timestamp '" ); + appendAsTimestampWithMicros( appender, temporalAccessor, false, jdbcTimeZone ); + appender.appendSql( '\'' ); + } + break; + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void appendDateTimeLiteral( + SqlAppender appender, + Date date, + @SuppressWarnings("deprecation") + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, date ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, date, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + @SuppressWarnings("deprecation") + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, calendar ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, calendar, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + private String withTimeout(String lockString, int timeout) { + return switch (timeout) { + case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; + case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + default -> lockString; + }; + } + + @Override + public String getWriteLockString(int timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, int timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(int timeout) { + return withTimeout(" for share", timeout ); + } + + @Override + public String getReadLockString(String aliases, int timeout) { + return withTimeout(" for share of " + aliases, timeout ); + } + + @Override + public String getForUpdateNowaitString() { + return supportsNoWait() + ? " for update nowait" + : getForUpdateString(); + } + + @Override + public String getForUpdateNowaitString(String aliases) { + return supportsNoWait() + ? " for update of " + aliases + " nowait" + : getForUpdateString(aliases); + } + + @Override + public String getForUpdateSkipLockedString() { + return supportsSkipLocked() + ? " for update skip locked" + : getForUpdateString(); + } + + @Override + public String getForUpdateSkipLockedString(String aliases) { + return supportsSkipLocked() + ? " for update of " + aliases + " skip locked" + : getForUpdateString( aliases ); + } + + @Override + public boolean supportsNoWait() { + return true; + } + + @Override + public boolean supportsWait() { + return false; + } + + @Override + public boolean supportsSkipLocked() { + return true; + } + + @Override + public boolean supportsInsertReturning() { + return true; + } + + @Override + public boolean supportsOffsetInSubquery() { + return true; + } + + @Override + public boolean supportsWindowFunctions() { + return true; + } + + @Override + public boolean supportsLateral() { + return false; + } + + @Override + public boolean supportsRecursiveCTE() { + return true; + } + + @Override + public boolean supportsFetchClause(FetchClauseType type) { + return false; + } + + @Override + public String getForUpdateString() { + return " for update"; + } + + @Override + public boolean supportsFilterClause() { + return false; + } + + @Override + public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() { + return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE; + } + + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return RowLockStrategy.TABLE; + } + + @Override + public void augmentRecognizedTableTypes(List tableTypesList) { + super.augmentRecognizedTableTypes( tableTypesList ); + tableTypesList.add( "MATERIALIZED VIEW" ); + tableTypesList.add( "PARTITIONED TABLE" ); + } + + @Override + public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + super.contributeTypes(typeContributions, serviceRegistry); + contributeGaussDBTypes( typeContributions, serviceRegistry); + } + + /** + * Allow for extension points to override this only + */ + protected void contributeGaussDBTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration() + .getJdbcTypeRegistry(); + // For how BLOB affects Hibernate, see: + // http://in.relation.to/15492.lace + + jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE ); + + jdbcTypeRegistry.addDescriptorIfAbsent( GaussDBCastingInetJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( GaussDBCastingIntervalSecondJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( GaussDBStructCastingJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( GaussDBCastingJsonJdbcType.JSONB_INSTANCE ); + jdbcTypeRegistry.addTypeConstructorIfAbsent( GaussDBCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE ); + + // GaussDB requires a custom binder for binding untyped nulls as VARBINARY + typeContributions.contributeJdbcType( ObjectNullAsBinaryTypeJdbcType.INSTANCE ); + + // Until we remove StandardBasicTypes, we have to keep this + typeContributions.contributeType( + new JavaObjectType( + ObjectNullAsBinaryTypeJdbcType.INSTANCE, + typeContributions.getTypeConfiguration() + .getJavaTypeRegistry() + .getDescriptor( Object.class ) + ) + ); + + jdbcTypeRegistry.addDescriptor( GaussDBEnumJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( GaussDBOrdinalEnumJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( GaussDBUUIDJdbcType.INSTANCE ); + + // Replace the standard array constructor + jdbcTypeRegistry.addTypeConstructor( GaussDBArrayJdbcTypeConstructor.INSTANCE ); + } + + @Override + public UniqueDelegate getUniqueDelegate() { + return uniqueDelegate; + } + + @Override + public Exporter getTableExporter() { + return gaussDBTableExporter; + } + + /** + * @return {@code true}, but only because we can "batch" truncate + */ + @Override + public boolean canBatchTruncate() { + return true; + } + + @Override + public String getQueryHintString(String sql, String hints) { + return "/*+ " + hints + " */ " + sql; + } + + @Override + public String addSqlHintOrComment(String sql, QueryOptions queryOptions, boolean commentsEnabled) { + // GaussDB's extension pg_hint_plan needs the hint to be the first comment + if ( commentsEnabled && queryOptions.getComment() != null ) { + sql = prependComment( sql, queryOptions.getComment() ); + } + if ( queryOptions.getDatabaseHints() != null && !queryOptions.getDatabaseHints().isEmpty() ) { + sql = getQueryHintString( sql, queryOptions.getDatabaseHints() ); + } + return sql; + } + + @FunctionalInterface + private interface OptionalTableUpdateStrategy { + MutationOperation buildMutationOperation( + EntityMutationTarget mutationTarget, + OptionalTableUpdate optionalTableUpdate, + SessionFactoryImplementor factory); + } + + @Override + public MutationOperation createOptionalTableUpdateOperation( + EntityMutationTarget mutationTarget, + OptionalTableUpdate optionalTableUpdate, + SessionFactoryImplementor factory) { + return optionalTableUpdateStrategy.buildMutationOperation( mutationTarget, optionalTableUpdate, factory ); + } + + private static MutationOperation usingMerge( + EntityMutationTarget mutationTarget, + OptionalTableUpdate optionalTableUpdate, + SessionFactoryImplementor factory) { + final GaussDBSqlAstTranslator translator = new GaussDBSqlAstTranslator<>( factory, optionalTableUpdate ); + return translator.createMergeOperation( optionalTableUpdate ); + } + + private static MutationOperation withoutMerge( + EntityMutationTarget mutationTarget, + OptionalTableUpdate optionalTableUpdate, + SessionFactoryImplementor factory) { + return new OptionalTableUpdateOperation( mutationTarget, optionalTableUpdate, factory ); + } + + private static class NativeParameterMarkers implements ParameterMarkerStrategy { + /** + * Singleton access + */ + public static final NativeParameterMarkers INSTANCE = new NativeParameterMarkers(); + + @Override + public String createMarker(int position, JdbcType jdbcType) { + return "$" + position; + } + } + + @Override + public int getDefaultIntervalSecondScale() { + // The maximum scale for `interval second` is 6 unfortunately + return 6; + } + + @Override + public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { + return DmlTargetColumnQualifierSupport.TABLE_ALIAS; + } + + @Override + public boolean supportsFromClauseInUpdate() { + return true; + } + + @Override + public boolean supportsBindingNullSqlTypeForSetNull() { + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBEnumJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBEnumJdbcType.java new file mode 100644 index 000000000000..3a1d399826b4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBEnumJdbcType.java @@ -0,0 +1,168 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.NamedAuxiliaryDatabaseObject; +import org.hibernate.engine.jdbc.Size; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Arrays; + +import static java.util.Collections.emptySet; +import static org.hibernate.type.SqlTypes.NAMED_ENUM; +import static org.hibernate.type.SqlTypes.OTHER; +import static org.hibernate.type.descriptor.converter.internal.EnumHelper.getEnumeratedValues; + +/** + * Represents a named {@code enum} type on GaussDB. + *

+ * Hibernate does not automatically use this for enums + * mapped as {@link jakarta.persistence.EnumType#STRING}, and + * instead this type must be explicitly requested using: + *

+ * @JdbcTypeCode(SqlTypes.NAMED_ENUM)
+ * 
+ * + * @see org.hibernate.type.SqlTypes#NAMED_ENUM + * @see GaussDBDialect#getEnumTypeDeclaration(String, String[]) + * @see GaussDBDialect#getCreateEnumTypeCommand(String, String[]) + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLEnumJdbcType. + */ +public class GaussDBEnumJdbcType implements JdbcType { + + public static final GaussDBEnumJdbcType INSTANCE = new GaussDBEnumJdbcType(); + + @Override + public int getJdbcTypeCode() { + return OTHER; + } + + @Override + public int getDefaultSqlTypeCode() { + return NAMED_ENUM; + } + + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + @SuppressWarnings("unchecked") + final Class> enumClass = (Class>) javaType.getJavaType(); + return (appender, value, dialect, wrapperOptions) -> { + appender.appendSql( "'" ); + appender.appendSql( ((Enum) value).name() ); + appender.appendSql( "'::" ); + appender.appendSql( dialect.getEnumTypeDeclaration( enumClass ) ); + }; + } + + @Override + public String getFriendlyName() { + return "ENUM"; + } + + @Override + public String toString() { + return "EnumTypeDescriptor"; + } + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException { + st.setNull( index, Types.OTHER ); + } + + @Override + protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException { + st.setNull( name, Types.OTHER ); + } + + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setObject( index, getJavaType().unwrap( value, String.class, options ), Types.OTHER ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setObject( name, getJavaType().unwrap( value, String.class, options ), Types.OTHER ); + } + }; + } + + @Override + public ValueExtractor getExtractor(JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return getJavaType().wrap( rs.getObject( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return getJavaType().wrap( statement.getObject( index ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException { + return getJavaType().wrap( statement.getObject( name ), options ); + } + }; + } + + @Override + public void addAuxiliaryDatabaseObjects( + JavaType javaType, + BasicValueConverter valueConverter, + Size columnSize, + Database database, + JdbcTypeIndicators context) { + @SuppressWarnings("unchecked") + final Class> enumClass = (Class>) javaType.getJavaType(); + @SuppressWarnings("unchecked") + final String[] enumeratedValues = + valueConverter == null + ? getEnumeratedValues( enumClass ) + : getEnumeratedValues( enumClass, (BasicValueConverter,?>) valueConverter ) ; + if ( getDefaultSqlTypeCode() == NAMED_ENUM ) { + Arrays.sort( enumeratedValues ); + } + final Dialect dialect = database.getDialect(); + final String[] create = + dialect.getCreateEnumTypeCommand( javaType.getJavaTypeClass().getSimpleName(), enumeratedValues ); + final String[] drop = dialect.getDropEnumTypeCommand( enumClass ); + if ( create != null && create.length > 0 ) { + database.addAuxiliaryDatabaseObject( + new NamedAuxiliaryDatabaseObject( + enumClass.getSimpleName(), + database.getDefaultNamespace(), + create, + drop, + emptySet(), + true + ) + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBOrdinalEnumJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBOrdinalEnumJdbcType.java new file mode 100644 index 000000000000..e889105ebb6b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBOrdinalEnumJdbcType.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import jakarta.persistence.EnumType; + +import static org.hibernate.type.SqlTypes.NAMED_ORDINAL_ENUM; + +/** + * Represents a named {@code enum} type on GaussDB. + *

+ * Hibernate does not automatically use this for enums + * mapped as {@link EnumType#ORDINAL}, and + * instead this type must be explicitly requested using: + *

+ * @JdbcTypeCode(SqlTypes.NAMED_ORDINAL_ENUM)
+ * 
+ * + * @see org.hibernate.type.SqlTypes#NAMED_ORDINAL_ENUM + * @see GaussDBDialect#getEnumTypeDeclaration(String, String[]) + * @see GaussDBDialect#getCreateEnumTypeCommand(String, String[]) + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLOrdinalEnumJdbcType. + */ +public class GaussDBOrdinalEnumJdbcType extends GaussDBEnumJdbcType { + + public static final GaussDBOrdinalEnumJdbcType INSTANCE = new GaussDBOrdinalEnumJdbcType(); + + @Override + public int getDefaultSqlTypeCode() { + return NAMED_ORDINAL_ENUM; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java new file mode 100644 index 000000000000..277a65a2fe6a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java @@ -0,0 +1,303 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.query.sqm.ComparisonOperator; +import org.hibernate.query.common.FetchClauseType; +import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.spi.SqlAstTranslatorWithMerge; +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; +import org.hibernate.sql.ast.tree.from.NamedTableReference; +import org.hibernate.sql.ast.tree.from.TableReference; +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.InArrayPredicate; +import org.hibernate.sql.ast.tree.predicate.LikePredicate; +import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; +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.update.UpdateStatement; +import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl; +import org.hibernate.sql.exec.spi.JdbcOperation; +import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert; +import org.hibernate.sql.model.internal.TableInsertStandard; +import org.hibernate.type.SqlTypes; + +/** + * A SQL AST translator for GaussDB. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLSqlAstTranslator. + */ +public class GaussDBSqlAstTranslator extends SqlAstTranslatorWithMerge { + + public GaussDBSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { + super( sessionFactory, statement ); + } + + @Override + public void visitInArrayPredicate(InArrayPredicate inArrayPredicate) { + inArrayPredicate.getTestExpression().accept( this ); + appendSql( " = any (" ); + inArrayPredicate.getArrayParameter().accept( this ); + appendSql( ")" ); + } + + @Override + protected String getArrayContainsFunction() { + return super.getArrayContainsFunction(); + } + + @Override + protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) { + renderIntoIntoAndTable( tableInsert ); + appendSql( "default values" ); + } + + @Override + protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) { + visitInsertStatement( sqlAst ); + + return new JdbcOperationQueryInsertImpl( + getSql(), + getParameterBinders(), + getAffectedTableNames(), + null + ); + } + + @Override + protected void renderTableReferenceIdentificationVariable(TableReference tableReference) { + final String identificationVariable = tableReference.getIdentificationVariable(); + if ( identificationVariable != null ) { + final Clause currentClause = getClauseStack().getCurrent(); + if ( currentClause == Clause.INSERT ) { + // GaussDB requires the "as" keyword for inserts + appendSql( " as " ); + } + else { + append( WHITESPACE ); + } + append( tableReference.getIdentificationVariable() ); + } + } + + @Override + protected void renderDmlTargetTableExpression(NamedTableReference tableReference) { + super.renderDmlTargetTableExpression( tableReference ); + final Statement currentStatement = getStatementStack().getCurrent(); + if ( !( currentStatement instanceof UpdateStatement updateStatement ) + || !hasNonTrivialFromClause( updateStatement.getFromClause() ) ) { + // For UPDATE statements we render a full FROM clause and a join condition to match target table rows, + // but for that to work, we have to omit the alias for the target table reference here + renderTableReferenceIdentificationVariable( tableReference ); + } + } + + @Override + protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) { + renderFromClauseJoiningDmlTargetReference( statement ); + } + + @Override + protected void visitConflictClause(ConflictClause conflictClause) { + visitStandardConflictClause( conflictClause ); + } + + @Override + protected void renderExpressionAsClauseItem(Expression expression) { + expression.accept( this ); + } + + @Override + protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { + final JdbcMappingContainer lhsExpressionType = lhs.getExpressionType(); + if ( lhsExpressionType != null && lhsExpressionType.getJdbcTypeCount() == 1 + && lhsExpressionType.getSingleJdbcMapping().getJdbcType().getDdlTypeCode() == SqlTypes.SQLXML ) { + // In GaussDB, XMLTYPE is not "comparable", so we have to cast the two parts to varchar for this purpose + switch ( operator ) { + case EQUAL: + case NOT_DISTINCT_FROM: + case NOT_EQUAL: + case DISTINCT_FROM: + appendSql( "cast(" ); + lhs.accept( this ); + appendSql( " as text)" ); + appendSql( operator.sqlText() ); + appendSql( "cast(" ); + rhs.accept( this ); + appendSql( " as text)" ); + return; + default: + // Fall through + break; + } + } + renderComparisonStandard( lhs, operator, rhs ); + } + + @Override + public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) { + final boolean isNegated = booleanExpressionPredicate.isNegated(); + if ( isNegated ) { + appendSql( "not(" ); + } + booleanExpressionPredicate.getExpression().accept( this ); + if ( isNegated ) { + appendSql( CLOSE_PARENTHESIS ); + } + } + + @Override + public void visitNullnessPredicate(NullnessPredicate nullnessPredicate) { + final Expression expression = nullnessPredicate.getExpression(); + final JdbcMappingContainer expressionType = expression.getExpressionType(); + if ( isStruct( expressionType ) ) { + // Surprise, the null predicate checks if all components of the struct are null or not, + // rather than the column itself, so we have to use the distinct from predicate to implement this instead + expression.accept( this ); + if ( nullnessPredicate.isNegated() ) { + appendSql( " is distinct from null" ); + } + else { + appendSql( " is not distinct from null" ); + } + } + else { + super.visitNullnessPredicate( nullnessPredicate ); + } + } + + @Override + protected void renderMaterializationHint(CteMaterialization materialization) { + if ( materialization == CteMaterialization.NOT_MATERIALIZED ) { + appendSql( "not " ); + } + appendSql( "materialized " ); + } + + @Override + protected String getForUpdate() { + return getDialect().getForUpdateString(); + } + + @Override + protected String getForShare(int timeoutMillis) { + // Note that `for key share` is inappropriate as that only means "prevent PK changes" + return " for share"; + } + + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { + // Check if current query part is already row numbering to avoid infinite recursion + if ( getQueryPartForRowNumbering() == queryPart || isRowsOnlyFetchClauseType( queryPart ) ) { + return false; + } + return !getDialect().supportsFetchClause( queryPart.getFetchClauseType() ); + } + + @Override + public void visitQueryGroup(QueryGroup queryGroup) { + if ( shouldEmulateFetchClause( queryGroup ) ) { + emulateFetchOffsetWithWindowFunctions( queryGroup, true ); + } + else { + super.visitQueryGroup( queryGroup ); + } + } + + @Override + public void visitQuerySpec(QuerySpec querySpec) { + if ( shouldEmulateFetchClause( querySpec ) ) { + emulateFetchOffsetWithWindowFunctions( querySpec, true ); + } + else { + super.visitQuerySpec( querySpec ); + } + } + + @Override + public void visitOffsetFetchClause(QueryPart queryPart) { + if ( !isRowNumberingCurrentQueryPart() ) { + if ( getDialect().supportsFetchClause( FetchClauseType.ROWS_ONLY ) ) { + renderOffsetFetchClause( queryPart, true ); + } + else { + renderLimitOffsetClause( queryPart ); + } + } + } + + @Override + protected void renderStandardCycleClause(CteStatement cte) { + super.renderStandardCycleClause( cte ); + if ( cte.getCycleMarkColumn() != null && cte.getCyclePathColumn() == null && getDialect().supportsRecursiveCycleUsingClause() ) { + appendSql( " using " ); + appendSql( determineCyclePathColumnName( cte ) ); + } + } + + @Override + protected void renderPartitionItem(Expression expression) { + // We render an empty group instead of literals as some DBs don't support grouping by literals + // Note that integer literals, which refer to select item positions, are handled in #visitGroupByClause + if ( expression instanceof Literal ) { + appendSql( "()" ); + } + else if ( expression instanceof Summarization summarization ) { + appendSql( summarization.getKind().sqlText() ); + appendSql( OPEN_PARENTHESIS ); + renderCommaSeparated( summarization.getGroupings() ); + appendSql( CLOSE_PARENTHESIS ); + } + else { + expression.accept( this ); + } + } + + @Override + public void visitLikePredicate(LikePredicate likePredicate) { + // We need a custom implementation here because GaussDB + // uses the backslash character as default escape character + // According to the documentation, we can overcome this by specifying an empty escape character + // See https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE + likePredicate.getMatchExpression().accept( this ); + if ( likePredicate.isNegated() ) { + appendSql( " not" ); + } + if ( likePredicate.isCaseSensitive() ) { + appendSql( " like " ); + } + else { + appendSql( WHITESPACE ); + appendSql( getDialect().getCaseInsensitiveLike() ); + appendSql( WHITESPACE ); + } + likePredicate.getPattern().accept( this ); + if ( likePredicate.getEscapeCharacter() != null ) { + appendSql( " escape " ); + likePredicate.getEscapeCharacter().accept( this ); + } + } + + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + appendSql( OPEN_PARENTHESIS ); + visitArithmeticOperand( arithmeticExpression.getLeftHandOperand() ); + appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); + visitArithmeticOperand( arithmeticExpression.getRightHandOperand() ); + appendSql( CLOSE_PARENTHESIS ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBStructCastingJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBStructCastingJdbcType.java new file mode 100644 index 000000000000..b2d93f7f426d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBStructCastingJdbcType.java @@ -0,0 +1,100 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.hibernate.boot.model.naming.Identifier; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; + +/** + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLStructCastingJdbcType. + */ +public class GaussDBStructCastingJdbcType extends AbstractGaussDBStructJdbcType { + + public static final GaussDBStructCastingJdbcType INSTANCE = new GaussDBStructCastingJdbcType(); + public GaussDBStructCastingJdbcType() { + this( null, null, null ); + } + + private GaussDBStructCastingJdbcType( + EmbeddableMappingType embeddableMappingType, + String typeName, + int[] orderMapping) { + super( embeddableMappingType, typeName, orderMapping ); + } + + @Override + public AggregateJdbcType resolveAggregateJdbcType( + EmbeddableMappingType mappingType, + String sqlType, + RuntimeModelCreationContext creationContext) { + return new GaussDBStructCastingJdbcType( + mappingType, + sqlType, + creationContext.getBootModel() + .getDatabase() + .getDefaultNamespace() + .locateUserDefinedType( Identifier.toIdentifier( sqlType ) ) + .getOrderMapping() + ); + } + + @Override + public void appendWriteExpression( + String writeExpression, + SqlAppender appender, + Dialect dialect) { + appender.append( "cast(" ); + appender.append( writeExpression ); + appender.append( " as " ); + appender.append( getStructTypeName() ); + appender.append( ')' ); + } + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + final String stringValue = ( (GaussDBStructCastingJdbcType) getJdbcType() ).toString( + value, + getJavaType(), + options + ); + st.setString( index, stringValue ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + final String stringValue = ( (GaussDBStructCastingJdbcType) getJdbcType() ).toString( + value, + getJavaType(), + options + ); + st.setString( name, stringValue ); + } + + @Override + public Object getBindValue(X value, WrapperOptions options) throws SQLException { + return ( (GaussDBStructCastingJdbcType) getJdbcType() ).getBindValue( value, options ); + } + }; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBUUIDJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBUUIDJdbcType.java new file mode 100644 index 000000000000..9c4a106aa5e4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBUUIDJdbcType.java @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.UUID; + +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; + +/** + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLUUIDJdbcType. + */ +public class GaussDBUUIDJdbcType extends UUIDJdbcType { + + /** + * Singleton access + */ + public static final GaussDBUUIDJdbcType INSTANCE = new GaussDBUUIDJdbcType(); + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException { + st.setNull( index, getJdbcType().getJdbcTypeCode(), "uuid" ); + } + + @Override + protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException { + st.setNull( name, getJdbcType().getJdbcTypeCode(), "uuid" ); + } + + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setObject( index, getJavaType().unwrap( value, UUID.class, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setObject( name, getJavaType().unwrap( value, UUID.class, options ) ); + } + }; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/GaussDBAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/GaussDBAggregateSupport.java new file mode 100644 index 000000000000..90d300f481fe --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/GaussDBAggregateSupport.java @@ -0,0 +1,630 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.aggregate; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.hibernate.dialect.Dialect; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Column; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.SelectableMapping; +import org.hibernate.metamodel.mapping.SelectablePath; +import org.hibernate.metamodel.mapping.SqlTypedMapping; +import org.hibernate.sql.ast.SqlAstNodeRenderingMode; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.type.BasicPluralType; +import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.XmlHelper; +import org.hibernate.type.spi.TypeConfiguration; + +import static org.hibernate.type.SqlTypes.ARRAY; +import static org.hibernate.type.SqlTypes.BIGINT; +import static org.hibernate.type.SqlTypes.BINARY; +import static org.hibernate.type.SqlTypes.BOOLEAN; +import static org.hibernate.type.SqlTypes.DOUBLE; +import static org.hibernate.type.SqlTypes.FLOAT; +import static org.hibernate.type.SqlTypes.INTEGER; +import static org.hibernate.type.SqlTypes.JSON; +import static org.hibernate.type.SqlTypes.JSON_ARRAY; +import static org.hibernate.type.SqlTypes.LONG32VARBINARY; +import static org.hibernate.type.SqlTypes.SMALLINT; +import static org.hibernate.type.SqlTypes.SQLXML; +import static org.hibernate.type.SqlTypes.STRUCT; +import static org.hibernate.type.SqlTypes.STRUCT_ARRAY; +import static org.hibernate.type.SqlTypes.STRUCT_TABLE; +import static org.hibernate.type.SqlTypes.TINYINT; +import static org.hibernate.type.SqlTypes.VARBINARY; +import static org.hibernate.type.SqlTypes.XML_ARRAY; + +/** + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLAggregateSupport. + */ +public class GaussDBAggregateSupport extends AggregateSupportImpl { + + private static final AggregateSupport INSTANCE = new GaussDBAggregateSupport(); + + private static final String XML_EXTRACT_START = "xmlelement(name \"" + XmlHelper.ROOT_TAG + "\",(select xmlagg(t.v) from xmltable("; + private static final String XML_EXTRACT_SEPARATOR = "/*' passing "; + private static final String XML_EXTRACT_END = " columns v xml path '.')t))"; + private static final String XML_QUERY_START = "(select xmlagg(t.v) from xmltable("; + private static final String XML_QUERY_SEPARATOR = "' passing "; + private static final String XML_QUERY_END = " columns v xml path '.')t)"; + + public static AggregateSupport valueOf(Dialect dialect) { + return GaussDBAggregateSupport.INSTANCE; + } + + @Override + public String aggregateComponentCustomReadExpression( + String template, + String placeholder, + String aggregateParentReadExpression, + String columnExpression, + int aggregateColumnTypeCode, + SqlTypedMapping column, + TypeConfiguration typeConfiguration) { + switch ( aggregateColumnTypeCode ) { + case JSON_ARRAY: + case JSON: + switch ( column.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) { + case JSON: + case JSON_ARRAY: + return template.replace( + placeholder, + aggregateParentReadExpression + "->'" + columnExpression + "'" + ); + case BINARY: + case VARBINARY: + case LONG32VARBINARY: + // We encode binary data as hex, so we have to decode here + return template.replace( + placeholder, + "decode(" + aggregateParentReadExpression + "->>'" + columnExpression + "','hex')" + ); + case ARRAY: + final BasicPluralType pluralType = (BasicPluralType) column.getJdbcMapping(); + switch ( pluralType.getElementType().getJdbcType().getDefaultSqlTypeCode() ) { + case BOOLEAN: + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + case FLOAT: + case DOUBLE: + // For types that are natively supported in jsonb we can use jsonb_array_elements, + // but note that we can't use that for string types, + // because casting a jsonb[] to text[] will not omit the quotes of the jsonb text values + return template.replace( + placeholder, + "cast(array(select jsonb_array_elements(" + aggregateParentReadExpression + "->'" + columnExpression + "')) as " + column.getColumnDefinition() + ')' + ); + case BINARY: + case VARBINARY: + case LONG32VARBINARY: + // We encode binary data as hex, so we have to decode here + return template.replace( + placeholder, + "array(select decode(jsonb_array_elements_text(" + aggregateParentReadExpression + "->'" + columnExpression + "'),'hex'))" + ); + default: + return template.replace( + placeholder, + "cast(array(select jsonb_array_elements_text(" + aggregateParentReadExpression + "->'" + columnExpression + "')) as " + column.getColumnDefinition() + ')' + ); + } + default: + return template.replace( + placeholder, + "cast(" + aggregateParentReadExpression + "->>'" + columnExpression + "' as " + column.getColumnDefinition() + ')' + ); + } + case XML_ARRAY: + case SQLXML: + switch ( column.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) { + case SQLXML: + return template.replace( + placeholder, + XML_EXTRACT_START + xmlExtractArguments( aggregateParentReadExpression, columnExpression + "/*" ) + XML_EXTRACT_END + ); + case XML_ARRAY: + if ( typeConfiguration.getCurrentBaseSqlTypeIndicators().isXmlFormatMapperLegacyFormatEnabled() ) { + throw new IllegalArgumentException( "XML array '" + columnExpression + "' in '" + aggregateParentReadExpression + "' is not supported with legacy format enabled." ); + } + else { + return template.replace( + placeholder, + "xmlelement(name \"Collection\",(select xmlagg(t.v order by t.i) from xmltable(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression + "/*" ) + " columns v xml path '.', i for ordinality)t))" + ); + } + case BINARY: + case VARBINARY: + case LONG32VARBINARY: + // We encode binary data as hex, so we have to decode here + return template.replace( + placeholder, + "decode((select t.v from xmltable(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression )+ " columns v text path '.') t),'hex')" + ); + case ARRAY: + throw new UnsupportedOperationException( "Transforming XML_ARRAY to native arrays is not supported on GaussDB!" ); + default: + return template.replace( + placeholder, + "(select t.v from xmltable(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression ) + " columns v " + column.getColumnDefinition() + " path '.') t)" + ); + } + case STRUCT: + case STRUCT_ARRAY: + case STRUCT_TABLE: + return template.replace( placeholder, '(' + aggregateParentReadExpression + ")." + columnExpression ); + } + throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumnTypeCode ); + } + + private static String xmlExtractArguments(String aggregateParentReadExpression, String xpathFragment) { + final String extractArguments; + int separatorIndex; + if ( aggregateParentReadExpression.startsWith( XML_EXTRACT_START ) + && aggregateParentReadExpression.endsWith( XML_EXTRACT_END ) + && (separatorIndex = aggregateParentReadExpression.indexOf( XML_EXTRACT_SEPARATOR )) != -1 ) { + final StringBuilder sb = new StringBuilder( aggregateParentReadExpression.length() - XML_EXTRACT_START.length() + xpathFragment.length() ); + sb.append( aggregateParentReadExpression, XML_EXTRACT_START.length(), separatorIndex ); + sb.append( '/' ); + sb.append( xpathFragment ); + sb.append( aggregateParentReadExpression, separatorIndex + 2, aggregateParentReadExpression.length() - XML_EXTRACT_END.length() ); + extractArguments = sb.toString(); + } + else if ( aggregateParentReadExpression.startsWith( XML_QUERY_START ) + && aggregateParentReadExpression.endsWith( XML_QUERY_END ) + && (separatorIndex = aggregateParentReadExpression.indexOf( XML_QUERY_SEPARATOR )) != -1 ) { + final StringBuilder sb = new StringBuilder( aggregateParentReadExpression.length() - XML_QUERY_START.length() + xpathFragment.length() ); + sb.append( aggregateParentReadExpression, XML_QUERY_START.length(), separatorIndex ); + sb.append( '/' ); + sb.append( xpathFragment ); + sb.append( aggregateParentReadExpression, separatorIndex, aggregateParentReadExpression.length() - XML_QUERY_END.length() ); + extractArguments = sb.toString(); + } + else { + extractArguments = "'/" + XmlHelper.ROOT_TAG + "/" + xpathFragment + "' passing " + aggregateParentReadExpression; + } + return extractArguments; + } + + private static String jsonCustomWriteExpression(String customWriteExpression, JdbcMapping jdbcMapping) { + final int sqlTypeCode = jdbcMapping.getJdbcType().getDefaultSqlTypeCode(); + switch ( sqlTypeCode ) { + case BINARY: + case VARBINARY: + case LONG32VARBINARY: + // We encode binary data as hex + return "to_jsonb(encode(" + customWriteExpression + ",'hex'))"; + case ARRAY: + final BasicPluralType pluralType = (BasicPluralType) jdbcMapping; + switch ( pluralType.getElementType().getJdbcType().getDefaultSqlTypeCode() ) { + case BINARY: + case VARBINARY: + case LONG32VARBINARY: + // We encode binary data as hex + return "to_jsonb(array(select encode(unnest(" + customWriteExpression + "),'hex')))"; + default: + return "to_jsonb(" + customWriteExpression + ")"; + } + default: + return "to_jsonb(" + customWriteExpression + ")"; + } + } + + private static String xmlCustomWriteExpression(String customWriteExpression, JdbcMapping jdbcMapping) { + final int sqlTypeCode = jdbcMapping.getJdbcType().getDefaultSqlTypeCode(); + switch ( sqlTypeCode ) { + case BINARY: + case VARBINARY: + case LONG32VARBINARY: + // We encode binary data as hex + return "encode(" + customWriteExpression + ",'hex')"; +// case ARRAY: +// final BasicPluralType pluralType = (BasicPluralType) jdbcMapping; +// switch ( pluralType.getElementType().getJdbcType().getDefaultSqlTypeCode() ) { +// case BINARY: +// case VARBINARY: +// case LONG32VARBINARY: +// // We encode binary data as hex +// return "to_jsonb(array(select encode(unnest(" + customWriteExpression + "),'hex')))"; +// default: +// return "to_jsonb(" + customWriteExpression + ")"; +// } + default: + return customWriteExpression; + } + } + + @Override + public String aggregateComponentAssignmentExpression( + String aggregateParentAssignmentExpression, + String columnExpression, + int aggregateColumnTypeCode, + Column column) { + switch ( aggregateColumnTypeCode ) { + case JSON: + case JSON_ARRAY: + case SQLXML: + case XML_ARRAY: + // For JSON/XML we always have to replace the whole object + return aggregateParentAssignmentExpression; + case STRUCT: + case STRUCT_ARRAY: + case STRUCT_TABLE: + return aggregateParentAssignmentExpression + "." + columnExpression; + } + throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumnTypeCode ); + } + + @Override + public boolean requiresAggregateCustomWriteExpressionRenderer(int aggregateSqlTypeCode) { + switch ( aggregateSqlTypeCode ) { + case JSON: + case SQLXML: + return true; + } + return false; + } + + @Override + public boolean preferSelectAggregateMapping(int aggregateSqlTypeCode) { + // The JDBC driver does not support selecting java.sql.Struct, so return false to select individual parts + return aggregateSqlTypeCode != STRUCT; + } + + @Override + public WriteExpressionRenderer aggregateCustomWriteExpressionRenderer( + SelectableMapping aggregateColumn, + SelectableMapping[] columnsToUpdate, + TypeConfiguration typeConfiguration) { + final int aggregateSqlTypeCode = aggregateColumn.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode(); + switch ( aggregateSqlTypeCode ) { + case JSON: + return new RootJsonWriteExpression( aggregateColumn, columnsToUpdate ); + case SQLXML: + return new RootXmlWriteExpression( aggregateColumn, columnsToUpdate ); + } + throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateSqlTypeCode ); + } + + interface JsonWriteExpression { + void append( + SqlAppender sb, + String path, + SqlAstTranslator translator, + AggregateColumnWriteExpression expression); + } + private static class AggregateJsonWriteExpression implements JsonWriteExpression { + private final LinkedHashMap subExpressions = new LinkedHashMap<>(); + + protected void initializeSubExpressions(SelectableMapping[] columns) { + for ( SelectableMapping column : columns ) { + final SelectablePath selectablePath = column.getSelectablePath(); + final SelectablePath[] parts = selectablePath.getParts(); + AggregateJsonWriteExpression currentAggregate = this; + for ( int i = 1; i < parts.length - 1; i++ ) { + currentAggregate = (AggregateJsonWriteExpression) currentAggregate.subExpressions.computeIfAbsent( + parts[i].getSelectableName(), + k -> new AggregateJsonWriteExpression() + ); + } + final String customWriteExpression = column.getWriteExpression(); + currentAggregate.subExpressions.put( + parts[parts.length - 1].getSelectableName(), + new BasicJsonWriteExpression( + column, + jsonCustomWriteExpression( customWriteExpression, column.getJdbcMapping() ) + ) + ); + } + } + + @Override + public void append( + SqlAppender sb, + String path, + SqlAstTranslator translator, + AggregateColumnWriteExpression expression) { + sb.append( "||jsonb_build_object" ); + char separator = '('; + for ( Map.Entry entry : subExpressions.entrySet() ) { + final String column = entry.getKey(); + final JsonWriteExpression value = entry.getValue(); + final String subPath = path + "->'" + column + "'"; + sb.append( separator ); + if ( value instanceof AggregateJsonWriteExpression ) { + sb.append( '\'' ); + sb.append( column ); + sb.append( "',coalesce(" ); + sb.append( subPath ); + sb.append( ",'{}')" ); + value.append( sb, subPath, translator, expression ); + } + else { + value.append( sb, subPath, translator, expression ); + } + separator = ','; + } + sb.append( ')' ); + } + } + + private static class RootJsonWriteExpression extends AggregateJsonWriteExpression + implements WriteExpressionRenderer { + private final boolean nullable; + private final String path; + + RootJsonWriteExpression(SelectableMapping aggregateColumn, SelectableMapping[] columns) { + this.nullable = aggregateColumn.isNullable(); + this.path = aggregateColumn.getSelectionExpression(); + initializeSubExpressions( columns ); + } + + @Override + public void render( + SqlAppender sqlAppender, + SqlAstTranslator translator, + AggregateColumnWriteExpression aggregateColumnWriteExpression, + String qualifier) { + final String basePath; + if ( qualifier == null || qualifier.isBlank() ) { + basePath = path; + } + else { + basePath = qualifier + "." + path; + } + if ( nullable ) { + sqlAppender.append( "coalesce(" ); + sqlAppender.append( basePath ); + sqlAppender.append( ",'{}')" ); + } + else { + sqlAppender.append( basePath ); + } + append( sqlAppender, basePath, translator, aggregateColumnWriteExpression ); + } + } + private static class BasicJsonWriteExpression implements JsonWriteExpression { + + private final SelectableMapping selectableMapping; + private final String customWriteExpressionStart; + private final String customWriteExpressionEnd; + + BasicJsonWriteExpression(SelectableMapping selectableMapping, String customWriteExpression) { + this.selectableMapping = selectableMapping; + if ( customWriteExpression.equals( "?" ) ) { + this.customWriteExpressionStart = ""; + this.customWriteExpressionEnd = ""; + } + else { + final String[] parts = StringHelper.split( "?", customWriteExpression ); + assert parts.length == 2; + this.customWriteExpressionStart = parts[0]; + this.customWriteExpressionEnd = parts[1]; + } + } + + @Override + public void append( + SqlAppender sb, + String path, + SqlAstTranslator translator, + AggregateColumnWriteExpression expression) { + sb.append( '\'' ); + sb.append( selectableMapping.getSelectableName() ); + sb.append( "'," ); + sb.append( customWriteExpressionStart ); + // We use NO_UNTYPED here so that expressions which require type inference are casted explicitly, + // since we don't know how the custom write expression looks like where this is embedded, + // so we have to be pessimistic and avoid ambiguities + translator.render( expression.getValueExpression( selectableMapping ), SqlAstNodeRenderingMode.NO_UNTYPED ); + sb.append( customWriteExpressionEnd ); + } + } + + interface XmlWriteExpression { + void append( + SqlAppender sb, + String path, + SqlAstTranslator translator, + AggregateColumnWriteExpression expression); + } + private static class AggregateXmlWriteExpression implements XmlWriteExpression { + + private final SelectableMapping selectableMapping; + private final String columnDefinition; + private final LinkedHashMap subExpressions = new LinkedHashMap<>(); + + private AggregateXmlWriteExpression(SelectableMapping selectableMapping, String columnDefinition) { + this.selectableMapping = selectableMapping; + this.columnDefinition = columnDefinition; + } + + protected void initializeSubExpressions(SelectableMapping aggregateColumn, SelectableMapping[] columns) { + for ( SelectableMapping column : columns ) { + final SelectablePath selectablePath = column.getSelectablePath(); + final SelectablePath[] parts = selectablePath.getParts(); + AggregateXmlWriteExpression currentAggregate = this; + for ( int i = 1; i < parts.length - 1; i++ ) { + final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) currentAggregate.selectableMapping.getJdbcMapping().getJdbcType(); + final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType(); + final int selectableIndex = embeddableMappingType.getSelectableIndex( parts[i].getSelectableName() ); + currentAggregate = (AggregateXmlWriteExpression) currentAggregate.subExpressions.computeIfAbsent( + parts[i].getSelectableName(), + k -> new AggregateXmlWriteExpression( embeddableMappingType.getJdbcValueSelectable( selectableIndex ), columnDefinition ) + ); + } + final String customWriteExpression = column.getWriteExpression(); + currentAggregate.subExpressions.put( + parts[parts.length - 1].getSelectableName(), + new BasicXmlWriteExpression( + column, + xmlCustomWriteExpression( customWriteExpression, column.getJdbcMapping() ) + ) + ); + } + passThroughUnsetSubExpressions( aggregateColumn ); + } + + protected void passThroughUnsetSubExpressions(SelectableMapping aggregateColumn) { + final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) aggregateColumn.getJdbcMapping().getJdbcType(); + final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType(); + final int jdbcValueCount = embeddableMappingType.getJdbcValueCount(); + for ( int i = 0; i < jdbcValueCount; i++ ) { + final SelectableMapping selectableMapping = embeddableMappingType.getJdbcValueSelectable( i ); + + final XmlWriteExpression xmlWriteExpression = subExpressions.get( selectableMapping.getSelectableName() ); + if ( xmlWriteExpression == null ) { + subExpressions.put( + selectableMapping.getSelectableName(), + new PassThroughXmlWriteExpression( selectableMapping ) + ); + } + else if ( xmlWriteExpression instanceof AggregateXmlWriteExpression writeExpression ) { + writeExpression.passThroughUnsetSubExpressions( selectableMapping ); + } + } + } + + protected String getTagName() { + return selectableMapping.getSelectableName(); + } + + @Override + public void append( + SqlAppender sb, + String path, + SqlAstTranslator translator, + AggregateColumnWriteExpression expression) { + sb.append( "xmlelement(name " ); + sb.appendDoubleQuoteEscapedString( getTagName() ); + sb.append( ",xmlconcat" ); + char separator = '('; + for ( Map.Entry entry : subExpressions.entrySet() ) { + sb.append( separator ); + + final XmlWriteExpression value = entry.getValue(); + if ( value instanceof AggregateXmlWriteExpression ) { + final String subPath = XML_QUERY_START + xmlExtractArguments( path, entry.getKey() ) + XML_QUERY_END; + value.append( sb, subPath, translator, expression ); + } + else { + value.append( sb, path, translator, expression ); + } + separator = ','; + } + sb.append( "))" ); + } + } + + private static class RootXmlWriteExpression extends AggregateXmlWriteExpression + implements WriteExpressionRenderer { + private final String path; + + RootXmlWriteExpression(SelectableMapping aggregateColumn, SelectableMapping[] columns) { + super( aggregateColumn, aggregateColumn.getColumnDefinition() ); + path = aggregateColumn.getSelectionExpression(); + initializeSubExpressions( aggregateColumn, columns ); + } + + @Override + protected String getTagName() { + return XmlHelper.ROOT_TAG; + } + + @Override + public void render( + SqlAppender sqlAppender, + SqlAstTranslator translator, + AggregateColumnWriteExpression aggregateColumnWriteExpression, + String qualifier) { + final String basePath; + if ( qualifier == null || qualifier.isBlank() ) { + basePath = path; + } + else { + basePath = qualifier + "." + path; + } + append( sqlAppender, XML_QUERY_START + "'/" + getTagName() + "' passing " + basePath + XML_QUERY_END, translator, aggregateColumnWriteExpression ); + } + } + private static class BasicXmlWriteExpression implements XmlWriteExpression { + + private final SelectableMapping selectableMapping; + private final String[] customWriteExpressionParts; + + BasicXmlWriteExpression(SelectableMapping selectableMapping, String customWriteExpression) { + this.selectableMapping = selectableMapping; + if ( customWriteExpression.equals( "?" ) ) { + this.customWriteExpressionParts = new String[]{ "", "" }; + } + else { + assert !customWriteExpression.startsWith( "?" ); + final String[] parts = StringHelper.split( "?", customWriteExpression ); + assert parts.length == 2 || (parts.length & 1) == 1; + this.customWriteExpressionParts = parts; + } + } + + @Override + public void append( + SqlAppender sb, + String path, + SqlAstTranslator translator, + AggregateColumnWriteExpression expression) { + final JdbcType jdbcType = selectableMapping.getJdbcMapping().getJdbcType(); + final boolean isArray = jdbcType.getDefaultSqlTypeCode() == XML_ARRAY; + sb.append( "xmlelement(name " ); + sb.appendDoubleQuoteEscapedString( selectableMapping.getSelectableName() ); + sb.append( ',' ); + if ( isArray ) { + // Remove the tag to wrap the value into the selectable specific tag + sb.append( "(select xmlagg(t.v order by t.i) from xmltable('/Collection/*' passing " ); + } + sb.append( customWriteExpressionParts[0] ); + for ( int i = 1; i < customWriteExpressionParts.length; i++ ) { + // We use NO_UNTYPED here so that expressions which require type inference are casted explicitly, + // since we don't know how the custom write expression looks like where this is embedded, + // so we have to be pessimistic and avoid ambiguities + translator.render( expression.getValueExpression( selectableMapping ), SqlAstNodeRenderingMode.NO_UNTYPED ); + sb.append( customWriteExpressionParts[i] ); + } + if ( isArray ) { + sb.append( " columns v xml path '.', i for ordinality)t)" ); + } + sb.append( ')' ); + } + } + + private static class PassThroughXmlWriteExpression implements XmlWriteExpression { + + private final SelectableMapping selectableMapping; + + PassThroughXmlWriteExpression(SelectableMapping selectableMapping) { + this.selectableMapping = selectableMapping; + } + + @Override + public void append( + SqlAppender sb, + String path, + SqlAstTranslator translator, + AggregateColumnWriteExpression expression) { + sb.append( XML_QUERY_START ); + sb.append( xmlExtractArguments( path, selectableMapping.getSelectableName() ) ); + sb.append( XML_QUERY_END ); + } + } + +} 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 5dfe7936ab3f..10629c63b550 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 @@ -14,6 +14,7 @@ import org.hibernate.dialect.function.array.*; import org.hibernate.dialect.function.json.*; import org.hibernate.dialect.function.xml.DB2XmlTableFunction; +import org.hibernate.dialect.function.xml.GaussDBXmlQueryFunction; import org.hibernate.dialect.function.xml.H2XmlConcatFunction; import org.hibernate.dialect.function.xml.H2XmlElementFunction; import org.hibernate.dialect.function.xml.H2XmlForestFunction; @@ -2572,6 +2573,15 @@ public void format_toChar() { functionRegistry.register( "format", new FormatFunction( "to_char", typeConfiguration ) ); } + /** + * Usually Oracle-style (except for Informix which quite close to MySQL-style) + * + * @see org.hibernate.dialect.OracleDialect#datetimeFormat + */ + public void format_toChar_gauss() { + functionRegistry.register( "format", new GaussDBFormatFunction( "to_char", typeConfiguration ) ); + } + /** * MySQL-style (also Ingres) * @@ -2662,6 +2672,14 @@ public void array_postgresql() { functionRegistry.register( "array_list", new PostgreSQLArrayConstructorFunction( true ) ); } + /** + * GaussDB array() constructor function + */ + public void array_gaussdb() { + functionRegistry.register( "array", new GaussDBArrayConstructorFunction( false ) ); + functionRegistry.register( "array_list", new GaussDBArrayConstructorFunction( true ) ); + } + /** * Google Spanner array() constructor function */ @@ -2746,6 +2764,16 @@ public void arrayContains_postgresql() { functionRegistry.register( "array_includes_nullable", new ArrayIncludesOperatorFunction( true, typeConfiguration ) ); } + /** + * GaussDB array contains operator + */ + public void arrayContains_gaussdb() { + functionRegistry.register( "array_contains", new GaussDBArrayContainsOperatorFunction( false, typeConfiguration ) ); + functionRegistry.register( "array_contains_nullable", new GaussDBArrayContainsOperatorFunction( true, typeConfiguration ) ); + functionRegistry.register( "array_includes", new ArrayIncludesOperatorFunction( false, typeConfiguration ) ); + functionRegistry.register( "array_includes_nullable", new ArrayIncludesOperatorFunction( true, typeConfiguration ) ); + } + /** * Oracle array_contains() function */ @@ -2798,6 +2826,16 @@ public void arrayIntersects_postgresql() { functionRegistry.registerAlternateKey( "array_overlaps_nullable", "array_intersects_nullable" ); } + /** + * GaussDB array intersects operator + */ + public void arrayIntersects_gaussdb() { + functionRegistry.register( "array_intersects", new ArrayIntersectsOperatorFunction( false, typeConfiguration ) ); + functionRegistry.register( "array_intersects_nullable", new ArrayIntersectsOperatorFunction( true, typeConfiguration ) ); + functionRegistry.registerAlternateKey( "array_overlaps", "array_intersects" ); + functionRegistry.registerAlternateKey( "array_overlaps_nullable", "array_intersects_nullable" ); + } + /** * Oracle array_intersects() function */ @@ -2821,6 +2859,13 @@ public void arrayPosition_postgresql() { functionRegistry.register( "array_position", new PostgreSQLArrayPositionFunction( typeConfiguration ) ); } + /** + * GaussDB array_position() function + */ + public void arrayPosition_gaussdb() { + functionRegistry.register( "array_position", new GaussDBArrayPositionFunction( typeConfiguration ) ); + } + /** * H2 array_position() function */ @@ -2937,6 +2982,13 @@ public void arrayConcat_postgresql() { functionRegistry.register( "array_concat", new PostgreSQLArrayConcatFunction() ); } + /** + * PostgreSQL array_concat() function + */ + public void arrayConcat_gaussdb() { + functionRegistry.register( "array_concat", new GaussDBArrayConcatFunction() ); + } + /** * Oracle array_concat() function */ @@ -2958,6 +3010,13 @@ public void arrayPrepend_postgresql() { functionRegistry.register( "array_prepend", new PostgreSQLArrayConcatElementFunction( true ) ); } + /** + * GaussDB array_prepend() function + */ + public void arrayPrepend_gaussdb() { + functionRegistry.register( "array_prepend", new GaussDBArrayConcatElementFunction( true ) ); + } + /** * Oracle array_prepend() function */ @@ -2979,6 +3038,13 @@ public void arrayAppend_postgresql() { functionRegistry.register( "array_append", new PostgreSQLArrayConcatElementFunction( false ) ); } + /** + * GaussDB array_append() function + */ + public void arrayAppend_gaussdb() { + functionRegistry.register( "array_append", new GaussDBArrayConcatElementFunction( false ) ); + } + /** * Oracle array_append() function */ @@ -3054,6 +3120,13 @@ public void arraySet_unnest() { functionRegistry.register( "array_set", new ArraySetUnnestFunction() ); } + /** + * GaussDB array_set() function + */ + public void arraySet_gaussdb() { + functionRegistry.register( "array_set", new GaussDBArraySetFunction() ); + } + /** * Oracle array_set() function */ @@ -3084,6 +3157,13 @@ public void arrayRemove_h2(int maximumArraySize) { functionRegistry.register( "array_remove", new H2ArrayRemoveFunction( maximumArraySize ) ); } + /** + * GaussDB array_remove() function + */ + public void arrayRemove_gaussdb() { + functionRegistry.register( "array_remove", new GaussDBArrayRemoveFunction()); + } + /** * HSQL array_remove() function */ @@ -3098,6 +3178,13 @@ public void arrayRemove_oracle() { functionRegistry.register( "array_remove", new OracleArrayRemoveFunction() ); } + /** + * GaussDB array_remove_index() function + */ + public void arrayRemoveIndex_gaussdb() { + functionRegistry.register( "array_remove_index", new GaussDBArrayRemoveIndexFunction(false) ); + } + /** * H2 array_remove_index() function */ @@ -3215,6 +3302,13 @@ public void arrayReplace_oracle() { functionRegistry.register( "array_replace", new OracleArrayReplaceFunction() ); } + /** + * GaussDB array_replace() function + */ + public void arrayReplace_gaussdb() { + functionRegistry.register( "array_replace", new GaussDBArrayReplaceFunction() ); + } + /** * H2, HSQLDB, CockroachDB and PostgreSQL array_trim() function */ @@ -3251,6 +3345,13 @@ public void arrayTrim_oracle() { functionRegistry.register( "array_trim", new OracleArrayTrimFunction() ); } + /** + * GaussDB array_trim() emulation for versions before 14 + */ + public void arrayTrim_gaussdb() { + functionRegistry.register( "array_trim", new GaussDBArrayTrimFunction() ); + } + /** * H2 array_fill() function */ @@ -3275,6 +3376,14 @@ public void arrayFill_postgresql() { functionRegistry.register( "array_fill_list", new PostgreSQLArrayFillFunction( true ) ); } + /** + * GaussDB array_fill() function + */ + public void arrayFill_gaussdb() { + functionRegistry.register( "array_fill", new GaussDBArrayFillFunction( false ) ); + functionRegistry.register( "array_fill_list", new GaussDBArrayFillFunction( true ) ); + } + /** * Cockroach array_fill() function */ @@ -3347,6 +3456,13 @@ public void jsonValue_postgresql(boolean supportsStandard) { functionRegistry.register( "json_value", new PostgreSQLJsonValueFunction( supportsStandard, typeConfiguration ) ); } + /** + * GaussDB json_value() function + */ + public void jsonValue_gaussdb(boolean supportsStandard) { + functionRegistry.register( "json_value", new GaussDBJsonValueFunction( typeConfiguration ) ); + } + /** * CockroachDB json_value() function */ @@ -3389,6 +3505,13 @@ public void jsonQuery() { functionRegistry.register( "json_query", new JsonQueryFunction( typeConfiguration, true, true ) ); } + /** + * GaussDB json_query() function + */ + public void jsonQuery_gaussdb() { + functionRegistry.register( "json_query", new GaussDBJsonQueryFunction( typeConfiguration ) ); + } + /** * json_query() function */ @@ -3452,6 +3575,13 @@ public void jsonExists() { functionRegistry.register( "json_exists", new JsonExistsFunction( typeConfiguration, true, true ) ); } + /** + * json_exists() function + */ + public void jsonExists_gaussdb() { + functionRegistry.register( "json_exists", new GaussDBJsonExistsFunction( typeConfiguration, false, false ) ); + } + /** * json_exists() function that doesn't support the passing clause */ @@ -3564,6 +3694,13 @@ public void jsonObject_postgresql() { functionRegistry.register( "json_object", new PostgreSQLJsonObjectFunction( typeConfiguration ) ); } + /** + * GaussDB json_object() function + */ + public void jsonObject_gaussdb() { + functionRegistry.register( "json_object", new GaussDBJsonObjectFunction( typeConfiguration ) ); + } + /** * json_array() function */ @@ -3592,6 +3729,13 @@ public void jsonArray_sqlserver(boolean supportsExtendedJson) { functionRegistry.register( "json_array", new SQLServerJsonArrayFunction( supportsExtendedJson, typeConfiguration ) ); } + /** + * GaussDB json_array() function + */ + public void jsonArray_gaussdb() { + functionRegistry.register( "json_array", new GaussDBJsonArrayFunction( typeConfiguration ) ); + } + /** * SAP HANA json_array() function */ @@ -3655,6 +3799,13 @@ public void jsonArrayAgg_postgresql(boolean supportsStandard) { functionRegistry.register( "json_arrayagg", new PostgreSQLJsonArrayAggFunction( supportsStandard, typeConfiguration ) ); } + /** + * GaussDB json_arrayagg() function + */ + public void jsonArrayAgg_gaussdb(boolean supportsStandard) { + functionRegistry.register( "json_arrayagg", new GaussDBJsonArrayAggFunction( supportsStandard, typeConfiguration ) ); + } + /** * SQL Server json_arrayagg() function */ @@ -3711,6 +3862,13 @@ public void jsonObjectAgg_postgresql(boolean supportsStandard) { functionRegistry.register( "json_objectagg", new PostgreSQLJsonObjectAggFunction( supportsStandard, typeConfiguration ) ); } + /** + * GaussDB json_objectagg() function + */ + public void jsonObjectAgg_gaussdb(boolean supportsStandard) { + functionRegistry.register( "json_objectagg", new GaussDBJsonObjectAggFunction( supportsStandard, typeConfiguration ) ); + } + /** * MySQL json_objectagg() function */ @@ -3753,6 +3911,13 @@ public void jsonSet_postgresql() { functionRegistry.register( "json_set", new PostgreSQLJsonSetFunction( typeConfiguration ) ); } + /** + * GaussDB json_set() function + */ + public void jsonSet_gaussdb() { + functionRegistry.register( "json_set", new GaussDBJsonSetFunction( typeConfiguration ) ); + } + /** * MySQL json_set() function */ @@ -3791,6 +3956,13 @@ public void jsonRemove_postgresql() { functionRegistry.register( "json_remove", new PostgreSQLJsonRemoveFunction( typeConfiguration ) ); } + /** + * GaussDB json_remove() function + */ + public void jsonRemove_gaussdb() { + functionRegistry.register( "json_remove", new GaussDBJsonRemoveFunction( typeConfiguration ) ); + } + /** * CockroachDB json_remove() function */ @@ -3835,6 +4007,13 @@ public void jsonReplace_postgresql() { functionRegistry.register( "json_replace", new PostgreSQLJsonReplaceFunction( typeConfiguration ) ); } + /** + * GaussDB json_replace() function + */ + public void jsonReplace_gaussdb() { + functionRegistry.register( "json_replace", new GaussDBJsonReplaceFunction( typeConfiguration ) ); + } + /** * MySQL json_replace() function */ @@ -3873,6 +4052,13 @@ public void jsonInsert_postgresql() { functionRegistry.register( "json_insert", new PostgreSQLJsonInsertFunction( typeConfiguration ) ); } + /** + * GaussDB json_insert() function + */ + public void jsonInsert_gaussdb() { + functionRegistry.register( "json_insert", new GaussDBJsonInsertFunction( typeConfiguration ) ); + } + /** * MySQL json_insert() function */ @@ -3911,6 +4097,13 @@ public void jsonMergepatch_postgresql() { functionRegistry.register( "json_mergepatch", new PostgreSQLJsonMergepatchFunction( typeConfiguration ) ); } + /** + * GaussDB json_mergepatch() function + */ + public void jsonMergepatch_gaussdb() { + functionRegistry.register( "json_mergepatch", new GaussDBJsonMergepatchFunction( typeConfiguration ) ); + } + /** * MySQL json_mergepatch() function */ @@ -3941,6 +4134,13 @@ public void jsonArrayAppend_postgresql(boolean supportsLax) { functionRegistry.register( "json_array_append", new PostgreSQLJsonArrayAppendFunction( supportsLax, typeConfiguration ) ); } + /** + * GaussDB json_array_append() function + */ + public void jsonArrayAppend_gaussdb(boolean supportsLax) { + functionRegistry.register( "json_array_append", new GaussDBJsonArrayAppendFunction( supportsLax, typeConfiguration ) ); + } + /** * MySQL json_array_append() function */ @@ -3986,6 +4186,13 @@ public void jsonArrayInsert_postgresql() { functionRegistry.register( "json_array_insert", new PostgreSQLJsonArrayInsertFunction( typeConfiguration ) ); } + /** + * gauss json_array_insert() function + */ + public void jsonArrayInsert_gauss() { + functionRegistry.register( "json_array_insert", new GaussDBJsonArrayInsertFunction( typeConfiguration ) ); + } + /** * MySQL json_array_insert() function */ @@ -4151,6 +4358,13 @@ public void xmlquery_postgresql() { functionRegistry.register( "xmlquery", new PostgreSQLXmlQueryFunction( typeConfiguration ) ); } + /** + * GaussDB xmlquery() function + */ + public void xmlquery_gaussdb() { + functionRegistry.register( "xmlquery", new GaussDBXmlQueryFunction( typeConfiguration ) ); + } + /** * SQL Server xmlquery() function */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java new file mode 100644 index 000000000000..34ddc3301382 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java @@ -0,0 +1,791 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import org.hibernate.dialect.Dialect; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.metamodel.mapping.MappingModelExpressible; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.query.common.TemporalUnit; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor; +import org.hibernate.query.sqm.function.FunctionRenderer; +import org.hibernate.query.sqm.function.MultipatternSqmFunctionDescriptor; +import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; +import org.hibernate.query.sqm.function.SelfRenderingSqmFunction; +import org.hibernate.query.sqm.function.SqmFunctionDescriptor; +import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; +import org.hibernate.query.sqm.produce.function.ArgumentsValidator; +import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.spi.StringBuilderSqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; +import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; +import org.hibernate.sql.ast.tree.expression.DurationUnit; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.Format; +import org.hibernate.sql.ast.tree.expression.QueryLiteral; +import org.hibernate.sql.ast.tree.expression.SqlTuple; +import org.hibernate.sql.ast.tree.expression.SqlTupleContainer; +import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; +import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; +import org.hibernate.type.BasicType; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.ArrayList; +import java.util.List; + +import static org.hibernate.internal.util.StringHelper.splitFull; +import static org.hibernate.query.sqm.BinaryArithmeticOperator.DIVIDE_PORTABLE; +import static org.hibernate.query.sqm.BinaryArithmeticOperator.MODULO; +import static org.hibernate.query.sqm.ComparisonOperator.GREATER_THAN_OR_EQUAL; +import static org.hibernate.query.sqm.ComparisonOperator.LESS_THAN; +import static org.hibernate.query.sqm.ComparisonOperator.LESS_THAN_OR_EQUAL; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL; +import static org.hibernate.query.sqm.produce.function.StandardArgumentsValidators.exactly; +import static org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers.invariant; +import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.invariant; + +/** + * A format function with support for composite temporal expressions. + * + * @author Christian Beikov + * + * Notes: Original code of this class is based on FormatFunction. + * + */ +public class GaussDBFormatFunction extends AbstractSqmFunctionDescriptor implements FunctionRenderer { + + private final String nativeFunctionName; + private final boolean reversedArguments; + private final boolean concatPattern; + private final boolean supportsTime; + + public GaussDBFormatFunction(String nativeFunctionName, TypeConfiguration typeConfiguration) { + this( nativeFunctionName, false, true, typeConfiguration ); + } + + public GaussDBFormatFunction( + String nativeFunctionName, + boolean reversedArguments, + boolean concatPattern, + TypeConfiguration typeConfiguration) { + this( nativeFunctionName, reversedArguments, concatPattern, true, typeConfiguration ); + } + + public GaussDBFormatFunction( + String nativeFunctionName, + boolean reversedArguments, + boolean concatPattern, + boolean supportsTime, + TypeConfiguration typeConfiguration) { + super( + "format", + new ArgumentTypesValidator( exactly( 2 ), TEMPORAL, STRING ), + invariant( typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.STRING ) ), + invariant( typeConfiguration, TEMPORAL, STRING ) + ); + this.nativeFunctionName = nativeFunctionName; + this.reversedArguments = reversedArguments; + this.concatPattern = concatPattern; + this.supportsTime = supportsTime; + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + sqlAppender.appendSql( nativeFunctionName ); + sqlAppender.append( '(' ); + final SqlAstNode expression = sqlAstArguments.get( 0 ); + final SqlAstNode format = sqlAstArguments.get( 1 ); + if ( reversedArguments ) { + format.accept( walker ); + sqlAppender.append( ',' ); + if ( !supportsTime && isTimeTemporal( expression ) ) { + sqlAppender.append( "date'1970-01-01'+" ); + } + expression.accept( walker ); + } + else { + if ( !supportsTime && isTimeTemporal( expression ) ) { + sqlAppender.append( "date'1970-01-01'+" ); + } + expression.accept( walker ); + sqlAppender.append( ',' ); + format.accept( walker ); + } + sqlAppender.append( ')' ); + } + + private boolean isTimeTemporal(SqlAstNode expression) { + if ( expression instanceof Expression ) { + final JdbcMappingContainer expressionType = ( (Expression) expression ).getExpressionType(); + if ( expressionType.getJdbcTypeCount() == 1 ) { + switch ( expressionType.getSingleJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) { + case SqlTypes.TIME: + case SqlTypes.TIME_WITH_TIMEZONE: + case SqlTypes.TIME_UTC: + return true; + default: + break; + } + } + } + return false; + } + + @Override + protected SelfRenderingSqmFunction generateSqmFunctionExpression( + List> arguments, + ReturnableType impliedResultType, + QueryEngine queryEngine) { + return new FormatSqmFunction<>( + this, + this, + arguments, + impliedResultType, + getArgumentsValidator(), + getReturnTypeResolver(), + concatPattern, + queryEngine + ); + } + + @Override + public String getArgumentListSignature() { + return "(TEMPORAL datetime as STRING pattern)"; + } + + protected static class FormatSqmFunction extends SelfRenderingSqmFunction { + + private final boolean supportsPatternLiterals; + private final TypeConfiguration typeConfiguration; + private QueryEngine queryEngine; + + public FormatSqmFunction( + SqmFunctionDescriptor descriptor, + FunctionRenderer renderer, + List> arguments, + ReturnableType impliedResultType, + ArgumentsValidator argumentsValidator, + FunctionReturnTypeResolver returnTypeResolver, + boolean supportsPatternLiterals, + QueryEngine queryEngine) { + super( + descriptor, + renderer, + arguments, + impliedResultType, + argumentsValidator, + returnTypeResolver, + queryEngine.getCriteriaBuilder(), + "format" + ); + this.supportsPatternLiterals = supportsPatternLiterals; + this.typeConfiguration = queryEngine.getTypeConfiguration(); + this.queryEngine = queryEngine; + } + + @Override + public Expression convertToSqlAst(SqmToSqlAstConverter walker) { + final List arguments = resolveSqlAstArguments( getArguments(), walker ); + final ReturnableType resultType = resolveResultType( walker ); + final MappingModelExpressible mappingModelExpressible = + resultType == null + ? null + : getMappingModelExpressible( walker, resultType, arguments ); + final SqlAstNode expression = arguments.get( 0 ); + if ( expression instanceof SqlTupleContainer ) { + // SqlTupleContainer means this is a composite temporal type i.e. uses `@TimeZoneStorage(COLUMN)` + // The support for this kind of type requires that we inject the offset from the second column + // as literal into the pattern, and apply the formatting on the date time part + final SqlTuple sqlTuple = ( (SqlTupleContainer) expression ).getSqlTuple(); + final FunctionRenderer timestampaddFunction = getFunction( walker, "timestampadd" ); + final BasicType integerType = typeConfiguration.getBasicTypeRegistry() + .resolve( StandardBasicTypes.INTEGER ); + arguments.set( 0, getOffsetAdjusted( sqlTuple, timestampaddFunction, integerType ) ); + if ( getArgumentsValidator() != null ) { + getArgumentsValidator().validateSqlTypes( arguments, getFunctionName() ); + } + final Format format = (Format) arguments.get( 1 ); + // If the format contains a time zone or offset, we must replace that with the offset column + if ( format.getFormat().contains( "x" ) || !supportsPatternLiterals ) { + final FunctionRenderer concatFunction = getFunction( walker, "concat" ); + final FunctionRenderer substringFunction = getFunction( walker, "substring", 3 ); + final BasicType stringType = typeConfiguration.getBasicTypeRegistry() + .resolve( StandardBasicTypes.STRING ); + final Dialect dialect = walker.getCreationContext().getDialect(); + Expression formatExpression = null; + final StringBuilder sb = new StringBuilder(); + final StringBuilderSqlAppender sqlAppender = new StringBuilderSqlAppender( sb ); + final String delimiter; + if ( supportsPatternLiterals ) { + dialect.appendDatetimeFormat( sqlAppender, "'a'" ); + delimiter = sb.substring( 0, sb.indexOf( "a" ) ).replace( "''", "'" ); + } + else { + delimiter = ""; + } + final String[] chunks = splitFull( "'", format.getFormat() ); + final Expression offsetExpression = sqlTuple.getExpressions().get( 1 ); + // Splitting by `'` will put actual format pattern parts to even indices and literal pattern parts + // to uneven indices. We will only replace the time zone and offset pattern in the format pattern parts + for ( int i = 0; i < chunks.length; i += 2 ) { + // The general idea is to replace the various patterns `xxx`, `xx` and `x` by concatenating + // the offset column as literal i.e. `HH:mmxxx` is translated to `HH:mm'''||offset||'''` + // xxx stands for the full offset i.e. `+01:00` + // xx stands for the medium offset i.e. `+0100` + // x stands for the small offset i.e. `+01` + final String[] fullParts = splitFull( "xxx", chunks[i] ); + for ( int j = 0; j < fullParts.length; j++ ) { + if ( fullParts[j].isEmpty() ) { + continue; + } + final String[] mediumParts = splitFull( "xx", fullParts[j] ); + for ( int k = 0; k < mediumParts.length; k++ ) { + if ( mediumParts[k].isEmpty() ) { + continue; + } + final String[] smallParts = splitFull( "x", mediumParts[k] ); + for ( int l = 0; l < smallParts.length; l++ ) { + if ( smallParts[l].isEmpty() ) { + continue; + } + sb.setLength( 0 ); + dialect.appendDatetimeFormat( sqlAppender, smallParts[l] ); + final String formatPart = sb.toString(); + if ( supportsPatternLiterals ) { + formatExpression = concat( + concatFunction, + stringType, + formatExpression, + new QueryLiteral<>( formatPart, stringType ) + ); + } + else { + formatExpression = concat( + concatFunction, + stringType, + formatExpression, + new SelfRenderingFunctionSqlAstExpression( + getFunctionName(), + getFunctionRenderer(), + List.of( + arguments.get( 0 ), + new QueryLiteral<>( formatPart, stringType ) + ), + resultType, + mappingModelExpressible + ) + ); + } + if ( l + 1 < smallParts.length ) { + // This is for `x` patterns, which require `+01` + // so we concat `substring(offset, 1, 4)` + // Since the offset is always in the full format + formatExpression = concatAsLiteral( + concatFunction, + stringType, + delimiter, + formatExpression, + createSmallOffset( + concatFunction, + substringFunction, + stringType, + integerType, + offsetExpression + ) + ); + } + } + if ( k + 1 < mediumParts.length ) { + // This is for `xx` patterns, which require `+0100` + // so we concat `substring(offset, 1, 4)||substring(offset, 4, 6)` + // Since the offset is always in the full format + formatExpression = concatAsLiteral( + concatFunction, + stringType, + delimiter, + formatExpression, + createMediumOffset( + concatFunction, + substringFunction, + stringType, + integerType, + offsetExpression + ) + ); + } + } + if ( j + 1 < fullParts.length ) { + formatExpression = concatAsLiteral( + concatFunction, + stringType, + delimiter, + formatExpression, + createFullOffset( + concatFunction, + stringType, + integerType, + offsetExpression + ) + ); + } + } + + if ( i + 1 < chunks.length ) { + // Handle the pattern literal content + final String formatLiteralPart; + if ( supportsPatternLiterals ) { + sb.setLength( 0 ); + dialect.appendDatetimeFormat( sqlAppender, "'" + chunks[i + 1] + "'" ); + formatLiteralPart = sb.toString().replace( "''", "'" ); + } + else { + formatLiteralPart = chunks[i + 1]; + } + formatExpression = concat( + concatFunction, + stringType, + formatExpression, + new QueryLiteral<>( + formatLiteralPart, + stringType + ) + ); + } + } + + if ( supportsPatternLiterals ) { + arguments.set( 1, formatExpression ); + } + else { + return formatExpression; + } + } + } + else { + if ( getArgumentsValidator() != null ) { + getArgumentsValidator().validateSqlTypes( arguments, getFunctionName() ); + } + + if ( !supportsPatternLiterals ) { + final FunctionRenderer concatFunction = getFunction( walker, "concat" ); + final BasicType stringType = typeConfiguration.getBasicTypeRegistry() + .resolve( StandardBasicTypes.STRING ); + Expression formatExpression = null; + final Format format = (Format) arguments.get( 1 ); + final String[] chunks = splitFull( "'", format.getFormat() ); + // Splitting by `'` will put actual format pattern parts to even indices and literal pattern parts + // to uneven indices. We need to apply the format parts and then concatenate because the pattern + // doesn't support literals + for ( int i = 0; i < chunks.length; i += 2 ) { + formatExpression = concat( + concatFunction, + stringType, + formatExpression, + new SelfRenderingFunctionSqlAstExpression( + getFunctionName(), + getFunctionRenderer(), + List.of( arguments.get( 0 ), new Format( chunks[i] ) ), + resultType, + mappingModelExpressible + ) + ); + if ( i + 1 < chunks.length ) { + // Handle the pattern literal content + formatExpression = concat( + concatFunction, + stringType, + formatExpression, + new QueryLiteral<>( chunks[i + 1], stringType ) + ); + } + } + return formatExpression; + } + } + return new SelfRenderingFunctionSqlAstExpression( + getFunctionName(), + getFunctionRenderer(), + arguments, + resultType, + mappingModelExpressible + ); + } + + private FunctionRenderer getFunction(SqmToSqlAstConverter walker, String name) { + return (FunctionRenderer) + walker.getCreationContext().getSqmFunctionRegistry().findFunctionDescriptor( name ); + } + + private FunctionRenderer getFunction(SqmToSqlAstConverter walker, String name, int argumentCount) { + final SqmFunctionDescriptor functionDescriptor = + walker.getCreationContext().getSqmFunctionRegistry() + .findFunctionDescriptor( name ); + if ( functionDescriptor instanceof MultipatternSqmFunctionDescriptor multipatternSqmFunctionDescriptor ) { + return (FunctionRenderer) multipatternSqmFunctionDescriptor.getFunction( argumentCount ); + } + else { + return (FunctionRenderer) functionDescriptor; + } + } + + private SqlAstNode getOffsetAdjusted( + SqlTuple sqlTuple, + FunctionRenderer timestampaddFunction, + BasicType integerType) { + final Expression instantExpression = sqlTuple.getExpressions().get( 0 ); + final Expression offsetExpression = sqlTuple.getExpressions().get( 1 ); + + return new SelfRenderingFunctionSqlAstExpression( + "timestampadd", + timestampaddFunction, + List.of( + new DurationUnit( TemporalUnit.SECOND, integerType ), + offsetExpression, + instantExpression + ), + (ReturnableType) instantExpression.getExpressionType(), + instantExpression.getExpressionType() + ); + } + + private Expression createFullOffset( + FunctionRenderer concatFunction, + BasicType stringType, + BasicType integerType, + Expression offsetExpression) { + if ( offsetExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString() ) { + return offsetExpression; + } + else { + // ZoneOffset as seconds + final CaseSearchedExpression caseSearchedExpression = + zoneOffsetSeconds( stringType, integerType, offsetExpression ); + final Expression hours = getHours( integerType, offsetExpression ); + final Expression minutes = getMinutes( integerType, offsetExpression ); + + final CaseSearchedExpression minuteStart = new CaseSearchedExpression( stringType ); + minuteStart.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new LessThanPredicate( + minutes, + new QueryLiteral<>( 10, integerType ), + false, + null + ), + new QueryLiteral<>( ":0", stringType ) + ) + ); + minuteStart.otherwise( new QueryLiteral<>( ":", stringType ) ); + return concat( + concatFunction, + stringType, + concat( + concatFunction, + stringType, + concat( concatFunction, stringType, caseSearchedExpression, hours ), + minuteStart + ), + minutes + ); + } + } + + private Expression createMediumOffset( + FunctionRenderer concatFunction, + FunctionRenderer substringFunction, + BasicType stringType, + BasicType integerType, + Expression offsetExpression) { + if ( offsetExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString() ) { + return concat( + concatFunction, + stringType, + createSmallOffset( + concatFunction, + substringFunction, + stringType, + integerType, + offsetExpression + ), + new SelfRenderingFunctionSqlAstExpression( + "substring", + substringFunction, + List.of( + offsetExpression, + new QueryLiteral<>( 4, integerType ), + new QueryLiteral<>( 6, integerType ) + ), + stringType, + stringType + ) + ); + } + else { + // ZoneOffset as seconds + final CaseSearchedExpression caseSearchedExpression = + zoneOffsetSeconds( stringType, integerType, offsetExpression ); + + final Expression hours = getHours( integerType, offsetExpression ); + final Expression minutes = getMinutes( integerType, offsetExpression ); + + final CaseSearchedExpression minuteStart = new CaseSearchedExpression( stringType ); + minuteStart.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new LessThanPredicate( + minutes, + new QueryLiteral<>( 10, integerType ), + false, + null + ), + new QueryLiteral<>( "0", stringType ) + ) + ); + minuteStart.otherwise( new QueryLiteral<>( "", stringType ) ); + return concat( + concatFunction, + stringType, + concat( + concatFunction, + stringType, + concat( concatFunction, stringType, caseSearchedExpression, hours ), + minuteStart + ), + minutes + ); + } + } + + private Expression createSmallOffset( + FunctionRenderer concatFunction, + FunctionRenderer substringFunction, + BasicType stringType, + BasicType integerType, + Expression offsetExpression) { + if ( offsetExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString() ) { + return new SelfRenderingFunctionSqlAstExpression( + "substring", + substringFunction, + List.of( + offsetExpression, + new QueryLiteral<>( 1, integerType ), + new QueryLiteral<>( 4, integerType ) + ), + stringType, + stringType + ); + } + else { + // ZoneOffset as seconds + final CaseSearchedExpression caseSearchedExpression = + zoneOffsetSeconds( stringType, integerType, offsetExpression ); + final Expression hours = getHours( integerType, offsetExpression ); + return concat( concatFunction, stringType, caseSearchedExpression, hours ); + } + } + + private Expression concatAsLiteral( + FunctionRenderer concatFunction, + BasicType stringType, + String delimiter, + Expression expression, + Expression expression2) { + return concat( + concatFunction, + stringType, + concat( + concatFunction, + stringType, + concat( + concatFunction, + stringType, + expression, + new QueryLiteral<>( delimiter, stringType ) + ), + expression2 + ), + new QueryLiteral<>( delimiter, stringType ) + ); + } + + private Expression concat( + FunctionRenderer concatFunction, + BasicType stringType, + Expression expression, + Expression expression2) { + if ( expression == null ) { + return expression2; + } + else if ( expression instanceof SelfRenderingFunctionSqlAstExpression selfRenderingFunction + && "concat".equals( selfRenderingFunction.getFunctionName() ) ) { + final List list = (List) selfRenderingFunction.getArguments(); + final SqlAstNode lastOperand = list.get( list.size() - 1 ); + if ( expression2 instanceof QueryLiteral literal2 + && lastOperand instanceof QueryLiteral literalOperand ) { + list.set( + list.size() - 1, + new QueryLiteral<>( + literalOperand.getLiteralValue().toString() + + literal2.getLiteralValue().toString(), + stringType + ) + ); + } + else { + list.add( expression2 ); + } + return expression; + } + else if ( expression2 instanceof SelfRenderingFunctionSqlAstExpression selfRenderingFunction + && "concat".equals( selfRenderingFunction.getFunctionName() ) ) { + final List list = (List) selfRenderingFunction.getArguments(); + final SqlAstNode firstOperand = list.get( 0 ); + if ( expression instanceof QueryLiteral literal + && firstOperand instanceof QueryLiteral literalOperand ) { + list.set( + list.size() - 1, + new QueryLiteral<>( + literal.getLiteralValue().toString() + + literalOperand.getLiteralValue().toString(), + stringType + ) + ); + } + else { + list.add( 0, expression ); + } + return expression2; + } + else if ( expression instanceof QueryLiteral literal + && expression2 instanceof QueryLiteral literal2 ) { + return new QueryLiteral<>( + literal.getLiteralValue().toString() + + literal2.getLiteralValue().toString(), + stringType + ); + } + else { + final List list = new ArrayList<>( 2 ); + list.add( expression ); + list.add( expression2 ); + return new SelfRenderingFunctionSqlAstExpression( + "concat", + concatFunction, + list, + stringType, + stringType + ); + } + } + + private Expression getHours( + BasicType integerType, + Expression offsetExpression) { + BinaryArithmeticExpression divisionExpr = new BinaryArithmeticExpression( + offsetExpression, + DIVIDE_PORTABLE, + new QueryLiteral<>(3600, integerType), + integerType + ); + return floor(divisionExpr); + } + + private Expression getMinutes( + BasicType integerType, + Expression offsetExpression){ + return new BinaryArithmeticExpression( + abs(new BinaryArithmeticExpression( + offsetExpression, + MODULO, + new QueryLiteral<>( 3600, integerType ), + integerType + )), + DIVIDE_PORTABLE, + new QueryLiteral<>( 60, integerType ), + integerType + ); + } + + private Expression abs(Expression expression) { + return new SelfRenderingFunctionSqlAstExpression( + "abs", + findSelfRenderingFunction( "abs", 2 ), + List.of( expression ), + (ReturnableType) expression.getExpressionType(), + expression.getExpressionType() + ); + } + + private Expression floor(Expression expression) { + return new SelfRenderingFunctionSqlAstExpression( + "floor", + findSelfRenderingFunction( "floor", 2 ), + List.of( expression ), + (ReturnableType) expression.getExpressionType(), + expression.getExpressionType() + ); + } + + private FunctionRenderer findSelfRenderingFunction(String functionName, int argumentCount) { + final SqmFunctionDescriptor functionDescriptor = + queryEngine.getSqmFunctionRegistry() + .findFunctionDescriptor( functionName ); + if ( functionDescriptor instanceof MultipatternSqmFunctionDescriptor multiPatternFunction ) { + return (FunctionRenderer) multiPatternFunction.getFunction( argumentCount ); + } + return (FunctionRenderer) functionDescriptor; + } + } + + private static CaseSearchedExpression zoneOffsetSeconds(BasicType stringType, BasicType integerType, Expression offsetExpression) { + final CaseSearchedExpression caseSearchedExpression = new CaseSearchedExpression(stringType); + caseSearchedExpression.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new ComparisonPredicate( + offsetExpression, + LESS_THAN_OR_EQUAL, + new QueryLiteral<>( -36000, integerType) + ), + new QueryLiteral<>( "-", stringType) + ) + ); + caseSearchedExpression.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new ComparisonPredicate( + offsetExpression, + LESS_THAN, + new QueryLiteral<>( 0, integerType) + ), + new QueryLiteral<>( "-0", stringType) + ) + ); + caseSearchedExpression.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new ComparisonPredicate( + offsetExpression, + GREATER_THAN_OR_EQUAL, + new QueryLiteral<>( 36000, integerType) + ), + new QueryLiteral<>( "+", stringType) + ) + ); + caseSearchedExpression.otherwise( new QueryLiteral<>( "+0", stringType) ); + return caseSearchedExpression; + } + + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBMinMaxFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBMinMaxFunction.java new file mode 100644 index 000000000000..6603e4d230c7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBMinMaxFunction.java @@ -0,0 +1,116 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import java.util.List; + +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; +import org.hibernate.query.sqm.function.FunctionKind; +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.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.Expression; +import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.type.SqlTypes; + +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.COMPARABLE; + +/** + * GaussDB doesn't support min/max for uuid yet, + * but since that type is comparable we want to support this operation. + * The workaround is to cast uuid to text and aggregate that, which preserves the ordering, + * and finally cast the result back to uuid. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLDialect. + */ +public class GaussDBMinMaxFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public GaussDBMinMaxFunction(String name) { + super( + name, + FunctionKind.AGGREGATE, + new ArgumentTypesValidator( StandardArgumentsValidators.exactly( 1 ), COMPARABLE ), + StandardFunctionReturnTypeResolvers.useFirstNonNull(), + StandardFunctionArgumentTypeResolvers.IMPLIED_RESULT_TYPE + ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + render( sqlAppender, sqlAstArguments, null, returnType, walker ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + Predicate filter, + ReturnableType returnType, + SqlAstTranslator translator) { + final boolean caseWrapper = filter != null && !translator.getSessionFactory().getJdbcServices().getDialect().supportsFilterClause(); + sqlAppender.appendSql( getName() ); + sqlAppender.appendSql( '(' ); + final Expression arg = (Expression) sqlAstArguments.get( 0 ); + final String castTarget; + if ( caseWrapper ) { + translator.getCurrentClauseStack().push( Clause.WHERE ); + sqlAppender.appendSql( "case when " ); + filter.accept( translator ); + translator.getCurrentClauseStack().pop(); + sqlAppender.appendSql( " then " ); + castTarget = renderArgument( sqlAppender, translator, arg ); + sqlAppender.appendSql( " else null end)" ); + } + else { + castTarget = renderArgument( sqlAppender, translator, arg ); + sqlAppender.appendSql( ')' ); + if ( filter != null ) { + translator.getCurrentClauseStack().push( Clause.WHERE ); + sqlAppender.appendSql( " filter (where " ); + filter.accept( translator ); + sqlAppender.appendSql( ')' ); + translator.getCurrentClauseStack().pop(); + } + } + if ( castTarget != null ) { + sqlAppender.appendSql( "::" ); + sqlAppender.appendSql( castTarget ); + } + } + + private String renderArgument(SqlAppender sqlAppender, SqlAstTranslator translator, Expression arg) { + final JdbcMapping sourceMapping = arg.getExpressionType().getSingleJdbcMapping(); + // Cast uuid expressions to "text" first, aggregate that, and finally cast to uuid again + if ( sourceMapping.getJdbcType().getDefaultSqlTypeCode() == SqlTypes.UUID ) { + sqlAppender.appendSql( "cast(" ); + arg.accept( translator ); + sqlAppender.appendSql( " as text)" ); + return "uuid"; + } + else { + arg.accept( translator ); + return null; + } + } + + @Override + public String getArgumentListSignature() { + return "(COMPARABLE arg)"; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncFunction.java new file mode 100644 index 000000000000..9c0a7735200e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncFunction.java @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.sqm.function.SelfRenderingSqmFunction; +import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.query.sqm.tree.expression.SqmExtractUnit; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * Custom {@link TruncFunction} for GaussDB which uses the dialect-specific function for numeric truncation + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLTruncFunction. + */ +public class GaussDBTruncFunction extends TruncFunction { + private final GaussDBTruncRoundFunction gaussDBTruncRoundFunction; + + public GaussDBTruncFunction(boolean supportsTwoArguments, TypeConfiguration typeConfiguration) { + super( + "trunc(?1)", + null, + DatetimeTrunc.DATE_TRUNC, + null, + typeConfiguration + ); + this.gaussDBTruncRoundFunction = new GaussDBTruncRoundFunction( "trunc", supportsTwoArguments ); + } + + @Override + protected SelfRenderingSqmFunction generateSqmFunctionExpression( + List> arguments, + ReturnableType impliedResultType, + QueryEngine queryEngine) { + final List> args = new ArrayList<>( arguments ); + if ( arguments.size() != 2 || !( arguments.get( 1 ) instanceof SqmExtractUnit ) ) { + // numeric truncation + return gaussDBTruncRoundFunction.generateSqmFunctionExpression( + arguments, + impliedResultType, + queryEngine + ); + } + // datetime truncation + return new SelfRenderingSqmFunction<>( + this, + datetimeRenderingSupport, + args, + impliedResultType, + TruncArgumentsValidator.DATETIME_VALIDATOR, + getReturnTypeResolver(), + queryEngine.getCriteriaBuilder(), + getName() + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncRoundFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncRoundFunction.java new file mode 100644 index 000000000000..62e61765e6b4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBTruncRoundFunction.java @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor; +import org.hibernate.query.sqm.function.FunctionRenderer; +import org.hibernate.query.sqm.function.SelfRenderingSqmFunction; +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.query.sqm.tree.SqmTypedNode; +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.Expression; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC; + +/** + * GaussDB only supports the two-argument {@code trunc} and {@code round} functions + * with the following signatures: + *
    + *
  • {@code trunc(numeric, integer)}
  • + *
  • {@code round(numeric, integer)}
  • + *
+ *

+ * This custom function falls back to using {@code floor} as a workaround only when necessary, + * e.g. when there are 2 arguments to the function and either: + *

    + *
  • The first argument is not of type {@code numeric}
  • + * or + *
  • The dialect doesn't support the two-argument {@code trunc} function
  • + *
+ * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLTruncRoundFunction. + */ +public class GaussDBTruncRoundFunction extends AbstractSqmFunctionDescriptor implements FunctionRenderer { + private final boolean supportsTwoArguments; + + public GaussDBTruncRoundFunction(String name, boolean supportsTwoArguments) { + super( + name, + new ArgumentTypesValidator( StandardArgumentsValidators.between( 1, 2 ), NUMERIC, INTEGER ), + StandardFunctionReturnTypeResolvers.useArgType( 1 ), + StandardFunctionArgumentTypeResolvers.invariant( NUMERIC, INTEGER ) + ); + this.supportsTwoArguments = supportsTwoArguments; + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final int numberOfArguments = arguments.size(); + final Expression firstArg = (Expression) arguments.get( 0 ); + final JdbcType jdbcType = firstArg.getExpressionType().getSingleJdbcMapping().getJdbcType(); + if ( numberOfArguments == 1 || supportsTwoArguments && jdbcType.isDecimal() ) { + // use native two-argument function + sqlAppender.appendSql( getName() ); + sqlAppender.appendSql( "(" ); + firstArg.accept( walker ); + if ( numberOfArguments > 1 ) { + sqlAppender.appendSql( ", " ); + arguments.get( 1 ).accept( walker ); + } + sqlAppender.appendSql( ")" ); + } + else { + // workaround using floor + if ( getName().equals( "trunc" ) ) { + sqlAppender.appendSql( "sign(" ); + firstArg.accept( walker ); + sqlAppender.appendSql( ")*floor(abs(" ); + firstArg.accept( walker ); + sqlAppender.appendSql( ")*1e" ); + arguments.get( 1 ).accept( walker ); + } + else { + sqlAppender.appendSql( "floor(" ); + firstArg.accept( walker ); + sqlAppender.appendSql( "*1e" ); + arguments.get( 1 ).accept( walker ); + sqlAppender.appendSql( "+0.5" ); + } + sqlAppender.appendSql( ")/1e" ); + arguments.get( 1 ).accept( walker ); + } + } + + @Override + public String getArgumentListSignature() { + return "(NUMERIC number[, INTEGER places])"; + } + + @Override + protected SelfRenderingSqmFunction generateSqmFunctionExpression( + List> arguments, + ReturnableType impliedResultType, + QueryEngine queryEngine) { + return new SelfRenderingSqmFunction<>( + this, + this, + arguments, + impliedResultType, + getArgumentsValidator(), + getReturnTypeResolver(), + queryEngine.getCriteriaBuilder(), + getName() + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayRemoveIndexUnnestFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayRemoveIndexUnnestFunction.java index 74d88eab2f87..65ab05672969 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayRemoveIndexUnnestFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayRemoveIndexUnnestFunction.java @@ -24,7 +24,7 @@ */ public class ArrayRemoveIndexUnnestFunction extends AbstractSqmSelfRenderingFunctionDescriptor { - private final boolean castEmptyArrayLiteral; + protected final boolean castEmptyArrayLiteral; public ArrayRemoveIndexUnnestFunction(boolean castEmptyArrayLiteral) { super( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatElementFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatElementFunction.java new file mode 100644 index 000000000000..c25eb4b12dc6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatElementFunction.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.engine.jdbc.Size; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.metamodel.mapping.SqlTypedMapping; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstNodeRenderingMode; +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.Expression; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.type.BasicPluralType; + +/** + * GaussDB variant of the function to properly return {@code null} when the array argument is null. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayConcatElementFunction. + */ +public class GaussDBArrayConcatElementFunction extends ArrayConcatElementFunction { + + public GaussDBArrayConcatElementFunction(boolean prepend) { + super( "", "||", "", prepend ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression firstArgument = (Expression) sqlAstArguments.get( 0 ); + final Expression secondArgument = (Expression) sqlAstArguments.get( 1 ); + final Expression arrayArgument; + final Expression elementArgument; + if ( prepend ) { + elementArgument = firstArgument; + arrayArgument = secondArgument; + } + else { + arrayArgument = firstArgument; + elementArgument = secondArgument; + } + final String elementCastType; + if ( needsElementCasting( elementArgument ) ) { + final JdbcMappingContainer arrayType = arrayArgument.getExpressionType(); + final Size size = arrayType instanceof SqlTypedMapping ? ( (SqlTypedMapping) arrayType ).toSize() : null; + elementCastType = DdlTypeHelper.getCastTypeName( + ( (BasicPluralType) returnType ).getElementType(), + size, + walker.getSessionFactory().getTypeConfiguration() + ); + } + else { + elementCastType = null; + } + sqlAppender.append( "case when " ); + walker.render( arrayArgument, SqlAstNodeRenderingMode.DEFAULT ); + sqlAppender.append( " is not null then " ); + if ( prepend && elementCastType != null) { + sqlAppender.append( "cast(" ); + walker.render( firstArgument, SqlAstNodeRenderingMode.DEFAULT ); + sqlAppender.append( " as " ); + sqlAppender.append( elementCastType ); + sqlAppender.append( ')' ); + } + else { + walker.render( firstArgument, SqlAstNodeRenderingMode.DEFAULT ); + } + sqlAppender.append( "||" ); + if ( !prepend && elementCastType != null) { + sqlAppender.append( "cast(" ); + walker.render( secondArgument, SqlAstNodeRenderingMode.DEFAULT ); + sqlAppender.append( " as " ); + sqlAppender.append( elementCastType ); + sqlAppender.append( ')' ); + } + else { + walker.render( secondArgument, SqlAstNodeRenderingMode.DEFAULT ); + } + sqlAppender.append( " end" ); + } + + private static boolean needsElementCasting(Expression elementExpression) { + // GaussDB needs casting of null and string literal expressions + return elementExpression instanceof Literal && ( + elementExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString() + || ( (Literal) elementExpression ).getLiteralValue() == null + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatFunction.java new file mode 100644 index 000000000000..b09fea3db34b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConcatFunction.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; + +/** + * GaussDB variant of the function to properly return {@code null} when one of the arguments is null. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayConcatFunction. + */ +public class GaussDBArrayConcatFunction extends ArrayConcatFunction { + + public GaussDBArrayConcatFunction() { + super( "", "||", "" ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + sqlAppender.append( "case when " ); + String separator = ""; + for ( SqlAstNode node : sqlAstArguments ) { + sqlAppender.append( separator ); + node.accept( walker ); + sqlAppender.append( " is not null" ); + separator = " and "; + } + + sqlAppender.append( " then " ); + super.render( sqlAppender, sqlAstArguments, returnType, walker ); + sqlAppender.append( " end" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConstructorFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConstructorFunction.java new file mode 100644 index 000000000000..2712f2ec4b93 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayConstructorFunction.java @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.BasicPluralType; +import org.hibernate.type.BasicType; + +/** + * Special array constructor function that also applies a cast to the array literal, + * based on the inferred result type. GaussDB needs this, + * because by default it assumes a {@code text[]}, which is not compatible with {@code varchar[]}. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayConstructorFunction. + */ +public class GaussDBArrayConstructorFunction extends ArrayConstructorFunction { + + public GaussDBArrayConstructorFunction(boolean list) { + super( list, true ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + String arrayTypeName = null; + if ( returnType instanceof BasicPluralType pluralType ) { + if ( needsArrayCasting( pluralType.getElementType() ) ) { + arrayTypeName = DdlTypeHelper.getCastTypeName( + returnType, + walker.getSessionFactory().getTypeConfiguration() + ); + sqlAppender.append( "cast(" ); + } + } + super.render( sqlAppender, sqlAstArguments, returnType, walker ); + if ( arrayTypeName != null ) { + sqlAppender.appendSql( " as " ); + sqlAppender.appendSql( arrayTypeName ); + sqlAppender.appendSql( ')' ); + } + } + + private static boolean needsArrayCasting(BasicType elementType) { + // GaussDB doesn't do implicit conversion between text[] and varchar[], so we need casting + return elementType.getJdbcType().isString(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java new file mode 100644 index 000000000000..f93e94efce5c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.BasicPluralType; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + +/** + * Special array contains function that also applies a cast to the element argument. PostgreSQL needs this, + * because by default it assumes a {@code text[]}, which is not compatible with {@code varchar[]}. + * + * Notes: Original code of this class is based on ArrayContainsOperatorFunction. + */ +public class GaussDBArrayContainsOperatorFunction extends ArrayContainsUnnestFunction { + + public GaussDBArrayContainsOperatorFunction(boolean nullable, TypeConfiguration typeConfiguration) { + super( nullable, typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression haystackExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression needleExpression = (Expression) sqlAstArguments.get( 1 ); + final JdbcMappingContainer needleTypeContainer = needleExpression.getExpressionType(); + final JdbcMapping needleType = needleTypeContainer == null ? null : needleTypeContainer.getSingleJdbcMapping(); + if ( needleType == null || needleType instanceof BasicPluralType ) { + LOG.deprecatedArrayContainsWithArray(); + if ( nullable ) { + super.render( sqlAppender, sqlAstArguments, returnType, walker ); + } + else { + haystackExpression.accept( walker ); + sqlAppender.append( "@>" ); + needleExpression.accept( walker ); + } + } + else { + if ( nullable ) { + sqlAppender.append( "(array_positions(" ); + haystackExpression.accept( walker ); + sqlAppender.append( ',' ); + needleExpression.accept( walker ); + sqlAppender.append( "))[1] is not null" ); + } + else { + haystackExpression.accept( walker ); + sqlAppender.append( "@>" ); + if ( needsArrayCasting( needleExpression ) ) { + sqlAppender.append( "cast(array[" ); + needleExpression.accept( walker ); + sqlAppender.append( "] as " ); + sqlAppender.append( DdlTypeHelper.getCastTypeName( + haystackExpression.getExpressionType(), + walker.getSessionFactory().getTypeConfiguration() + ) ); + sqlAppender.append( ')' ); + } + else { + sqlAppender.append( "array[" ); + needleExpression.accept( walker ); + sqlAppender.append( ']' ); + } + } + } + } + + private static boolean needsArrayCasting(Expression elementExpression) { + // Gauss doesn't do implicit conversion between text[] and varchar[], so we need casting + return elementExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayFillFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayFillFunction.java new file mode 100644 index 000000000000..f3243bd0eb58 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayFillFunction.java @@ -0,0 +1,66 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.Literal; + +/** + * Custom casting for the array fill function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayFillFunction. + */ +public class GaussDBArrayFillFunction extends AbstractArrayFillFunction { + + public GaussDBArrayFillFunction(boolean list) { + super( list ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + sqlAppender.append( "array_fill(" ); + final String elementCastType; + final Expression elementExpression = (Expression) sqlAstArguments.get( 0 ); + if ( needsElementCasting( elementExpression ) ) { + elementCastType = DdlTypeHelper.getCastTypeName( + elementExpression.getExpressionType(), + walker.getSessionFactory().getTypeConfiguration() + ); + sqlAppender.append( "cast(" ); + } + else { + elementCastType = null; + } + sqlAstArguments.get( 0 ).accept( walker ); + if ( elementCastType != null ) { + sqlAppender.append( " as " ); + sqlAppender.append( elementCastType ); + sqlAppender.append( ')' ); + } + sqlAppender.append( ",array[" ); + sqlAstArguments.get( 1 ).accept( walker ); + sqlAppender.append( "])" ); + } + + private static boolean needsElementCasting(Expression elementExpression) { + // GaussDB needs casting of null and string literal expressions + return elementExpression instanceof Literal && ( + elementExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString() + || ( (Literal) elementExpression ).getLiteralValue() == null + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionFunction.java new file mode 100644 index 000000000000..c1db4df41aa0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionFunction.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstNodeRenderingMode; +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.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB variant of the function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayPositionFunction. + */ +public class GaussDBArrayPositionFunction extends AbstractArrayPositionFunction { + + public GaussDBArrayPositionFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression elementExpression = (Expression) sqlAstArguments.get( 1 ); + + sqlAppender.append( "(array_positions(" ); + arrayExpression.accept( walker ); + sqlAppender.append( ", " ); + walker.render( elementExpression, SqlAstNodeRenderingMode.DEFAULT ); + sqlAppender.append( "))[1]" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionsFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionsFunction.java new file mode 100644 index 000000000000..8b3b8b2643cc --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayPositionsFunction.java @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstNodeRenderingMode; +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.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB variant of the function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLArrayPositionsFunction. + */ +public class GaussDBArrayPositionsFunction extends AbstractArrayPositionsFunction { + + public GaussDBArrayPositionsFunction(boolean list, TypeConfiguration typeConfiguration) { + super( list, typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression elementExpression = (Expression) sqlAstArguments.get( 1 ); + sqlAppender.append( "array_positions(" ); + walker.render( arrayExpression, SqlAstNodeRenderingMode.DEFAULT ); + sqlAppender.append( ',' ); + walker.render( elementExpression, SqlAstNodeRenderingMode.DEFAULT ); + sqlAppender.append( ')' ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java new file mode 100644 index 000000000000..4882f57dd959 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.Literal; + +import java.util.List; + +/** + * Gaussdb array_remove function. + */ +public class GaussDBArrayRemoveFunction extends AbstractArrayRemoveFunction { + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression indexExpression = (Expression) sqlAstArguments.get( 1 ); + + sqlAppender.append( "CASE WHEN "); + arrayExpression.accept( walker ); + sqlAppender.append( " IS NULL THEN NULL ELSE COALESCE(( SELECT array_agg(val) FROM unnest("); + arrayExpression.accept( walker ); + sqlAppender.append( ") AS val" ); + + if ( indexExpression instanceof Literal ) { + Literal literal = (Literal) indexExpression; + Object literalValue = literal.getLiteralValue(); + if ( literalValue != null ) { + appendWhere( sqlAppender, walker, indexExpression ); + } + else { + sqlAppender.append( " where val IS NOT NULL" ); + } + } + else { + appendWhere( sqlAppender, walker, indexExpression ); + } + sqlAppender.append( "), CAST(ARRAY[] AS VARCHAR[]) ) END AS result_array" ); + } + + /** + * can not get value if type like string + * @param sqlAppender + * @param walker + * @param indexExpression + */ + private static void appendWhere(SqlAppender sqlAppender, SqlAstTranslator walker, Expression indexExpression) { + sqlAppender.append( " where val IS NULL OR val not in (" ); + indexExpression.accept( walker ); + sqlAppender.append( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java new file mode 100644 index 000000000000..60c081d047d3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.Literal; + +import java.util.List; + +/** + * Gaussdb array_remove function. + */ +public class GaussDBArrayRemoveIndexFunction extends ArrayRemoveIndexUnnestFunction { + + + + public GaussDBArrayRemoveIndexFunction(boolean castEmptyArrayLiteral) { + super( castEmptyArrayLiteral ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression indexExpression = (Expression) sqlAstArguments.get( 1 ); + + sqlAppender.append( "case when "); + arrayExpression.accept( walker ); + sqlAppender.append( " IS NOT NULL THEN COALESCE((SELECT array_agg(" ); + arrayExpression.accept( walker ); + sqlAppender.append( "[idx]) FROM generate_subscripts(" ); + arrayExpression.accept( walker ); + sqlAppender.append( ", 1) AS idx " ); + + if ( indexExpression instanceof Literal ) { + Literal literal = (Literal) indexExpression; + Object literalValue = literal.getLiteralValue(); + if ( literalValue != null ) { + appendWhere( sqlAppender, walker, indexExpression ); + } + } + else { + appendWhere( sqlAppender, walker, indexExpression ); + } + + sqlAppender.append( "), CAST(ARRAY[] AS VARCHAR ARRAY)) " ); + if ( castEmptyArrayLiteral ) { + sqlAppender.append( "ELSE CAST(ARRAY[] AS VARCHAR ARRAY) " ); + } + sqlAppender.append( "END AS result_array" ); + } + + private static void appendWhere(SqlAppender sqlAppender, SqlAstTranslator walker, Expression indexExpression) { + sqlAppender.append( "where idx not in (" ); + indexExpression.accept( walker ); + sqlAppender.append( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java new file mode 100644 index 000000000000..e153e72b57b6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.Literal; + +import java.util.List; + +/** + * Gaussdb array_replace function. + */ +public class GaussDBArrayReplaceFunction extends ArrayReplaceUnnestFunction { + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + + sqlAppender.append( "CASE WHEN "); + sqlAstArguments.get( 0 ).accept( walker ); + sqlAppender.append( " IS NULL THEN NULL ELSE COALESCE((SELECT array_agg(CASE "); + final Expression originValueExpression = (Expression) sqlAstArguments.get( 1 ); + if ( originValueExpression instanceof Literal ) { + Literal literal = (Literal) originValueExpression; + Object literalValue = literal.getLiteralValue(); + if ( literalValue != null ) { + sqlAppender.append( "WHEN val = "); + sqlAstArguments.get( 1 ).accept( walker ); + } + else { + sqlAppender.append( "WHEN val is null "); + } + } + else { + sqlAppender.append( "WHEN val = "); + sqlAstArguments.get( 1 ).accept( walker ); + } + sqlAppender.append( " THEN "); + sqlAstArguments.get( 2 ).accept( walker ); + sqlAppender.append( " ELSE val END) FROM unnest( "); + sqlAstArguments.get( 0 ).accept( walker ); + sqlAppender.append( ") AS val ), CAST(ARRAY[] AS VARCHAR[]) ) END AS result_array"); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java new file mode 100644 index 000000000000..3daa6399af49 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import org.hibernate.metamodel.model.domain.ReturnableType; +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.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; + +import java.util.List; + +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; + +/** + * Gaussdb array_set function. + */ +public class GaussDBArraySetFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public GaussDBArraySetFunction() { + super( + "array_set", + StandardArgumentsValidators.composite( + new ArrayAndElementArgumentValidator( 0, 2 ), + new ArgumentTypesValidator( null, ANY, INTEGER, ANY ) + ), + ArrayViaArgumentReturnTypeResolver.DEFAULT_INSTANCE, + StandardFunctionArgumentTypeResolvers.composite( + StandardFunctionArgumentTypeResolvers.IMPLIED_RESULT_TYPE, + StandardFunctionArgumentTypeResolvers.invariant( ANY, INTEGER, ANY ), + new ArrayAndElementArgumentTypeResolver( 0, 2 ) + ) + ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression indexExpression = (Expression) sqlAstArguments.get( 1 ); + final Expression elementExpression = (Expression) sqlAstArguments.get( 2 ); + + sqlAppender.append( "( SELECT array_agg( CASE WHEN idx_gen = "); + indexExpression.accept( walker ); + sqlAppender.append( " THEN "); + elementExpression.accept( walker ); + sqlAppender.append( " ELSE CASE WHEN idx_gen <= array_length(ewa1_0.the_array, 1) "); + sqlAppender.append( " THEN ewa1_0.the_array[idx_gen] ELSE NULL END END ORDER BY idx_gen ) "); + sqlAppender.append( " FROM generate_series(1, GREATEST(COALESCE(array_length( "); + arrayExpression.accept( walker ); + sqlAppender.append( " , 1), 0), "); + indexExpression.accept( walker ); + sqlAppender.append( " )) AS idx_gen ) AS result_array "); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java new file mode 100644 index 000000000000..00b88437c64c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.array; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; + +import java.util.List; + +/** + * Gaussdb array_trim function. + * + * Notes: Original code of this class is based on PostgreSQLArrayTrimEmulation. + */ +public class GaussDBArrayTrimFunction extends AbstractArrayTrimFunction { + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression lengthExpression = (Expression) sqlAstArguments.get( 1 ); + + sqlAppender.append( "array_trim("); + arrayExpression.accept( walker ); + sqlAppender.append( ","); + lengthExpression.accept( walker ); + sqlAppender.append( ")"); + + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java new file mode 100644 index 000000000000..75bf0e8c893c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +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.Expression; +import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.ast.tree.select.SortSpecification; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_arrayagg function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonArrayAggFunction. + */ +public class GaussDBJsonArrayAggFunction extends JsonArrayAggFunction { + + private final boolean supportsStandard; + + public GaussDBJsonArrayAggFunction(boolean supportsStandard, TypeConfiguration typeConfiguration) { + super( true, typeConfiguration ); + this.supportsStandard = supportsStandard; + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + Predicate filter, + List withinGroup, + ReturnableType returnType, + SqlAstTranslator translator) { + + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + + sqlAppender.appendSql( "to_jsonb( array_agg( CASE WHEN " ); + arrayExpression.accept( translator ); + sqlAppender.appendSql( " IS NOT NULL THEN " ); + arrayExpression.accept( translator ); + sqlAppender.appendSql( "::text ELSE NULL END" ); + if ( withinGroup != null && !withinGroup.isEmpty() ) { + translator.getCurrentClauseStack().push( Clause.WITHIN_GROUP ); + sqlAppender.appendSql( " order by " ); + withinGroup.get( 0 ).accept( translator ); + for ( int i = 1; i < withinGroup.size(); i++ ) { + sqlAppender.appendSql( ',' ); + withinGroup.get( i ).accept( translator ); + } + translator.getCurrentClauseStack().pop(); + } + sqlAppender.appendSql( ") ) AS result" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java new file mode 100644 index 000000000000..4eb0991551a2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_array_append function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonArrayAppendFunction. + */ +public class GaussDBJsonArrayAppendFunction extends AbstractJsonArrayAppendFunction { + + private final boolean supportsLax; + + public GaussDBJsonArrayAppendFunction(boolean supportsLax, TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + this.supportsLax = supportsLax; + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator translator) { + + + } + + private static boolean isJsonType(Expression expression) { + final JdbcMappingContainer expressionType = expression.getExpressionType(); + return expressionType != null && expressionType.getSingleJdbcMapping().getJdbcType().isJson(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java new file mode 100644 index 000000000000..9a918b1946a6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; +import org.hibernate.query.sqm.function.FunctionKind; +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.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.JsonNullBehavior; +import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + + +/** + * Notes: Original code of this class is based on JsonArrayFunction. + */ +public class GaussDBJsonArrayFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public GaussDBJsonArrayFunction(TypeConfiguration typeConfiguration) { + super( + "json_array", + FunctionKind.NORMAL, + null, + StandardFunctionReturnTypeResolvers.invariant( + typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON ) + ), + null + ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + sqlAppender.appendSql( "json_array" ); + char separator = '('; + if ( sqlAstArguments.isEmpty() ) { + sqlAppender.appendSql( separator ); + renderReturningClause( sqlAppender, walker ); + } + else { + final SqlAstNode lastArgument = sqlAstArguments.get( sqlAstArguments.size() - 1 ); + final JsonNullBehavior nullBehavior; + final int argumentsCount; + if ( lastArgument instanceof JsonNullBehavior ) { + nullBehavior = (JsonNullBehavior) lastArgument; + argumentsCount = sqlAstArguments.size() - 1; + } + else { + nullBehavior = JsonNullBehavior.ABSENT; + argumentsCount = sqlAstArguments.size(); + } + for ( int i = 0; i < argumentsCount; i++ ) { + Expression valueNode = (Expression) sqlAstArguments.get( i ); + if ( nullBehavior == JsonNullBehavior.ABSENT && valueNode instanceof Literal ) { + Object literalValue = ((Literal) valueNode).getLiteralValue(); + if ( literalValue == null ) { + continue; + } + } + sqlAppender.appendSql( separator ); + valueNode.accept( walker ); + separator = ','; + } + renderReturningClause( sqlAppender, walker ); + } + sqlAppender.appendSql( ')' ); + } + + protected void renderReturningClause(SqlAppender sqlAppender, SqlAstTranslator walker) { + // No-op + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java new file mode 100644 index 000000000000..dd9399e00750 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_array_insert function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonArrayInsertFunction. + */ +public class GaussDBJsonArrayInsertFunction extends AbstractJsonArrayInsertFunction { + + public GaussDBJsonArrayInsertFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator translator) { + + final Expression json = (Expression) arguments.get( 0 ); + final Expression jsonPath = (Expression) arguments.get( 1 ); + final SqlAstNode value = arguments.get( 2 ); + + sqlAppender.append( "json_array_insert(" ); + json.accept( translator ); + sqlAppender.append( "," ); + jsonPath.accept( translator ); + sqlAppender.append( "," ); + value.accept( translator ); + sqlAppender.append( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java new file mode 100644 index 000000000000..bd2267079556 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.Iterator; +import java.util.Map; + + +/** + * PostgreSQL json_query function. + */ +public class GaussDBJsonExistsFunction extends JsonExistsFunction { + + public GaussDBJsonExistsFunction(TypeConfiguration typeConfiguration, + boolean supportsJsonPathExpression, + boolean supportsJsonPathPassingClause) { + super(typeConfiguration, supportsJsonPathExpression, supportsJsonPathPassingClause); + } + + @Override + protected void render( + SqlAppender sqlAppender, + JsonExistsArguments arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + + sqlAppender.appendSql( "json_contains_path(" ); + arguments.jsonDocument().accept( walker ); + sqlAppender.appendSql( ",'one', '" ); + + String literalValue = walker.getLiteralValue( arguments.jsonPath() ); + final JsonPathPassingClause passingClause = arguments.passingClause(); + if ( passingClause != null ) { + final Map passingExpressions = passingClause.getPassingExpressions(); + final Iterator> iterator = passingExpressions.entrySet().iterator(); + Map.Entry entry = iterator.next(); + literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + while ( iterator.hasNext() ) { + entry = iterator.next(); + sqlAppender.appendSql( ',' ); + literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + } + } + + sqlAppender.appendSql( literalValue ); + sqlAppender.appendSql( "') = 1" ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java new file mode 100644 index 000000000000..c15d4c1be072 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_insert function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonInsertFunction. + */ +public class GaussDBJsonInsertFunction extends AbstractJsonInsertFunction { + + public GaussDBJsonInsertFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator translator) { + + final Expression json = (Expression) arguments.get( 0 ); + final Expression jsonPath = (Expression) arguments.get( 1 ); + final SqlAstNode value = arguments.get( 2 ); + sqlAppender.appendSql( "json_insert(" ); + json.accept( translator ); + sqlAppender.appendSql( "," ); + jsonPath.accept( translator ); + sqlAppender.appendSql( "," ); + value.accept( translator ); + sqlAppender.appendSql( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java new file mode 100644 index 000000000000..5e6db40072dd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_mergepatch function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonMergepatchFunction. + */ +public class GaussDBJsonMergepatchFunction extends AbstractJsonMergepatchFunction { + + public GaussDBJsonMergepatchFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator translator) { + + sqlAppender.appendSql( "json_merge" ); + char separator = '('; + for ( int i = 0; i < arguments.size(); i++ ) { + sqlAppender.appendSql( separator ); + arguments.get( i ).accept( translator ); + separator = ','; + } + sqlAppender.appendSql( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java new file mode 100644 index 000000000000..2ad4ca528118 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_objectagg function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonObjectAggFunction. + */ +public class GaussDBJsonObjectAggFunction extends JsonObjectAggFunction { + + + public GaussDBJsonObjectAggFunction(boolean supportsStandard, TypeConfiguration typeConfiguration) { + super( ":", true, typeConfiguration ); + } + + @Override + protected void render( + SqlAppender sqlAppender, + JsonObjectAggArguments arguments, + Predicate filter, + ReturnableType returnType, + SqlAstTranslator translator) { + + sqlAppender.appendSql( "json_object_agg(" ); + sqlAppender.appendSql( "CASE WHEN " ); + arguments.key().accept( translator ); + sqlAppender.appendSql( " IS NOT NULL " ); + sqlAppender.appendSql( " THEN " ); + arguments.key().accept( translator ); + sqlAppender.appendSql( " END," ); + arguments.value().accept( translator ); + sqlAppender.appendSql( ")" ); + } + + @Override + protected void renderUniqueAndReturningClause(SqlAppender sqlAppender, JsonObjectAggArguments arguments, SqlAstTranslator translator) { + renderUniqueClause( sqlAppender, arguments, translator ); + renderReturningClause( sqlAppender, arguments, translator ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java new file mode 100644 index 000000000000..cc5a75d2aa12 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java @@ -0,0 +1,73 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.JsonNullBehavior; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.List; + +/** + * Gaussdb json_object function. + * + * Notes: Original code of this class is based on PostgreSQLJsonObjectFunction. + */ +public class GaussDBJsonObjectFunction extends JsonObjectFunction { + + public GaussDBJsonObjectFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration, false ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + + sqlAppender.appendSql( "json_build_object" ); + char separator = '('; + if ( sqlAstArguments.isEmpty() ) { + sqlAppender.appendSql( separator ); + } + else { + final JsonNullBehavior nullBehavior; + final int argumentsCount; + if ( ( sqlAstArguments.size() & 1 ) == 1 ) { + nullBehavior = (JsonNullBehavior) sqlAstArguments.get( sqlAstArguments.size() - 1 ); + argumentsCount = sqlAstArguments.size() - 1; + } + else { + nullBehavior = JsonNullBehavior.NULL; + argumentsCount = sqlAstArguments.size(); + } + sqlAppender.appendSql('('); + separator = ' '; + for ( int i = 0; i < argumentsCount; i += 2 ) { + final SqlAstNode key = sqlAstArguments.get( i ); + Expression valueNode = (Expression) sqlAstArguments.get( i+1 ); + if ( nullBehavior == JsonNullBehavior.ABSENT && walker.getLiteralValue( valueNode ) == null) { + continue; + } + if (separator != ' ') { + sqlAppender.appendSql(separator); + } + else { + separator = ','; + } + key.accept( walker ); + sqlAppender.appendSql( ',' ); + valueNode.accept( walker ); + } + } + sqlAppender.appendSql( ')' ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java new file mode 100644 index 000000000000..4f5f7604c137 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; +import org.hibernate.sql.ast.tree.expression.JsonQueryWrapMode; +import org.hibernate.type.descriptor.jdbc.JsonHelper; +import org.hibernate.type.spi.TypeConfiguration; + +import java.util.Iterator; +import java.util.Map; + +/** + * PostgreSQL json_query function. + */ +public class GaussDBJsonQueryFunction extends JsonQueryFunction { + + public GaussDBJsonQueryFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration, true, true ); + } + + @Override + protected void render( + SqlAppender sqlAppender, + JsonQueryArguments arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + + if ( arguments.wrapMode() == JsonQueryWrapMode.WITH_WRAPPER ) { + sqlAppender.appendSql( "json_build_array(" ); + } + arguments.jsonDocument().accept( walker ); + sqlAppender.appendSql( "::json #> '{" ); + String literalValue = walker.getLiteralValue( arguments.jsonPath() ); + + final JsonPathPassingClause passingClause = arguments.passingClause(); + if ( passingClause != null ) { + final Map passingExpressions = passingClause.getPassingExpressions(); + final Iterator> iterator = passingExpressions.entrySet().iterator(); + Map.Entry entry = iterator.next(); + literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + while ( iterator.hasNext() ) { + entry = iterator.next(); + sqlAppender.appendSql( ',' ); + literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + } + } + + sqlAppender.append( JsonHelper.parseJsonPath( literalValue ) ); + sqlAppender.appendSql( "}'" ); + if ( arguments.wrapMode() == JsonQueryWrapMode.WITH_WRAPPER ) { + sqlAppender.appendSql( ")" ); + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java new file mode 100644 index 000000000000..1dd82c67b24b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_remove function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonSetFunction. + */ +public class GaussDBJsonRemoveFunction extends AbstractJsonRemoveFunction { + + public GaussDBJsonRemoveFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator translator) { + + final Expression json = (Expression) arguments.get( 0 ); + final Expression jsonPath = (Expression) arguments.get( 1 ); + sqlAppender.appendSql( "json_remove(" ); + json.accept( translator ); + sqlAppender.appendSql( "," ); + jsonPath.accept( translator ); + sqlAppender.appendSql( ")" ); + } + + private boolean isJsonType(Expression expression) { + final JdbcMappingContainer expressionType = expression.getExpressionType(); + return expressionType != null && expressionType.getSingleJdbcMapping().getJdbcType().isJson(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java new file mode 100644 index 000000000000..11109fbb24a9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_replace function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonSetFunction. + */ +public class GaussDBJsonReplaceFunction extends AbstractJsonReplaceFunction { + + public GaussDBJsonReplaceFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator translator) { + + final Expression json = (Expression) arguments.get( 0 ); + final Expression jsonPath = (Expression) arguments.get( 1 ); + final SqlAstNode value = arguments.get( 2 ); + sqlAppender.appendSql( "json_replace(" ); + json.accept( translator ); + sqlAppender.appendSql( "," ); + jsonPath.accept( translator ); + sqlAppender.appendSql( "," ); + value.accept( translator ); + sqlAppender.appendSql( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java new file mode 100644 index 000000000000..cc481f992309 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_set function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonSetFunction. + */ +public class GaussDBJsonSetFunction extends AbstractJsonSetFunction { + + public GaussDBJsonSetFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List arguments, + ReturnableType returnType, + SqlAstTranslator translator) { + + final Expression json = (Expression) arguments.get( 0 ); + final Expression jsonPath = (Expression) arguments.get( 1 ); + final SqlAstNode value = arguments.get( 2 ); + sqlAppender.appendSql( "json_set(" ); + json.accept( translator ); + sqlAppender.appendSql( "," ); + jsonPath.accept( translator ); + sqlAppender.appendSql( "," ); + value.accept( translator ); + sqlAppender.appendSql( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java new file mode 100644 index 000000000000..96de171d0750 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.json; + +import java.util.Iterator; +import java.util.Map; + +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; +import org.hibernate.type.descriptor.jdbc.JsonHelper; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB json_value function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLJsonValueFunction. + */ +public class GaussDBJsonValueFunction extends JsonValueFunction { + + + public GaussDBJsonValueFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration, true, true ); + } + + @Override + protected void render( + SqlAppender sqlAppender, + JsonValueArguments arguments, + ReturnableType returnType, + SqlAstTranslator walker) { + + if (arguments.returningType() != null) { + sqlAppender.appendSql( "(" ); + } + arguments.jsonDocument().accept( walker ); + sqlAppender.appendSql( "::json #>> '{" ); + String literalValue = walker.getLiteralValue( arguments.jsonPath() ); + + final JsonPathPassingClause passingClause = arguments.passingClause(); + if ( passingClause != null ) { + final Map passingExpressions = passingClause.getPassingExpressions(); + final Iterator> iterator = passingExpressions.entrySet().iterator(); + Map.Entry entry = iterator.next(); + literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + while ( iterator.hasNext() ) { + entry = iterator.next(); + sqlAppender.appendSql( ',' ); + literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + } + } + + sqlAppender.append( JsonHelper.parseJsonPath( literalValue ) ); + sqlAppender.appendSql( "}'" ); + if (arguments.returningType() != null) { + sqlAppender.appendSql( ")::" ); + arguments.returningType().accept( walker ); + } + } + + @Override + protected void renderReturningClause(SqlAppender sqlAppender, JsonValueArguments arguments, SqlAstTranslator walker) { + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java new file mode 100644 index 000000000000..08f7aa9d3eca --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.function.xml; + +import java.util.List; + +import org.hibernate.dialect.function.json.ExpressionTypeHelper; +import org.hibernate.metamodel.model.domain.ReturnableType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * GaussDB xmlquery function. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLXmlQueryFunction. + */ +public class GaussDBXmlQueryFunction extends XmlQueryFunction { + + public GaussDBXmlQueryFunction(TypeConfiguration typeConfiguration) { + super( false, typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + final Expression xmlDocument = (Expression) sqlAstArguments.get( 1 ); + final boolean needsCast = !ExpressionTypeHelper.isXml( xmlDocument ); + sqlAppender.appendSql( "(select xmlagg(v) from unnest(xpath(" ); + sqlAstArguments.get( 0 ).accept( walker ); + sqlAppender.appendSql( ',' ); + if ( needsCast ) { + sqlAppender.appendSql( "cast(" ); + } + sqlAstArguments.get( 1 ).accept( walker ); + if ( needsCast ) { + sqlAppender.appendSql( " as xml)" ); + } + sqlAppender.appendSql( ")) t(v))" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java new file mode 100644 index 000000000000..2703ef8244d6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.identity; + +import static org.hibernate.internal.util.StringHelper.unquote; + +/** + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLIdentityColumnSupport. + */ +public class GaussDBIdentityColumnSupport extends IdentityColumnSupportImpl { + + public static final GaussDBIdentityColumnSupport INSTANCE = new GaussDBIdentityColumnSupport(); + @Override + public boolean supportsIdentityColumns() { + return true; + } + + @Override + public String getIdentitySelectString(String table, String column, int type) { + return "select currval('" + unquote(table) + '_' + unquote(column) + "_seq')"; + } + + @Override + public String getIdentityColumnString(int type) { + return "generated by default as identity"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sequence/GaussDBSequenceSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/sequence/GaussDBSequenceSupport.java new file mode 100644 index 000000000000..9b97eaf0b70b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sequence/GaussDBSequenceSupport.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.sequence; + +import org.hibernate.MappingException; +import org.hibernate.dialect.GaussDBDialect; + +/** + * Sequence support for {@link GaussDBDialect}. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLAggregateSupport. + */ +public class GaussDBSequenceSupport implements SequenceSupport { + + public static final SequenceSupport INSTANCE = new GaussDBSequenceSupport(); + + @Override + public String getSelectSequenceNextValString(String sequenceName) { + return "nextval('" + sequenceName + "')"; + } + + @Override + public String getSelectSequencePreviousValString(String sequenceName) throws MappingException { + return "currval('" + sequenceName + "')"; + } + + @Override + public boolean sometimesNeedsStartingValue() { + return true; + } + + @Override + public String getDropSequenceString(String sequenceName) { + return "drop sequence if exists " + sequenceName; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/GaussDBCallableStatementSupport.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/GaussDBCallableStatementSupport.java new file mode 100644 index 000000000000..b7e2818dbc0a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/GaussDBCallableStatementSupport.java @@ -0,0 +1,184 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.procedure.internal; + +import java.util.List; + +import org.hibernate.HibernateException; +import org.hibernate.dialect.AbstractGaussDBStructJdbcType; +import org.hibernate.procedure.spi.FunctionReturnImplementor; +import org.hibernate.procedure.spi.ProcedureCallImplementor; +import org.hibernate.procedure.spi.ProcedureParameterImplementor; +import org.hibernate.query.OutputableType; +import org.hibernate.query.spi.ProcedureParameterMetadataImplementor; +import org.hibernate.sql.exec.internal.JdbcCallImpl; +import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration; +import org.hibernate.sql.exec.spi.JdbcOperationQueryCall; +import org.hibernate.type.SqlTypes; + +import jakarta.persistence.ParameterMode; + +/** + * GaussDB implementation of CallableStatementSupport. + * + * @author liubao + * + * Notes: Original code of this class is based on PostgreSQLTruncFunction. + */ +public class GaussDBCallableStatementSupport extends AbstractStandardCallableStatementSupport { + /** + * Singleton access + */ + public static final GaussDBCallableStatementSupport INSTANCE = new GaussDBCallableStatementSupport( true ); + public static final GaussDBCallableStatementSupport V10_INSTANCE = new GaussDBCallableStatementSupport( false ); + + private final boolean supportsProcedures; + + private GaussDBCallableStatementSupport(boolean supportsProcedures) { + this.supportsProcedures = supportsProcedures; + } + + @Override + public JdbcOperationQueryCall interpretCall(ProcedureCallImplementor procedureCall) { + final String procedureName = procedureCall.getProcedureName(); + final FunctionReturnImplementor functionReturn = procedureCall.getFunctionReturn(); + final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata(); + final boolean firstParamIsRefCursor = parameterMetadata.getParameterCount() != 0 + && isFirstParameterModeRefCursor( parameterMetadata ); + + final List> registrations = parameterMetadata.getRegistrationsAsList(); + final int paramStringSizeEstimate; + if ( functionReturn == null && parameterMetadata.hasNamedParameters() ) { + // That's just a rough estimate. I guess most params will have fewer than 8 chars on average + paramStringSizeEstimate = registrations.size() * 10; + } + else { + // For every param rendered as '?' we have a comma, hence the estimate + paramStringSizeEstimate = registrations.size() * 2; + } + final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder(); + + final int jdbcParameterOffset; + final int startIndex; + final CallMode callMode; + if ( functionReturn != null ) { + if ( functionReturn.getJdbcTypeCode() == SqlTypes.REF_CURSOR ) { + if ( firstParamIsRefCursor ) { + // validate that the parameter strategy is positional (cannot mix, and REF_CURSOR is inherently positional) + if ( parameterMetadata.hasNamedParameters() ) { + throw new HibernateException( "Cannot mix named parameters and REF_CURSOR parameter on GaussDB" ); + } + callMode = CallMode.CALL_RETURN; + startIndex = 1; + jdbcParameterOffset = 1; + builder.addParameterRegistration( registrations.get( 0 ).toJdbcParameterRegistration( 1, procedureCall ) ); + } + else { + callMode = CallMode.TABLE_FUNCTION; + startIndex = 0; + jdbcParameterOffset = 1; + // Old style +// callMode = CallMode.CALL_RETURN; +// startIndex = 0; +// jdbcParameterOffset = 2; +// builder.setFunctionReturn( functionReturn.toJdbcFunctionReturn( procedureCall.getSession() ) ); + } + } + else { + callMode = CallMode.FUNCTION; + startIndex = 0; + jdbcParameterOffset = 1; + } + } + else if ( supportsProcedures ) { + jdbcParameterOffset = 1; + startIndex = 0; + callMode = CallMode.NATIVE_CALL; + } + else if ( firstParamIsRefCursor ) { + // validate that the parameter strategy is positional (cannot mix, and REF_CURSOR is inherently positional) + if ( parameterMetadata.hasNamedParameters() ) { + throw new HibernateException( "Cannot mix named parameters and REF_CURSOR parameter on GaussDB" ); + } + jdbcParameterOffset = 1; + startIndex = 1; + callMode = CallMode.CALL_RETURN; + builder.addParameterRegistration( registrations.get( 0 ).toJdbcParameterRegistration( 1, procedureCall ) ); + } + else { + jdbcParameterOffset = 1; + startIndex = 0; + callMode = CallMode.CALL; + } + + final StringBuilder buffer = new StringBuilder( callMode.start.length() + callMode.end.length() + procedureName.length() + paramStringSizeEstimate ) + .append( callMode.start ); + buffer.append( procedureName ); + + if ( startIndex == registrations.size() ) { + buffer.append( '(' ); + } + else { + char sep = '('; + for ( int i = startIndex; i < registrations.size(); i++ ) { + final ProcedureParameterImplementor parameter = registrations.get( i ); + if ( !supportsProcedures && parameter.getMode() == ParameterMode.REF_CURSOR ) { + throw new HibernateException( + "GaussDB supports only one REF_CURSOR parameter, but multiple were registered" ); + } + buffer.append( sep ); + final JdbcCallParameterRegistration registration = parameter.toJdbcParameterRegistration( + i + jdbcParameterOffset, + procedureCall + ); + final OutputableType type = registration.getParameterType(); + final String castType; + if ( parameter.getName() != null ) { + buffer.append( parameter.getName() ).append( " => " ); + } + if ( type != null && type.getJdbcType() instanceof AbstractGaussDBStructJdbcType ) { + // We have to cast struct type parameters so that GaussDB understands nulls + castType = ( (AbstractGaussDBStructJdbcType) type.getJdbcType() ).getStructTypeName(); + buffer.append( "cast(" ); + } + else { + castType = null; + } + buffer.append( "?" ); + if ( castType != null ) { + buffer.append( " as " ).append( castType ).append( ')' ); + } + sep = ','; + builder.addParameterRegistration( registration ); + } + } + + buffer.append( callMode.end ); + builder.setCallableName( buffer.toString() ); + return builder.buildJdbcCall(); + } + + private static boolean isFirstParameterModeRefCursor(ProcedureParameterMetadataImplementor parameterMetadata) { + return parameterMetadata.getRegistrationsAsList().get( 0 ).getMode() == ParameterMode.REF_CURSOR; + } + + enum CallMode { + TABLE_FUNCTION("select * from ", ")"), + FUNCTION("select ", ")"), + NATIVE_CALL("call ", ")"), + CALL_RETURN("{?=call ", ")}"), + CALL("{call ", ")}"); + + private final String start; + private final String end; + + CallMode(String start, String end) { + this.start = start; + this.end = end; + } + + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java index c596ea03eac7..c63bab9e1309 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java @@ -59,6 +59,7 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; +import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -187,6 +188,7 @@ public interface SqlAstWalker { void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate); void visitBetweenPredicate(BetweenPredicate betweenPredicate); + void visitLessThanPredicate(LessThanPredicate lessThanPredicate); void visitFilterPredicate(FilterPredicate filterPredicate); void visitFilterFragmentPredicate(FilterPredicate.FilterFragmentPredicate fragmentPredicate); 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 d140524d8bd0..abc5089df9eb 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 @@ -172,6 +172,7 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; +import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -7545,6 +7546,14 @@ public void visitBetweenPredicate(BetweenPredicate betweenPredicate) { betweenPredicate.getUpperBound().accept( this ); } + @Override + public void visitLessThanPredicate(LessThanPredicate lessThanPredicate) { + + lessThanPredicate.getExpression().accept( this ); + appendSql( " < " ); + lessThanPredicate.getUpperBound().accept( this ); + } + @Override public void visitFilterPredicate(FilterPredicate filterPredicate) { // visits each fragment with " and " between them diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java index ceefcf50c63d..28311b3a0aae 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java @@ -65,6 +65,7 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; +import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -603,4 +604,10 @@ public void visitStandardTableDelete(TableDeleteStandard tableDelete) { public void visitCustomTableDelete(TableDeleteCustomSql tableDelete) { throw new UnsupportedOperationException(); } + + @Override + public void visitLessThanPredicate(LessThanPredicate lessThanPredicate) { + + throw new UnsupportedOperationException(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java index 49572a3f1870..72993c01e08c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java @@ -63,6 +63,7 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; +import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -650,4 +651,10 @@ public void visitCustomTableDelete(TableDeleteCustomSql tableDelete) { public void visitColumnWriteFragment(ColumnWriteFragment columnWriteFragment) { throw new UnsupportedOperationException(); } + + @Override + public void visitLessThanPredicate(LessThanPredicate lessThanPredicate) { + + throw new UnsupportedOperationException(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java new file mode 100644 index 000000000000..cb243abbd2ea --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.sql.ast.tree.predicate; + +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.sql.ast.SqlAstWalker; +import org.hibernate.sql.ast.tree.expression.Expression; + +/** + * @author Steve Ebersole + */ +public class LessThanPredicate extends AbstractPredicate { + + private final Expression expression; + private final Expression upperBound; + + public LessThanPredicate( + Expression expression, + Expression upperBound, + boolean negated, + JdbcMappingContainer expressionType) { + super( expressionType, negated ); + this.expression = expression; + this.upperBound = upperBound; + } + + public Expression getExpression() { + return expression; + } + + public Expression getUpperBound() { + return upperBound; + } + + @Override + public void accept(SqlAstWalker sqlTreeWalker) { + sqlTreeWalker.visitLessThanPredicate( this ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java index e6bd54878626..94e43b7f989c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java @@ -11,9 +11,11 @@ import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.AbstractCollection; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; @@ -1624,4 +1626,30 @@ public T[] toArray(T[] a) { } } + public static String parseJsonPath(String path) { + if (path == null || !path.startsWith("$")) { + throw new IllegalArgumentException("Invalid JSON path"); + } + + List result = new ArrayList<>(); + String[] parts = path.substring(1).split("\\."); + + for (String part : parts) { + while (part.contains("[")) { + int start = part.indexOf("["); + int end = part.indexOf("]", start); + if (end == -1) { + throw new IllegalArgumentException("Invalid JSON path format"); + } + result.add(part.substring(0, start)); + result.add(part.substring(start + 1, end)); + part = part.substring(end + 1); + } + if (!part.isEmpty()) { + result.add(part); + } + } + + return String.join(",", result); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java index 689e5de9a2c1..e1433a70ed0b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java @@ -13,12 +13,14 @@ import org.hibernate.orm.test.annotations.collectionelement.ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.Event; import org.hibernate.orm.test.annotations.collectionelement.ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.Plan; import org.hibernate.orm.test.annotations.collectionelement.ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.SubPlan; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.jdbc.SQLStatementInspector; import org.hibernate.testing.orm.junit.DomainModel; 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.BeforeEach; import org.junit.jupiter.api.Test; @@ -80,6 +82,9 @@ public void setUp(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, + reason = "type:resolved.If you operate a table with the same name as the system view under the schema, " + + "you will be redirected to the system view and an error will be reported.") public void testInitializeCollection(SessionFactoryScope scope) { final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); statementInspector.clear(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java index 4678d372f113..f5a09b2dba4c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java @@ -14,6 +14,7 @@ import org.hibernate.Transaction; import org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -560,6 +561,9 @@ public void testNativeQueryAndCompositePKAndComponents(SessionFactoryScope scope } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, + reason = "type:resolved.If you operate a table with the same name as the system view under the schema, " + + "you will be redirected to the system view and an error will be reported.") public void testDiscriminator(SessionFactoryScope scope) { scope.inSession( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java index 29f4ef580ea1..9d4def6e6c46 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java @@ -11,6 +11,7 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventType; @@ -37,6 +38,7 @@ import org.hibernate.orm.test.jpa.pack.various.Seat; import org.hibernate.stat.Statistics; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.testing.transaction.TransactionUtil; import org.hibernate.testing.util.ServiceRegistryUtil; import org.junit.jupiter.api.AfterEach; @@ -63,6 +65,7 @@ * @author Gavin King * @author Hardy Ferentschik */ +@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class PackagedEntityManagerTest extends PackagingTestCase { private EntityManagerFactory emf; @AfterEach diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java index 1c978909d5fa..5a395f0b1088 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java @@ -33,6 +33,8 @@ import org.hibernate.orm.test.jpa.pack.defaultpar.Version; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.util.ServiceRegistryUtil; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import org.junit.jupiter.api.Test; @@ -80,6 +82,7 @@ private void assertClassesContained(ScanResult scanResult, Class classToCheckFor } @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testCustomScanner() throws Exception { File defaultPar = buildDefaultPar(); File explicitPar = buildExplicitPar(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java index 12b7b9905b3a..052f99fa2269 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java @@ -20,6 +20,8 @@ import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -41,6 +43,7 @@ public class LazyBasicFieldMergeTest { @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void test(SessionFactoryScope scope) { scope.inTransaction( session -> { Manager manager = new Manager(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java index 3f361fe9c266..f776f3c67a21 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java @@ -21,6 +21,8 @@ import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import org.junit.jupiter.api.Test; /** @@ -38,6 +40,7 @@ public class LazyInitializationWithoutInlineDirtyTrackingTest { @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void test(SessionFactoryScope scope) { scope.inTransaction( s -> { File file = new File(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java index 11c92368019b..97aef0acd867 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java @@ -9,12 +9,15 @@ import java.util.List; import java.util.Set; +import org.hibernate.dialect.GaussDBDialect; + import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.JiraKey; 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.Test; import jakarta.persistence.Entity; @@ -106,6 +109,7 @@ public void testMergeParentWithoutChildren(SessionFactoryScope scope) { @Test @Jira("HHH-18177") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testMergeTransientInstanceWithGeneratedId(SessionFactoryScope scope) { Book merged = scope.fromTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java index ef62a5bef7ea..9fae651509d0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java @@ -7,10 +7,13 @@ import java.util.ArrayList; import java.util.List; +import org.hibernate.dialect.GaussDBDialect; + import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; 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.Test; import jakarta.persistence.CascadeType; @@ -38,6 +41,7 @@ public class CompositeIdAndMergeTest { @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testMerge(SessionFactoryScope scope) { Integer lineItemIndex = 2; Order persistedOrder = scope.fromTransaction( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java index 150f6cbafe3d..dd3d80d02aec 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java @@ -21,6 +21,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.AltibaseDialect; import org.hibernate.community.dialect.FirebirdDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; @@ -146,6 +147,7 @@ public void testNumeric(BiConsumer { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/basic/ExpressionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/basic/ExpressionsTest.java index 8644e45a0583..013845681fb3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/basic/ExpressionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/basic/ExpressionsTest.java @@ -32,6 +32,7 @@ import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -308,6 +309,7 @@ public void testSumWithSubqueryPath() { @Test @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "numeric overflows") @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "does not support extract(epoch)") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "datediff overflow limits") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.date multi overflows") public void testDateTimeOperations() { HibernateCriteriaBuilder builder = (HibernateCriteriaBuilder) this.builder; doInJPA( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java index 3710d83115e3..a366786923ae 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java @@ -22,6 +22,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.AltibaseDialect; import org.hibernate.community.dialect.FirebirdDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.CockroachDialect; import org.hibernate.community.dialect.DerbyDialect; @@ -1186,6 +1187,7 @@ public void testLockTimeoutEMProps() throws Exception { @SkipForDialect(value = CockroachDialect.class, comment = "Cockroach supports the 'for no key update' syntax but it doesn't work") @SkipForDialect(value = FirebirdDialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks") @SkipForDialect(value = AltibaseDialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks") + @SkipForDialect(value = GaussDBDialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks") public void testLockInsertFkTarget() { Lock lock = new Lock(); lock.setName( "name" ); @@ -1225,6 +1227,7 @@ public void testLockInsertFkTarget() { @SkipForDialect(value = CockroachDialect.class, comment = "Cockroach supports the 'for no key update' syntax but it doesn't work") @SkipForDialect(value = FirebirdDialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks") @SkipForDialect(value = AltibaseDialect.class, comment = "FK constraint checks are not compatible with exclusive locks") + @SkipForDialect(value = GaussDBDialect.class, comment = "FK constraint checks are not compatible with exclusive locks") public void testLockUpdateFkTarget() { Lock lock1 = new Lock(); lock1.setName( "l1" ); 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 40a22d98f1c8..08f94d6d7061 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 @@ -24,6 +24,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.AltibaseDialect; import org.hibernate.community.dialect.FirebirdDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.AbstractTransactSQLDialect; import org.hibernate.dialect.CockroachDialect; @@ -155,6 +156,7 @@ public void bitType() { @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle maps tinyint to number") @SkipForDialect(dialectClass = FirebirdDialect.class, reason = "No support for the tinyint datatype so we use smallint") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase maps tinyint to smallint") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Turns tinyints into shorts in result sets and advertises the type as short in the metadata") public void tinyintType() { createEntityManagerFactory( TinyintEntity.class ); doTest( TinyintEntity.class, (byte)127 ); @@ -297,6 +299,7 @@ public void lobTypes() { @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "EDB maps DATE and TIME to TIMESTAMP") @SkipForDialect(dialectClass = SybaseDialect.class, reason = "Sybase maps DATE and TIME to TIMESTAMP", matchSubTypes = true) @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase maps DATE and TIME to TIMESTAMP") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Gaussdb's Oracle model maps DATE and TIME to TIMESTAMP") public void dateTimeTypes() { createEntityManagerFactory( DateEntity.class, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryWithDatetimesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryWithDatetimesTest.java index b601baf28285..939c699f5434 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryWithDatetimesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NativeQueryWithDatetimesTest.java @@ -8,6 +8,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgresPlusDialect; @@ -26,6 +27,7 @@ public class NativeQueryWithDatetimesTest { @SkipForDialect(dialectClass = PostgresPlusDialect.class) @SkipForDialect(dialectClass = OracleDialect.class) + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss will map localdate to timestamp") @Test void test(EntityManagerFactoryScope scope) { scope.inTransaction(s -> s.persist(new Datetimes())); Object[] result = scope.fromTransaction(s -> (Object[]) s.createNativeQuery("select ctime, cdate, cdatetime from tdatetimes", Object[].class).getSingleResult()); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java index e601476775b5..17e0ee5b030a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java @@ -28,6 +28,7 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.PostgresPlusDialect; @@ -381,6 +382,7 @@ public Class getParameterType() { @Test @SkipForDialect(value = PostgreSQLDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") + @SkipForDialect(value = GaussDBDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") public void testNativeQueryNullPositionalParameter() throws Exception { @@ -416,6 +418,7 @@ public void testNativeQueryNullPositionalParameter() throws Exception { @Test @JiraKey(value = "HHH-10161") @SkipForDialect(value = PostgreSQLDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") + @SkipForDialect(value = GaussDBDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") public void testNativeQueryNullPositionalParameterParameter() throws Exception { @@ -467,6 +470,7 @@ public Class getParameterType() { @Test @SkipForDialect(value = PostgreSQLDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") + @SkipForDialect(value = GaussDBDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") public void testNativeQueryNullNamedParameter() throws Exception { @@ -502,6 +506,7 @@ public void testNativeQueryNullNamedParameter() throws Exception { @Test @JiraKey(value = "HHH-10161") @SkipForDialect(value = PostgreSQLDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") + @SkipForDialect(value = GaussDBDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot determine the parameter types and bind type is unknown because the value is null") public void testNativeQueryNullNamedParameterParameter() throws Exception { @@ -574,6 +579,7 @@ public void testQueryContainsQuotedSemicolonWithLimit() { @Test @JiraKey("HHH-18033") + @SkipForDialect(value = GaussDBDialect.class, comment = "Doesn't support semicolon as ending of statement") public void testNativeQueryContainsQuotedSemicolonWithLimit() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -600,6 +606,7 @@ public void testNativeQueryContainsQuotedSemicolonWithLimit() { @SkipForDialect(value = SybaseDialect.class, comment = "Doesn't support semicolon as ending of statement") @SkipForDialect(value = DerbyDialect.class, comment = "Doesn't support semicolon as ending of statement") @SkipForDialect(value = DB2Dialect.class, comment = "Doesn't support semicolon as ending of statement") + @SkipForDialect(value = GaussDBDialect.class, comment = "Doesn't support semicolon as ending of statement") public void testNativeQueryContainsQuotedSemicolonAndEndsWithSemicolonWithLimit() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java index 056673d79be7..7d6162254dd8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java @@ -19,6 +19,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; @@ -74,6 +75,7 @@ public void destroy() { @JiraKey(value = "HHH-12192") @SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "on postgres we send 'set client_min_messages = WARNING'") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.on gauss we send 'set client_min_messages = WARNING'") public void testErrorMessageContainsTheFailingDDLCommand() { try { entityManagerFactoryBuilder.generateSchema(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java index 6e8f8ad4461d..517176df1004 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java @@ -17,6 +17,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; @@ -65,6 +66,7 @@ public void destroy() { @JiraKey(value = "HHH-12192") @SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "on postgres we send 'set client_min_messages = WARNING'") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.on gauss we send 'set client_min_messages = WARNING'") public void testErrorMessageContainsTheFailingDDLCommand() { try { entityManagerFactoryBuilder.generateSchema(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java index a9f290bdc3d7..507acaa04179 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java @@ -7,12 +7,14 @@ import java.sql.Clob; import java.util.List; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.DomainModel; 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; @@ -67,6 +69,7 @@ public void tearDown(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class) public void testLengthFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { final Query query = session.createQuery( @@ -86,6 +89,7 @@ public void testLengthFunction(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class) public void testOctetLengthFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { final Query query = session.createQuery( @@ -106,6 +110,7 @@ public void testOctetLengthFunction(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class) public void testBitLengthFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { final Query query = session.createQuery( @@ -126,6 +131,7 @@ public void testBitLengthFunction(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class) public void testConcatFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { // Use trim('') instead of '' since Sybase interprets that as single space string... diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java index 3c5b445a43d5..237077c7598f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java @@ -6,6 +6,7 @@ import java.util.Arrays; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.testing.orm.junit.SessionFactory; @@ -29,6 +30,7 @@ public abstract class LongByteArrayTest { private static final int ARRAY_SIZE = 10000; @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testBoundedLongByteArrayAccess(SessionFactoryScope scope) { byte[] original = buildRecursively( ARRAY_SIZE, true ); byte[] changed = buildRecursively( ARRAY_SIZE, false ); @@ -79,6 +81,7 @@ public void testBoundedLongByteArrayAccess(SessionFactoryScope scope) { @Test @SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "Sybase returns byte[]{0}") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testEmptyArray(SessionFactoryScope scope) { byte[] empty = new byte[] {}; @@ -102,6 +105,7 @@ public void testEmptyArray(SessionFactoryScope scope) { } @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testSaving(SessionFactoryScope scope) { byte[] value = buildRecursively( ARRAY_SIZE, true ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java index 347bd781e62a..997e66713a13 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java @@ -10,11 +10,13 @@ import jakarta.persistence.criteria.CriteriaQuery; import org.hibernate.LockMode; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -71,6 +73,7 @@ public void testHql() { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.gauss's bug") public void testCriteria() { inTransaction( s -> { @@ -96,6 +99,7 @@ public void testCriteria() { @Test // @Ignore( "Support for locking on native-sql queries not yet implemented" ) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.not support") public void testNativeSql() { inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/ArrayOfArraysTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/ArrayOfArraysTest.java index 988b2e50b3aa..9f9850f1c77f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/ArrayOfArraysTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/ArrayOfArraysTest.java @@ -9,6 +9,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.type.SqlTypes; @@ -39,6 +40,7 @@ public class ArrayOfArraysTest { @ServiceRegistry( settings = @Setting( name = AvailableSettings.HBM2DDL_AUTO, value = "create-drop" ) ) @Test @SkipForDialect( dialectClass = CockroachDialect.class, reason = "Unable to find server array type for provided name bytes" ) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Method com.huawei.gaussdb.jdbc.jdbc.PgArray.getArrayImpl(long,int,Map) is not yet implemented.") public void testDoubleByteArrayWorks(SessionFactoryScope scope) { final Long id = scope.fromTransaction( session -> { final EntityWithDoubleByteArray entity = new EntityWithDoubleByteArray(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java index d057f7b7b7a8..539b0760dd78 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java @@ -13,6 +13,8 @@ import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import static org.junit.Assert.assertArrayEquals; /** @@ -28,6 +30,7 @@ protected Class[] getAnnotatedClasses() { } @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void test() { Integer productId = doInJPA(this::entityManagerFactory, entityManager -> { final Product product = new Product(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java index 7f6a7f1fdbbc..35193d76e9c6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java @@ -13,6 +13,7 @@ import org.hibernate.annotations.JavaType; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; @@ -27,6 +28,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -47,6 +49,7 @@ public class ByteArrayMappingTests { @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void verifyMappings(SessionFactoryScope scope) { final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory() .getRuntimeMetamodels() diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java index cb37bc6228dc..be2761e9e709 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java @@ -9,6 +9,7 @@ import org.hibernate.annotations.Nationalized; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.NationalizationSupport; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; @@ -21,6 +22,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -47,6 +49,7 @@ public class WrapperArrayHandlingLegacyTests { @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void verifyByteArrayMappings(SessionFactoryScope scope) { final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory() .getRuntimeMetamodels() diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java index 077af442343e..51708aa645f1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java @@ -12,6 +12,7 @@ import org.hibernate.community.dialect.AltibaseDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; @@ -99,6 +100,7 @@ public void tearDown(SessionFactoryScope scope) { } @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void verifyMappings(SessionFactoryScope scope) { final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory() .getRuntimeMetamodels() @@ -123,6 +125,7 @@ public void verifyMappings(SessionFactoryScope scope) { } @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void verifyReadWorks(SessionFactoryScope scope) { scope.inTransaction( (session) -> { @@ -140,6 +143,7 @@ public void verifyReadWorks(SessionFactoryScope scope) { @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase doesn't support comparing LOBs with the = operator") @SkipForDialect(dialectClass = OracleDialect.class, matchSubTypes = true, reason = "Oracle doesn't support comparing JSON with the = operator") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase doesn't support comparing CLOBs with the = operator") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void verifyComparisonWorks(SessionFactoryScope scope) { scope.inTransaction( (session) -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java index 97bb31d15c2d..e781bd02dab3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java @@ -5,6 +5,8 @@ package org.hibernate.orm.test.merge; import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import org.junit.Before; import org.junit.Test; @@ -46,6 +48,7 @@ public void setUp() { } @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testMerge() { doInJPA(this::entityManagerFactory, entityManager -> { Post post = entityManager.find(Post.class, 1L); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java index 6755b4d67cab..cd52da779c5b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.function.Consumer; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.query.common.JoinType; import org.hibernate.query.criteria.HibernateCriteriaBuilder; @@ -27,6 +28,7 @@ import org.hibernate.testing.orm.junit.DomainModel; 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; @@ -216,6 +218,7 @@ public void testEmbeddedRoot(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsSubqueryInOnClause.class) @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsOrderByInCorrelatedSubquery.class) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resovling.not support") public void testEmbedded(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java index 362a17ae9960..7ce55c128bba 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java @@ -17,6 +17,7 @@ import jakarta.persistence.criteria.ParameterExpression; import org.hibernate.dialect.CockroachDialect; import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaExpression; @@ -344,6 +345,7 @@ public void testCollatePostgreSQL(SessionFactoryScope scope) { @Test @SkipForDialect(dialectClass = CockroachDialect.class, reason = "Cockroach has unreliable support for numeric types in log function") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Unsupported function.") public void testLog(SessionFactoryScope scope) { scope.inTransaction( session -> { HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); @@ -417,6 +419,7 @@ public void testAtan2(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Function sinh(double precision) does not exist.") public void testHyperbolic(SessionFactoryScope scope) { scope.inTransaction( session -> { HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); 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 9dadefc0c7b1..8d84a60308ef 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 @@ -14,6 +14,7 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.HSQLDialect; @@ -210,6 +211,7 @@ public void testImplicitCollectionJoinInSelect(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testImplicitCollectionJoinInWhere(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -389,6 +391,7 @@ public void testAggregateIndexElementKeyValueWithAlias(SessionFactoryScope scope } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testMaxindexMaxelement(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -508,6 +511,7 @@ public void testTrigFunctions(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Unsupported function") public void testMathFunctions(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -733,6 +737,7 @@ public void testLocateFunction(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testOverlayFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1169,6 +1174,7 @@ public void testCastFunctionWithLength(SessionFactoryScope scope) { @SkipForDialect(dialectClass = DB2Dialect.class, majorVersion = 10, minorVersion = 5, reason = "On this version the length of the cast to the parameter appears to be > 2") @SkipForDialect( dialectClass = AltibaseDialect.class, reason = "Altibase cast to raw does not do truncatation") @SkipForDialect(dialectClass = HSQLDialect.class, reason = "HSQL interprets string as hex literal and produces error") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Gaussdb bytea doesn't have a length") public void testCastBinaryWithLength(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1675,6 +1681,7 @@ public void testDurationBy(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testDurationLiterals(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1784,6 +1791,7 @@ public void testDurationArithmeticOverflowing(SessionFactoryScope scope) { } @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.interval_mul result month: 0.000000, day: 432000000000000.000000 overflow") public void testDurationArithmeticWithLiterals(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1886,6 +1894,7 @@ public void testDurationSubtractionWithTimeLiterals(SessionFactoryScope scope) { @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "numeric overflow") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.interval_mul result month: 0.000000, day: 432000000000000.000000 overflow") public void testDurationSubtractionWithDatetimeLiterals(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1966,6 +1975,7 @@ public void testDurationArithmeticWithParameters(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.Bad value for type long") public void testIntervalDiffExpressions(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -2278,6 +2288,7 @@ public void testExtractFunctionWithAssertions(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsFormat.class) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testFormat(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -2312,6 +2323,7 @@ public void testFormatTime(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsMedian.class) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testMedian(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -2333,6 +2345,7 @@ public void testMedian(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Function sinh(double precision) does not exist.") public void testHyperbolic(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -2406,6 +2419,7 @@ public void testIn(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testMaxGreatest(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java index bba7fd5af42a..051c34595348 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java @@ -6,6 +6,7 @@ import java.time.LocalDate; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; @@ -60,6 +61,7 @@ public void cleanupData(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict do\"") public void testOnConflictDoNothing(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -84,6 +86,7 @@ public void testOnConflictDoNothing(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(\"") public void testOnConflictDoUpdate(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -111,6 +114,7 @@ public void testOnConflictDoUpdate(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(") public void testOnConflictDoUpdateWithWhere(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -141,6 +145,7 @@ else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof Sy @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(") public void testOnConflictDoUpdateWithWhereCriteria(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -176,6 +181,7 @@ else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof Sy } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict do\"") public void testOnConflictDoNothingMultiTable(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -203,6 +209,7 @@ public void testOnConflictDoNothingMultiTable(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) @SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "MERGE into a table that has a self-referential FK does not work") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(") public void testOnConflictDoUpdateMultiTable(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -231,6 +238,7 @@ public void testOnConflictDoUpdateMultiTable(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) @SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "MERGE into a table that has a self-referential FK does not work") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "syntax error at or near \"conflict(\"") public void testOnConflictDoUpdateWithWhereMultiTable(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java index 5df94e23a97d..9442257a9996 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java @@ -8,6 +8,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Tuple; import org.hibernate.cfg.QuerySettings; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsertSelect; import org.hibernate.query.criteria.JpaCriteriaInsertValues; @@ -18,6 +19,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; @@ -32,6 +34,7 @@ ) @SessionFactory @JiraKey("HHH-19314") +@SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resovling.not support") public class InsertConflictWithCriteriaCopyTreeEnabledTests { @Test diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java index 8a13b75952b6..44c9bda2bb79 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java @@ -4,11 +4,13 @@ */ package org.hibernate.orm.test.query.hql; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.DomainModel; 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.Setting; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; import static org.hibernate.cfg.QuerySettings.PORTABLE_INTEGER_DIVISION; @@ -20,6 +22,7 @@ @ServiceRegistry(settings = @Setting(name = PORTABLE_INTEGER_DIVISION, value = "true")) public class IntegerDivisionTest { @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss has different behavior") public void testIntegerDivision(SessionFactoryScope scope) { scope.inTransaction(s -> { assertFalse( s.createQuery("select 1 where 1/2 = 0 and 4/3 = 1", Integer.class) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java index 21676974c929..9fc4063acd7e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java @@ -18,6 +18,7 @@ import org.hibernate.cfg.QuerySettings; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MySQLDialect; @@ -71,6 +72,7 @@ @ServiceRegistry(settings = @Setting(name = QuerySettings.JSON_FUNCTIONS_ENABLED, value = "true")) @SessionFactory @Jira("https://hibernate.atlassian.net/browse/HHH-18496") +@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class JsonFunctionTests { JsonHolder entity; @@ -217,6 +219,7 @@ public void testJsonQuery(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonQueryNestedPath.class) + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Not support $.theNestedObjects[*].id") public void testJsonQueryNested(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -418,6 +421,7 @@ public void testJsonObjectAgg(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonObjectAgg.class) + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Gauss has different behavior") public void testJsonObjectAggNullFilter(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -460,6 +464,7 @@ public void testJsonObjectAggNullClause(SessionFactoryScope scope) { @SkipForDialect(dialectClass = DB2Dialect.class, reason = "DB2 has no way to throw an error on duplicate json object keys.") @SkipForDialect(dialectClass = CockroachDialect.class, reason = "CockroachDB has no way to throw an error on duplicate json object keys.") @SkipForDialect(dialectClass = PostgreSQLDialect.class, majorVersion = 15, matchSubTypes = true, reason = "CockroachDB has no way to throw an error on duplicate json object keys.") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.not supported") public void testJsonObjectAggUniqueKeys(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -621,6 +626,7 @@ public void testJsonInsertWithExisting(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonMergepatch.class) + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss has different function definition") public void testJsonMergepatch(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -638,6 +644,7 @@ public void testJsonMergepatch(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonMergepatch.class) + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss has different behavior") public void testJsonMergepatchVarargs(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java index f0bb2c58d37c..6ee74d2c8ff5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java @@ -6,6 +6,7 @@ import java.util.List; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.testing.orm.domain.StandardDomainModel; @@ -14,6 +15,7 @@ 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; @@ -51,6 +53,7 @@ public void tearDown(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss's has different behavior") public void testDefaultEscapeBackslash(SessionFactoryScope scope) { scope.inTransaction( session -> { Query q = session.createQuery( @@ -64,6 +67,7 @@ public void testDefaultEscapeBackslash(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss's has different behavior") public void testDefaultEscapeBackslashLiteral(SessionFactoryScope scope) { scope.inTransaction( session -> { Query q = session.createQuery( @@ -77,6 +81,7 @@ public void testDefaultEscapeBackslashLiteral(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss's has different behavior") public void testDefaultEscapeNoResults(SessionFactoryScope scope) { scope.inTransaction( session -> { Query q = session.createQuery( 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 2bec6419a9b7..6b3ad9c1d350 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 @@ -16,6 +16,7 @@ import org.hibernate.dialect.CockroachDialect; import org.hamcrest.number.IsCloseTo; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; import org.hibernate.testing.orm.junit.DialectFeatureChecks; @@ -516,6 +517,7 @@ public void testIntervalScaleExpressions(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.Bad value for type long : 00:00:00") public void testIntervalDiffExpressions(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java index 450c41744979..eb8b98a3c7c8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java @@ -24,6 +24,7 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.cfg.QuerySettings; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.type.SqlTypes; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; @@ -35,6 +36,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; +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; @@ -58,6 +60,7 @@ @ServiceRegistry(settings = @Setting(name = QuerySettings.XML_FUNCTIONS_ENABLED, value = "true")) @SessionFactory @Jira("https://hibernate.atlassian.net/browse/HHH-18497") +@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class XmlFunctionTests { XmlHolder entity; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java index 4d17d92e0fb1..bddec3ac301c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java @@ -6,6 +6,7 @@ import java.time.LocalDate; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.testing.orm.junit.DialectFeatureChecks; @@ -14,6 +15,7 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; 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.Test; import jakarta.persistence.Entity; @@ -30,6 +32,7 @@ ) @SessionFactory @JiraKey( "HHH-18069" ) +@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class UnionOfPartitionResultsTest { @Test diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java index bb8491a1f32f..cfc5d33490e5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java @@ -16,6 +16,8 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import org.junit.jupiter.api.Test; import java.util.UUID; @@ -28,7 +30,9 @@ StatelessSessionVersioningTest.UUIDVersioned.class}) @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) public class StatelessSessionVersioningTest { - @Test void testIdentity(SessionFactoryScope scope) { + @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + void testIdentity(SessionFactoryScope scope) { Dialect dialect = scope.getMetadataImplementor().getDatabase().getDialect(); scope.inStatelessTransaction(s -> { IdentityVersioned v = new IdentityVersioned(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java index e222731e81a3..a849afb63611 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java @@ -8,6 +8,7 @@ import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MySQLDialect; @@ -88,6 +89,7 @@ public void startUp(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testById(SessionFactoryScope scope) { scope.inSession( em -> { TableWithDateArrays tableRecord; @@ -106,6 +108,7 @@ public void testById(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testQueryById(SessionFactoryScope scope) { scope.inSession( em -> { TypedQuery tq = em.createNamedQuery( "TableWithDateArrays.JPQL.getById", TableWithDateArrays.class ); @@ -117,6 +120,7 @@ public void testQueryById(SessionFactoryScope scope) { @Test @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "Seems that comparing date[] through JDBC is buggy. ERROR: operator does not exist: timestamp without time zone[] = date[]") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "Seems that comparing date[] through JDBC is buggy. ERROR: operator does not exist: timestamp without time zone[] = date[]") public void testQuery(SessionFactoryScope scope) { scope.inSession( em -> { TypedQuery tq = em.createNamedQuery( "TableWithDateArrays.JPQL.getByData", TableWithDateArrays.class ); @@ -127,6 +131,7 @@ public void testQuery(SessionFactoryScope scope) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testNativeQueryById(SessionFactoryScope scope) { scope.inSession( em -> { TypedQuery tq = em.createNamedQuery( "TableWithDateArrays.Native.getById", TableWithDateArrays.class ); @@ -145,6 +150,7 @@ public void testNativeQueryById(SessionFactoryScope scope) { @SkipForDialect(dialectClass = HANADialect.class, reason = "HANA requires a special function to compare LOBs") @SkipForDialect(dialectClass = MySQLDialect.class, matchSubTypes = true, reason = "MySQL supports distinct from through a special operator") @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "Seems that comparing date[] through JDBC is buggy. ERROR: operator does not exist: timestamp without time zone[] = date[]") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Seems that comparing date[] through JDBC is buggy. ERROR: operator does not exist: timestamp without time zone[] = date[]") public void testNativeQuery(SessionFactoryScope scope) { scope.inSession( em -> { final Dialect dialect = em.getDialect(); @@ -163,6 +169,7 @@ public void testNativeQuery(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsTypedArrays.class) @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "The 'date' type is a synonym for timestamp on Oracle and PostgresPlus, so untyped reading produces Timestamps") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.The 'date' type is a synonym for timestamp on Oracle and PostgresPlus, so untyped reading produces Timestamps") public void testNativeQueryUntyped(SessionFactoryScope scope) { scope.inSession( em -> { Query q = em.createNamedQuery( "TableWithDateArrays.Native.getByIdUntyped" ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java index 4d23ac402df6..b8f4aab303a1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java @@ -8,6 +8,7 @@ import java.util.UUID; import org.hibernate.PropertyValueException; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.testing.orm.junit.DialectFeatureChecks; @@ -16,6 +17,7 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; 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.Assertions; import org.junit.jupiter.api.Test; @@ -47,6 +49,7 @@ public class DetachedEntityWithNullVersionTest { private static final String ITEM_UPDATED_NAME = "updated name"; @Test + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testMergeDetachedEntityWithIdentityId(SessionFactoryScope scope) { IdentityGeneratedIdItem item = new IdentityGeneratedIdItem(); persistItem( scope, item ); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java b/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java index 88d39c68db8a..3afcbeace26a 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java @@ -9,6 +9,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.service.spi.Configurable; @@ -16,6 +17,7 @@ import org.hibernate.service.spi.Stoppable; import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -30,6 +32,7 @@ protected void augmentConfigurationSettings(Properties properties) { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsNumeric() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); @@ -54,6 +57,7 @@ public void testSettingIsolationAsNumeric() throws Exception { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsNumericString() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); @@ -78,6 +82,7 @@ public void testSettingIsolationAsNumericString() throws Exception { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsName() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); @@ -102,6 +107,7 @@ public void testSettingIsolationAsName() throws Exception { } @Test + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsNameAlt() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); 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 ea7a8b5a0f7a..5857e4522f38 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 @@ -47,6 +47,7 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.HSQLDialect; @@ -550,6 +551,7 @@ public boolean apply(Dialect dialect) { || dialect instanceof H2Dialect || dialect instanceof SQLServerDialect || dialect instanceof PostgreSQLDialect + || dialect instanceof GaussDBDialect || dialect instanceof DB2Dialect || dialect instanceof OracleDialect || dialect instanceof SybaseDialect diff --git a/local-build-plugins/src/main/groovy/local.databases.gradle b/local-build-plugins/src/main/groovy/local.databases.gradle index 150ec7f19236..70b27d437cea 100644 --- a/local-build-plugins/src/main/groovy/local.databases.gradle +++ b/local-build-plugins/src/main/groovy/local.databases.gradle @@ -75,6 +75,17 @@ ext { // 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', 'connection.init_sql' : '' ], + gaussdb : [ + 'db.dialect' : 'org.hibernate.dialect.GaussDBDialect', + 'jdbc.driver' : 'com.huawei.gaussdb.jdbc.Driver', + 'jdbc.user' : 'hibernate_orm_test', + 'jdbc.pass' : 'Hibernate_orm_test@1234', + // Disable prepared statement caching to avoid issues with changing schemas + // Make batch verification work, see https://bbs.huaweicloud.com/forum/thread-02104174303512776081-1-1.html + 'jdbc.url' : 'jdbc:gaussdb://' + dbHost + '/hibernate_orm_test?currentSchema=test&preparedStatementCacheQueries=0&batchMode=off', + 'jdbc.datasource' : 'com.huawei.gaussdb.jdbc.Driver', + 'connection.init_sql': '' + ], edb_ci : [ 'db.dialect' : 'org.hibernate.dialect.PostgresPlusDialect', 'jdbc.driver': 'org.postgresql.Driver', diff --git a/local-build-plugins/src/main/groovy/local.java-module.gradle b/local-build-plugins/src/main/groovy/local.java-module.gradle index 40499736c718..c61cd5f561ec 100644 --- a/local-build-plugins/src/main/groovy/local.java-module.gradle +++ b/local-build-plugins/src/main/groovy/local.java-module.gradle @@ -73,6 +73,7 @@ dependencies { testRuntimeOnly jdbcLibs.mssql testRuntimeOnly jdbcLibs.informix testRuntimeOnly jdbcLibs.cockroachdb + testRuntimeOnly jdbcLibs.gaussdb testRuntimeOnly jdbcLibs.sybase testRuntimeOnly rootProject.fileTree(dir: 'drivers', include: '*.jar') diff --git a/settings.gradle b/settings.gradle index b8c10b4d326d..e634f0e87acd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -227,6 +227,7 @@ dependencyResolutionManagement { def mysqlVersion = version "mysql", "9.2.0" def oracleVersion = version "oracle", "23.7.0.25.01" def pgsqlVersion = version "pgsql", "42.7.4" + def gaussdbVersion = version "gaussdb", "506.0.0.b058" def sybaseVersion = version "sybase", "1.3.1" def tidbVersion = version "tidb", mysqlVersion def altibaseVersion = version "altibase", "7.3.0.0.3" @@ -238,6 +239,7 @@ dependencyResolutionManagement { library( "derbyTools", "org.apache.derby", "derbytools" ).versionRef( derbyVersion ) library( "postgresql", "org.postgresql", "postgresql" ).versionRef( pgsqlVersion ) library( "cockroachdb", "org.postgresql", "postgresql" ).versionRef( pgsqlVersion ) + library( "gaussdb", "com.huaweicloud.gaussdb", "gaussdbjdbc" ).versionRef( gaussdbVersion ) library( "mysql", "com.mysql", "mysql-connector-j" ).versionRef( mysqlVersion ) library( "tidb", "com.mysql", "mysql-connector-j" ).versionRef( tidbVersion ) library( "mariadb", "org.mariadb.jdbc", "mariadb-java-client" ).versionRef( mariadbVersion ) From 00e1c329b3cfc041a97f712240dcb13275350358 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Fri, 25 Apr 2025 11:49:15 +0800 Subject: [PATCH 02/33] [HHH-19365] update default port --- ci/build.sh | 2 +- docker_db.sh | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ci/build.sh b/ci/build.sh index 836d4f49e630..7f1bb82a2341 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -15,7 +15,7 @@ elif [ "$RDBMS" == "mariadb" ] || [ "$RDBMS" == "mariadb_10_4" ]; then elif [ "$RDBMS" == "postgresql" ] || [ "$RDBMS" == "postgresql_13" ]; then goal="-Pdb=pgsql_ci" elif [ "$RDBMS" == "gaussdb" ]; then - goal="-Pdb=gaussdb -DdbHost=localhost:5432" + goal="-Pdb=gaussdb -DdbHost=localhost:8000" elif [ "$RDBMS" == "edb" ] || [ "$RDBMS" == "edb_13" ]; then goal="-Pdb=edb_ci -DdbHost=localhost:5444" elif [ "$RDBMS" == "oracle" ]; then diff --git a/docker_db.sh b/docker_db.sh index 3f2c047ee533..27c8d45dd9de 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -217,7 +217,7 @@ gaussdb() { # config param CONTAINER_NAME=opengauss IMAGE=opengauss/opengauss:7.0.0-RC1 - PORT=5432 + PORT=8000 DB_USER=hibernate_orm_test DB_PASSWORD=Hibernate_orm_test@1234 DB_NAME=hibernate_orm_test @@ -230,7 +230,7 @@ gaussdb() { -e GS_NODENAME=opengauss \ -e GS_PORT=${PORT} \ -e GS_CGROUP_DISABLE=YES \ - -p ${PORT}:5432 \ + -p ${PORT}:8000 \ -d ${IMAGE} echo "wait OpenGauss starting..." @@ -246,11 +246,9 @@ gaussdb() { " echo "Initialization completed" - echo "connection information£º" + echo "connection information" echo " Host: localhost" echo " Port: ${PORT}" - echo " Username: ${DB_USER}" - echo " Password: ${DB_PASSWORD}" echo " Database: ${DB_NAME}" } From 99d9348893064aea2681bc2d8faf97238e11d5c2 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Fri, 25 Apr 2025 14:39:29 +0800 Subject: [PATCH 03/33] HHH-19365 - Solve gaussdb function support --- .../org/hibernate/dialect/GaussDBDialect.java | 35 +++++++------------ .../function/CommonFunctionFactory.java | 15 -------- .../GaussDBIdentityColumnSupport.java | 2 +- 3 files changed, 13 insertions(+), 39 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index ac399819b5ea..7f77bf337715 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -565,37 +565,19 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.array_gaussdb(); functionFactory.arrayAggregate(); functionFactory.arrayRemoveIndex_gaussdb(); - functionFactory.arrayLength_cardinality(); functionFactory.arrayConcat_gaussdb(); functionFactory.arrayPrepend_gaussdb(); functionFactory.arrayAppend_gaussdb(); functionFactory.arrayContains_gaussdb(); functionFactory.arrayIntersects_gaussdb(); - functionFactory.arrayGet_bracket(); functionFactory.arrayRemove_gaussdb(); functionFactory.arraySlice_operator(); functionFactory.arrayReplace_gaussdb(); functionFactory.arraySet_gaussdb(); - functionFactory.arrayTrim_gaussdb(); functionFactory.arrayFill_gaussdb(); - functionFactory.arrayPosition_gaussdb(); - functionFactory.jsonValue_gaussdb(true); - functionFactory.jsonQuery_gaussdb(); - functionFactory.jsonExists_gaussdb(); - functionFactory.jsonArray(); functionFactory.jsonObject_gaussdb(); - functionFactory.jsonArrayAgg_gaussdb( true ); functionFactory.jsonObjectAgg_gaussdb( true ); - functionFactory.jsonTable(); - - functionFactory.jsonSet_gaussdb(); - functionFactory.jsonRemove_gaussdb(); - functionFactory.jsonReplace_gaussdb(); - functionFactory.jsonInsert_gaussdb(); - functionFactory.jsonArray_gaussdb(); - functionFactory.jsonMergepatch_gaussdb(); - functionFactory.jsonArrayInsert_gauss(); functionFactory.xmlelement(); functionFactory.xmlcomment(); @@ -605,7 +587,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.xmlquery_gaussdb(); functionFactory.xmlexists(); functionFactory.xmlagg(); - functionFactory.xmltable( true ); functionFactory.makeDateTimeTimestamp(); // Note that GaussDB doesn't support the OVER clause for ordered set-aggregate functions @@ -633,8 +614,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" ); functionFactory.dateTrunc(); - functionFactory.unnest( null, "ordinality" ); - functionFactory.hex( "encode(?1, 'hex')" ); functionFactory.sha( "sha256(?1)" ); functionFactory.md5( "decode(md5(?1), 'hex')" ); @@ -1057,6 +1036,11 @@ public IdentityColumnSupport getIdentityColumnSupport() { return GaussDBIdentityColumnSupport.INSTANCE; } + @Override + public boolean supportsExpectedLobUsagePattern() { + return false; + } + @Override public NationalizationSupport getNationalizationSupport() { return NationalizationSupport.IMPLICIT; @@ -1127,7 +1111,7 @@ public String translateExtractField(TemporalUnit unit) { @Override public AggregateSupport getAggregateSupport() { - return GaussDBAggregateSupport.valueOf( this ); + return null; } @Override @@ -1327,7 +1311,12 @@ public boolean supportsLateral() { @Override public boolean supportsRecursiveCTE() { - return true; + return false; + } + + @Override + public boolean supportsOrderByInSubquery() { + return false; } @Override 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 10629c63b550..e32adb5eccdd 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 @@ -2768,7 +2768,6 @@ public void arrayContains_postgresql() { * GaussDB array contains operator */ public void arrayContains_gaussdb() { - functionRegistry.register( "array_contains", new GaussDBArrayContainsOperatorFunction( false, typeConfiguration ) ); functionRegistry.register( "array_contains_nullable", new GaussDBArrayContainsOperatorFunction( true, typeConfiguration ) ); functionRegistry.register( "array_includes", new ArrayIncludesOperatorFunction( false, typeConfiguration ) ); functionRegistry.register( "array_includes_nullable", new ArrayIncludesOperatorFunction( true, typeConfiguration ) ); @@ -2859,13 +2858,6 @@ public void arrayPosition_postgresql() { functionRegistry.register( "array_position", new PostgreSQLArrayPositionFunction( typeConfiguration ) ); } - /** - * GaussDB array_position() function - */ - public void arrayPosition_gaussdb() { - functionRegistry.register( "array_position", new GaussDBArrayPositionFunction( typeConfiguration ) ); - } - /** * H2 array_position() function */ @@ -3345,13 +3337,6 @@ public void arrayTrim_oracle() { functionRegistry.register( "array_trim", new OracleArrayTrimFunction() ); } - /** - * GaussDB array_trim() emulation for versions before 14 - */ - public void arrayTrim_gaussdb() { - functionRegistry.register( "array_trim", new GaussDBArrayTrimFunction() ); - } - /** * H2 array_fill() function */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java index 2703ef8244d6..9a29c5b2841a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -16,7 +16,7 @@ public class GaussDBIdentityColumnSupport extends IdentityColumnSupportImpl { public static final GaussDBIdentityColumnSupport INSTANCE = new GaussDBIdentityColumnSupport(); @Override public boolean supportsIdentityColumns() { - return true; + return false; } @Override From 61b46acee68897233c6daeb1fcd263e5532fd43f Mon Sep 17 00:00:00 2001 From: liubao68 Date: Fri, 25 Apr 2025 16:16:02 +0800 Subject: [PATCH 04/33] [HHH-19365]!supportsIdentityColumns --- .../org/hibernate/dialect/GaussDBDialect.java | 9 +++---- .../json/GaussDBJsonExistsFunction.java | 25 ++++++++++++------- ...SetReleaseWithStatementDelegationTest.java | 6 ++--- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 7f77bf337715..cd9ed90fed4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -17,7 +17,6 @@ import java.util.Map; import java.util.TimeZone; -import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Length; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -26,13 +25,12 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.aggregate.AggregateSupport; -import org.hibernate.dialect.aggregate.GaussDBAggregateSupport; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.GaussDBMinMaxFunction; import org.hibernate.dialect.function.GaussDBTruncFunction; import org.hibernate.dialect.function.GaussDBTruncRoundFunction; -import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.GaussDBIdentityColumnSupport; +import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; import org.hibernate.dialect.sequence.GaussDBSequenceSupport; @@ -58,11 +56,11 @@ import org.hibernate.procedure.internal.GaussDBCallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; import org.hibernate.query.SemanticException; +import org.hibernate.query.common.FetchClauseType; +import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.CastType; -import org.hibernate.query.common.FetchClauseType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy; import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; @@ -100,6 +98,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; +import org.checkerframework.checker.nullness.qual.Nullable; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.query.common.TemporalUnit.DAY; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java index bd2267079556..4ac6a7ca07f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java @@ -4,6 +4,9 @@ */ package org.hibernate.dialect.function.json; +import java.util.Iterator; +import java.util.Map; + import org.hibernate.metamodel.model.domain.ReturnableType; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; @@ -11,19 +14,17 @@ import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; import org.hibernate.type.spi.TypeConfiguration; -import java.util.Iterator; -import java.util.Map; - /** * PostgreSQL json_query function. */ public class GaussDBJsonExistsFunction extends JsonExistsFunction { - public GaussDBJsonExistsFunction(TypeConfiguration typeConfiguration, - boolean supportsJsonPathExpression, - boolean supportsJsonPathPassingClause) { - super(typeConfiguration, supportsJsonPathExpression, supportsJsonPathPassingClause); + public GaussDBJsonExistsFunction( + TypeConfiguration typeConfiguration, + boolean supportsJsonPathExpression, + boolean supportsJsonPathPassingClause) { + super( typeConfiguration, supportsJsonPathExpression, supportsJsonPathPassingClause ); } @Override @@ -43,11 +44,17 @@ protected void render( final Map passingExpressions = passingClause.getPassingExpressions(); final Iterator> iterator = passingExpressions.entrySet().iterator(); Map.Entry entry = iterator.next(); - literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + literalValue = literalValue.replace( + "$" + entry.getKey(), + walker.getLiteralValue( entry.getValue() ).toString() + ); while ( iterator.hasNext() ) { entry = iterator.next(); sqlAppender.appendSql( ',' ); - literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + literalValue = literalValue.replace( + "$" + entry.getKey(), + walker.getLiteralValue( entry.getValue() ).toString() + ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ResultSetReleaseWithStatementDelegationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ResultSetReleaseWithStatementDelegationTest.java index ed7fb0f19173..cb2b7e41fe8d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ResultSetReleaseWithStatementDelegationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ResultSetReleaseWithStatementDelegationTest.java @@ -11,11 +11,11 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl; -import org.hibernate.testing.DialectChecks; -import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.logger.Triggerable; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; +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; @@ -39,7 +39,7 @@ provider = ResultSetReleaseWithStatementDelegationTest.ConnectionProviderDelegateProvider.class) ) @SessionFactory -@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) @JiraKey( "HHH-19280" ) class ResultSetReleaseWithStatementDelegationTest { From 707df9c39c7b002bf6cdf46bc4579f2dfa6b669b Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Fri, 25 Apr 2025 17:36:48 +0800 Subject: [PATCH 05/33] HHH-19365 - ignore gauss identity --- .../src/main/java/org/hibernate/dialect/GaussDBDialect.java | 1 - .../dialect/identity/GaussDBIdentityColumnSupport.java | 6 ++---- .../orm/test/jpa/naturalid/MutableNaturalIdTest.java | 3 +++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index cd9ed90fed4b..4be61ba8de41 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -585,7 +585,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.xmlpi(); functionFactory.xmlquery_gaussdb(); functionFactory.xmlexists(); - functionFactory.xmlagg(); functionFactory.makeDateTimeTimestamp(); // Note that GaussDB doesn't support the OVER clause for ordered set-aggregate functions diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java index 9a29c5b2841a..f9e9ff65aef8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -4,8 +4,6 @@ */ package org.hibernate.dialect.identity; -import static org.hibernate.internal.util.StringHelper.unquote; - /** * @author liubao * @@ -21,11 +19,11 @@ public boolean supportsIdentityColumns() { @Override public String getIdentitySelectString(String table, String column, int type) { - return "select currval('" + unquote(table) + '_' + unquote(column) + "_seq')"; + return ""; } @Override public String getIdentityColumnString(int type) { - return "generated by default as identity"; + return ""; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java index 4862c0032817..c9fefa608297 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java @@ -5,6 +5,7 @@ package org.hibernate.orm.test.jpa.naturalid; import org.hibernate.community.dialect.AltibaseDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.OracleDialect; @@ -26,6 +27,8 @@ reason = "Hana do not support identity key generation") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase do not support identity key generation") +@SkipForDialect(dialectClass = GaussDBDialect.class, + reason = "Gauss do not support identity key generation") public class MutableNaturalIdTest extends AbstractJPATest { @Override protected Class[] getAnnotatedClasses() { From e37429382ae632f3b32ad3796cb3769efab55588 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Fri, 25 Apr 2025 18:13:10 +0800 Subject: [PATCH 06/33] [HHH-19365]!appendDatetimeFormat --- .../org/hibernate/dialect/GaussDBDialect.java | 22 +------------------ .../GaussDBIdentityColumnSupport.java | 4 ---- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 4be61ba8de41..00137f1e2ef1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -1073,27 +1073,7 @@ public boolean supportsTemporalLiteralOffset() { @Override public void appendDatetimeFormat(SqlAppender appender, String format) { - appender.appendSql( datetimeFormat( format ).result() ); - } - - public Replacer datetimeFormat(String format) { - return OracleDialect.datetimeFormat( format, true, false ) - .replace("SSSSSS", "US") - .replace("SSSSS", "US") - .replace("SSSS", "US") - .replace("SSS", "MS") - .replace("SS", "MS") - .replace("S", "MS") - //use ISO day in week, as per DateTimeFormatter - .replace("ee", "ID") - .replace("e", "fmID") - //TZR is TZ - .replace("zzz", "TZ") - .replace("zz", "TZ") - .replace("z", "TZ") - .replace("xxx", "OF") - .replace("xx", "OF") - .replace("x", "OF"); + throw new UnsupportedOperationException( "GaussDB not support datetime format yet" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java index f9e9ff65aef8..fee0f0472c1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -12,10 +12,6 @@ public class GaussDBIdentityColumnSupport extends IdentityColumnSupportImpl { public static final GaussDBIdentityColumnSupport INSTANCE = new GaussDBIdentityColumnSupport(); - @Override - public boolean supportsIdentityColumns() { - return false; - } @Override public String getIdentitySelectString(String table, String column, int type) { From e4835dca3a3ff99ff4eb3e5fd8a18e587d491830 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Fri, 25 Apr 2025 22:46:53 +0800 Subject: [PATCH 07/33] [HHH-19365]!xml-functions --- .../org/hibernate/dialect/GaussDBDialect.java | 8 --- .../function/CommonFunctionFactory.java | 8 --- .../function/xml/GaussDBXmlQueryFunction.java | 50 ------------------- 3 files changed, 66 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 00137f1e2ef1..487c57b868e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -578,14 +578,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.jsonObject_gaussdb(); functionFactory.jsonObjectAgg_gaussdb( true ); - functionFactory.xmlelement(); - functionFactory.xmlcomment(); - functionFactory.xmlforest(); - functionFactory.xmlconcat(); - functionFactory.xmlpi(); - functionFactory.xmlquery_gaussdb(); - functionFactory.xmlexists(); - functionFactory.makeDateTimeTimestamp(); // Note that GaussDB doesn't support the OVER clause for ordered set-aggregate functions functionFactory.inverseDistributionOrderedSetAggregates(); 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 e32adb5eccdd..350d61219c3b 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 @@ -14,7 +14,6 @@ import org.hibernate.dialect.function.array.*; import org.hibernate.dialect.function.json.*; import org.hibernate.dialect.function.xml.DB2XmlTableFunction; -import org.hibernate.dialect.function.xml.GaussDBXmlQueryFunction; import org.hibernate.dialect.function.xml.H2XmlConcatFunction; import org.hibernate.dialect.function.xml.H2XmlElementFunction; import org.hibernate.dialect.function.xml.H2XmlForestFunction; @@ -4343,13 +4342,6 @@ public void xmlquery_postgresql() { functionRegistry.register( "xmlquery", new PostgreSQLXmlQueryFunction( typeConfiguration ) ); } - /** - * GaussDB xmlquery() function - */ - public void xmlquery_gaussdb() { - functionRegistry.register( "xmlquery", new GaussDBXmlQueryFunction( typeConfiguration ) ); - } - /** * SQL Server xmlquery() function */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java deleted file mode 100644 index 08f7aa9d3eca..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/GaussDBXmlQueryFunction.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.xml; - -import java.util.List; - -import org.hibernate.dialect.function.json.ExpressionTypeHelper; -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB xmlquery function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLXmlQueryFunction. - */ -public class GaussDBXmlQueryFunction extends XmlQueryFunction { - - public GaussDBXmlQueryFunction(TypeConfiguration typeConfiguration) { - super( false, typeConfiguration ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List sqlAstArguments, - ReturnableType returnType, - SqlAstTranslator walker) { - final Expression xmlDocument = (Expression) sqlAstArguments.get( 1 ); - final boolean needsCast = !ExpressionTypeHelper.isXml( xmlDocument ); - sqlAppender.appendSql( "(select xmlagg(v) from unnest(xpath(" ); - sqlAstArguments.get( 0 ).accept( walker ); - sqlAppender.appendSql( ',' ); - if ( needsCast ) { - sqlAppender.appendSql( "cast(" ); - } - sqlAstArguments.get( 1 ).accept( walker ); - if ( needsCast ) { - sqlAppender.appendSql( " as xml)" ); - } - sqlAppender.appendSql( ")) t(v))" ); - } -} From 69efee4eeb617838ae400e651deffe99b83e98fa Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 08:58:54 +0800 Subject: [PATCH 08/33] [HHH-19365]add no guarantee of the data order. --- .../hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java index d96296c4b93d..213db39a8b57 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java @@ -112,7 +112,8 @@ private void executeQuery(SessionFactoryScope scope, boolean criteria, boolean l final EntityGraph entityGraph = session.getEntityGraph( "test-graph" ); final List resultList = query.setHint( HINT_SPEC_FETCH_GRAPH, entityGraph ).getResultList(); assertThat( resultList ).hasSize( 2 ); - assertThat( resultList.stream().map( p -> p.getAddress().getId() ) ).containsExactly( 1L, 2L ); + // No order by, there is no guarantee of the data order. + assertThat( resultList.stream().map( p -> p.getAddress().getId() ) ).contains( 1L, 2L ); inspector.assertExecutedCount( 1 ); inspector.assertNumberOfOccurrenceInQuery( 0, "join", where ? 2 : 1 ); } ); From beb3430b4ca88f8f2ecca81352a249a2e37b9ea9 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 09:15:34 +0800 Subject: [PATCH 09/33] [HHH-19365]change limit handler --- .../org/hibernate/dialect/GaussDBDialect.java | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 487c57b868e4..5e4dbf960290 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -4,19 +4,9 @@ */ package org.hibernate.dialect; -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.TimeZone; - +import jakarta.persistence.GenerationType; +import jakarta.persistence.TemporalType; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Length; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -32,7 +22,7 @@ import org.hibernate.dialect.identity.GaussDBIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; -import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; +import org.hibernate.dialect.pagination.LimitLimitHandler; import org.hibernate.dialect.sequence.GaussDBSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; @@ -96,9 +86,18 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; -import jakarta.persistence.GenerationType; -import jakarta.persistence.TemporalType; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.query.common.TemporalUnit.DAY; @@ -754,7 +753,7 @@ public String getQuerySequencesString() { @Override public LimitHandler getLimitHandler() { - return OffsetFetchLimitHandler.INSTANCE; + return LimitLimitHandler.INSTANCE; } @Override From 7df08626e082e124f63d8e7929f6ff3ee45c1bbf Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 11:45:00 +0800 Subject: [PATCH 10/33] [HHH-19365]Hyperbolic functions --- .../org/hibernate/dialect/GaussDBDialect.java | 4 -- .../json/GaussDBJsonQueryFunction.java | 8 +-- .../json/GaussDBJsonValueFunction.java | 10 ++-- .../type/descriptor/jdbc/JsonHelper.java | 53 ++++++++++--------- .../orm/test/query/hql/FunctionTests.java | 8 ++- .../orm/junit/DialectFeatureChecks.java | 1 + 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 5e4dbf960290..dd8aa04ce260 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -520,10 +520,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.degrees(); functionFactory.log(); functionFactory.mod_operator(); - functionFactory.log10(); - functionFactory.tanh(); - functionFactory.sinh(); - functionFactory.cosh(); functionFactory.moreHyperbolic(); functionFactory.cbrt(); functionFactory.pi(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java index 4f5f7604c137..6809d5196f20 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java @@ -17,7 +17,7 @@ import java.util.Map; /** - * PostgreSQL json_query function. + * GaussDB json_query function. */ public class GaussDBJsonQueryFunction extends JsonQueryFunction { @@ -44,11 +44,13 @@ protected void render( final Map passingExpressions = passingClause.getPassingExpressions(); final Iterator> iterator = passingExpressions.entrySet().iterator(); Map.Entry entry = iterator.next(); - literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + literalValue = literalValue.replace( "$" + entry.getKey(), + walker.getLiteralValue( entry.getValue() ).toString() ); while ( iterator.hasNext() ) { entry = iterator.next(); sqlAppender.appendSql( ',' ); - literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + literalValue = literalValue.replace( "$" + entry.getKey(), + walker.getLiteralValue( entry.getValue() ).toString() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java index 96de171d0750..91e88a603e28 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java @@ -36,7 +36,7 @@ protected void render( ReturnableType returnType, SqlAstTranslator walker) { - if (arguments.returningType() != null) { + if ( arguments.returningType() != null ) { sqlAppender.appendSql( "(" ); } arguments.jsonDocument().accept( walker ); @@ -48,17 +48,19 @@ protected void render( final Map passingExpressions = passingClause.getPassingExpressions(); final Iterator> iterator = passingExpressions.entrySet().iterator(); Map.Entry entry = iterator.next(); - literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + literalValue = literalValue.replace( "$" + entry.getKey(), + walker.getLiteralValue( entry.getValue() ).toString() ); while ( iterator.hasNext() ) { entry = iterator.next(); sqlAppender.appendSql( ',' ); - literalValue = literalValue.replace( "$"+entry.getKey(), walker.getLiteralValue( entry.getValue()).toString() ); + literalValue = literalValue.replace( "$" + entry.getKey(), + walker.getLiteralValue( entry.getValue() ).toString() ); } } sqlAppender.append( JsonHelper.parseJsonPath( literalValue ) ); sqlAppender.appendSql( "}'" ); - if (arguments.returningType() != null) { + if ( arguments.returningType() != null ) { sqlAppender.appendSql( ")::" ); arguments.returningType().accept( walker ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java index 94e43b7f989c..7683ea53de72 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java @@ -98,6 +98,33 @@ public static String arrayToString( return sb.toString(); } + public static String parseJsonPath(String path) { + if (path == null || !path.startsWith("$")) { + throw new IllegalArgumentException("Invalid JSON path"); + } + + List result = new ArrayList<>(); + String[] parts = path.substring(1).split("\\."); + + for (String part : parts) { + while (part.contains("[")) { + int start = part.indexOf("["); + int end = part.indexOf("]", start); + if (end == -1) { + throw new IllegalArgumentException("Invalid JSON path format"); + } + result.add(part.substring(0, start)); + result.add(part.substring(start + 1, end)); + part = part.substring(end + 1); + } + if (!part.isEmpty()) { + result.add(part); + } + } + + return String.join(",", result); + } + private static void toString(EmbeddableMappingType embeddableMappingType, Object value, WrapperOptions options, JsonAppender appender) { toString( embeddableMappingType, options, appender, value, '{' ); appender.append( '}' ); @@ -1626,30 +1653,4 @@ public T[] toArray(T[] a) { } } - public static String parseJsonPath(String path) { - if (path == null || !path.startsWith("$")) { - throw new IllegalArgumentException("Invalid JSON path"); - } - - List result = new ArrayList<>(); - String[] parts = path.substring(1).split("\\."); - - for (String part : parts) { - while (part.contains("[")) { - int start = part.indexOf("["); - int end = part.indexOf("]", start); - if (end == -1) { - throw new IllegalArgumentException("Invalid JSON path format"); - } - result.add(part.substring(0, start)); - result.add(part.substring(start + 1, end)); - part = part.substring(end + 1); - } - if (!part.isEmpty()) { - result.add(part); - } - } - - return String.join(",", result); - } } 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 8d84a60308ef..60d429d39d4f 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 @@ -1021,7 +1021,7 @@ public void testCastFunction(SessionFactoryScope scope) { assertThat( session.createQuery("select cast('1911-10-09' as Date)", Date.class).getSingleResult(), instanceOf(Date.class) ); assertThat( session.createQuery("select cast('1911-10-09 12:13:14.123' as Timestamp)", Timestamp.class).getSingleResult(), instanceOf(Timestamp.class) ); - assertThat( session.createQuery("select cast(date 1911-10-09 as String)", String.class).getSingleResult(), is("1911-10-09") ); + assertThat( session.createQuery("select cast(date 1911-10-09 as String)", String.class).getSingleResult(), anyOf( is("1911-10-09"), is("1911-10-09 00:00:00") ) ); assertThat( session.createQuery("select cast(time 12:13:14 as String)", String.class).getSingleResult(), anyOf( is("12:13:14"), is("12:13:14.0000"), is("12.13.14") ) ); assertThat( session.createQuery("select cast(datetime 1911-10-09 12:13:14 as String)", String.class).getSingleResult(), anyOf( startsWith("1911-10-09 12:13:14"), startsWith("1911-10-09-12.13.14") ) ); @@ -1222,7 +1222,8 @@ public void testStrFunction(SessionFactoryScope scope) { session.createQuery("select str(e.id), str(e.theInt), str(e.theDouble) from EntityOfBasics e", Object[].class) .list(); assertThat( session.createQuery("select str(69)", String.class).getSingleResult(), is("69") ); - assertThat( session.createQuery("select str(date 1911-10-09)", String.class).getSingleResult(), is("1911-10-09") ); + // str() do not specify the standard of date & time format + assertThat( session.createQuery("select str(date 1911-10-09)", String.class).getSingleResult(), anyOf( is("1911-10-09"), is( "1911-10-09 00:00:00" ) ) ); assertThat( session.createQuery("select str(time 12:13:14)", String.class).getSingleResult(), anyOf( is( "12:13:14"), is( "12:13:14.0000"), is( "12.13.14") ) ); } ); @@ -2288,7 +2289,6 @@ public void testExtractFunctionWithAssertions(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsFormat.class) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testFormat(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -2323,7 +2323,6 @@ public void testFormatTime(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsMedian.class) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testMedian(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -2345,7 +2344,6 @@ public void testMedian(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Function sinh(double precision) does not exist.") public void testHyperbolic(SessionFactoryScope scope) { scope.inTransaction( session -> { 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 5857e4522f38..841c0f4f5963 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 @@ -535,6 +535,7 @@ public boolean apply(Dialect dialect) { || dialect instanceof SybaseDialect || dialect instanceof DerbyDialect || dialect instanceof FirebirdDialect + || dialect instanceof GaussDBDialect || dialect instanceof DB2Dialect && ( (DB2Dialect) dialect ).getDB2Version().isBefore( 11 ) ); } } From fe1ffeb074cd3fa5e6d934c8ffb5e2ffebd41a4f Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 12:15:42 +0800 Subject: [PATCH 11/33] [HHH-19365]log10 functions --- .../org/hibernate/dialect/GaussDBDialect.java | 1 + .../orm/test/query/hql/FunctionTests.java | 17 +++++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index dd8aa04ce260..3a82b7d2cbec 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -523,6 +523,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.moreHyperbolic(); functionFactory.cbrt(); functionFactory.pi(); + functionFactory.log10_log(); functionFactory.trim2(); functionFactory.repeat(); functionFactory.initcap(); 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 60d429d39d4f..22fea36d5881 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 @@ -211,7 +211,6 @@ public void testImplicitCollectionJoinInSelect(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testImplicitCollectionJoinInWhere(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -391,7 +390,6 @@ public void testAggregateIndexElementKeyValueWithAlias(SessionFactoryScope scope } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testMaxindexMaxelement(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -511,7 +509,6 @@ public void testTrigFunctions(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Unsupported function") public void testMathFunctions(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -737,7 +734,6 @@ public void testLocateFunction(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testOverlayFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1174,7 +1170,7 @@ public void testCastFunctionWithLength(SessionFactoryScope scope) { @SkipForDialect(dialectClass = DB2Dialect.class, majorVersion = 10, minorVersion = 5, reason = "On this version the length of the cast to the parameter appears to be > 2") @SkipForDialect( dialectClass = AltibaseDialect.class, reason = "Altibase cast to raw does not do truncatation") @SkipForDialect(dialectClass = HSQLDialect.class, reason = "HSQL interprets string as hex literal and produces error") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Gaussdb bytea doesn't have a length") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "Gaussdb bytea doesn't have a length") public void testCastBinaryWithLength(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1682,7 +1678,6 @@ public void testDurationBy(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testDurationLiterals(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1792,7 +1787,8 @@ public void testDurationArithmeticOverflowing(SessionFactoryScope scope) { } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.interval_mul result month: 0.000000, day: 432000000000000.000000 overflow") + @SkipForDialect(dialectClass = GaussDBDialect.class, + reason = "GaussDB driver does not support implicit type casting to Long or Duration.") public void testDurationArithmeticWithLiterals(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1895,7 +1891,8 @@ public void testDurationSubtractionWithTimeLiterals(SessionFactoryScope scope) { @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "numeric overflow") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.interval_mul result month: 0.000000, day: 432000000000000.000000 overflow") + @SkipForDialect(dialectClass = GaussDBDialect.class, + reason = "numeric overflow") public void testDurationSubtractionWithDatetimeLiterals(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -1976,7 +1973,8 @@ public void testDurationArithmeticWithParameters(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.Bad value for type long") + @SkipForDialect( dialectClass = GaussDBDialect.class, + reason = "GaussDB driver does not support implicit type casting to Long or Duration.") public void testIntervalDiffExpressions(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -2417,7 +2415,6 @@ public void testIn(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testMaxGreatest(SessionFactoryScope scope) { scope.inTransaction( session -> { From 9220385d7ec2ab639e83f254ba6f606762291e3f Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 12:56:57 +0800 Subject: [PATCH 12/33] [HHH-19365]json functions --- .../org/hibernate/dialect/GaussDBDialect.java | 1 - .../function/CommonFunctionFactory.java | 28 ---------- .../json/GaussDBJsonArrayAppendFunction.java | 47 ----------------- .../json/GaussDBJsonMergepatchFunction.java | 44 ---------------- .../json/GaussDBJsonObjectAggFunction.java | 51 ------------------- .../orm/test/query/hql/JsonFunctionTests.java | 7 --- 6 files changed, 178 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 3a82b7d2cbec..4f0b2007fb2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -572,7 +572,6 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.arrayFill_gaussdb(); functionFactory.jsonObject_gaussdb(); - functionFactory.jsonObjectAgg_gaussdb( true ); functionFactory.makeDateTimeTimestamp(); // Note that GaussDB doesn't support the OVER clause for ordered set-aggregate functions 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 350d61219c3b..dad6835f9fca 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 @@ -3559,13 +3559,6 @@ public void jsonExists() { functionRegistry.register( "json_exists", new JsonExistsFunction( typeConfiguration, true, true ) ); } - /** - * json_exists() function - */ - public void jsonExists_gaussdb() { - functionRegistry.register( "json_exists", new GaussDBJsonExistsFunction( typeConfiguration, false, false ) ); - } - /** * json_exists() function that doesn't support the passing clause */ @@ -3846,13 +3839,6 @@ public void jsonObjectAgg_postgresql(boolean supportsStandard) { functionRegistry.register( "json_objectagg", new PostgreSQLJsonObjectAggFunction( supportsStandard, typeConfiguration ) ); } - /** - * GaussDB json_objectagg() function - */ - public void jsonObjectAgg_gaussdb(boolean supportsStandard) { - functionRegistry.register( "json_objectagg", new GaussDBJsonObjectAggFunction( supportsStandard, typeConfiguration ) ); - } - /** * MySQL json_objectagg() function */ @@ -4081,13 +4067,6 @@ public void jsonMergepatch_postgresql() { functionRegistry.register( "json_mergepatch", new PostgreSQLJsonMergepatchFunction( typeConfiguration ) ); } - /** - * GaussDB json_mergepatch() function - */ - public void jsonMergepatch_gaussdb() { - functionRegistry.register( "json_mergepatch", new GaussDBJsonMergepatchFunction( typeConfiguration ) ); - } - /** * MySQL json_mergepatch() function */ @@ -4118,13 +4097,6 @@ public void jsonArrayAppend_postgresql(boolean supportsLax) { functionRegistry.register( "json_array_append", new PostgreSQLJsonArrayAppendFunction( supportsLax, typeConfiguration ) ); } - /** - * GaussDB json_array_append() function - */ - public void jsonArrayAppend_gaussdb(boolean supportsLax) { - functionRegistry.register( "json_array_append", new GaussDBJsonArrayAppendFunction( supportsLax, typeConfiguration ) ); - } - /** * MySQL json_array_append() function */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java deleted file mode 100644 index 4eb0991551a2..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAppendFunction.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.mapping.JdbcMappingContainer; -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_array_append function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonArrayAppendFunction. - */ -public class GaussDBJsonArrayAppendFunction extends AbstractJsonArrayAppendFunction { - - private final boolean supportsLax; - - public GaussDBJsonArrayAppendFunction(boolean supportsLax, TypeConfiguration typeConfiguration) { - super( typeConfiguration ); - this.supportsLax = supportsLax; - } - - @Override - public void render( - SqlAppender sqlAppender, - List arguments, - ReturnableType returnType, - SqlAstTranslator translator) { - - - } - - private static boolean isJsonType(Expression expression) { - final JdbcMappingContainer expressionType = expression.getExpressionType(); - return expressionType != null && expressionType.getSingleJdbcMapping().getJdbcType().isJson(); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java deleted file mode 100644 index 5e6db40072dd..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonMergepatchFunction.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_mergepatch function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonMergepatchFunction. - */ -public class GaussDBJsonMergepatchFunction extends AbstractJsonMergepatchFunction { - - public GaussDBJsonMergepatchFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List arguments, - ReturnableType returnType, - SqlAstTranslator translator) { - - sqlAppender.appendSql( "json_merge" ); - char separator = '('; - for ( int i = 0; i < arguments.size(); i++ ) { - sqlAppender.appendSql( separator ); - arguments.get( i ).accept( translator ); - separator = ','; - } - sqlAppender.appendSql( ")" ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java deleted file mode 100644 index 2ad4ca528118..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectAggFunction.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_objectagg function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonObjectAggFunction. - */ -public class GaussDBJsonObjectAggFunction extends JsonObjectAggFunction { - - - public GaussDBJsonObjectAggFunction(boolean supportsStandard, TypeConfiguration typeConfiguration) { - super( ":", true, typeConfiguration ); - } - - @Override - protected void render( - SqlAppender sqlAppender, - JsonObjectAggArguments arguments, - Predicate filter, - ReturnableType returnType, - SqlAstTranslator translator) { - - sqlAppender.appendSql( "json_object_agg(" ); - sqlAppender.appendSql( "CASE WHEN " ); - arguments.key().accept( translator ); - sqlAppender.appendSql( " IS NOT NULL " ); - sqlAppender.appendSql( " THEN " ); - arguments.key().accept( translator ); - sqlAppender.appendSql( " END," ); - arguments.value().accept( translator ); - sqlAppender.appendSql( ")" ); - } - - @Override - protected void renderUniqueAndReturningClause(SqlAppender sqlAppender, JsonObjectAggArguments arguments, SqlAstTranslator translator) { - renderUniqueClause( sqlAppender, arguments, translator ); - renderReturningClause( sqlAppender, arguments, translator ); - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java index 9fc4063acd7e..21676974c929 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JsonFunctionTests.java @@ -18,7 +18,6 @@ import org.hibernate.cfg.QuerySettings; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MySQLDialect; @@ -72,7 +71,6 @@ @ServiceRegistry(settings = @Setting(name = QuerySettings.JSON_FUNCTIONS_ENABLED, value = "true")) @SessionFactory @Jira("https://hibernate.atlassian.net/browse/HHH-18496") -@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class JsonFunctionTests { JsonHolder entity; @@ -219,7 +217,6 @@ public void testJsonQuery(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonQueryNestedPath.class) - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Not support $.theNestedObjects[*].id") public void testJsonQueryNested(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -421,7 +418,6 @@ public void testJsonObjectAgg(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonObjectAgg.class) - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Gauss has different behavior") public void testJsonObjectAggNullFilter(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -464,7 +460,6 @@ public void testJsonObjectAggNullClause(SessionFactoryScope scope) { @SkipForDialect(dialectClass = DB2Dialect.class, reason = "DB2 has no way to throw an error on duplicate json object keys.") @SkipForDialect(dialectClass = CockroachDialect.class, reason = "CockroachDB has no way to throw an error on duplicate json object keys.") @SkipForDialect(dialectClass = PostgreSQLDialect.class, majorVersion = 15, matchSubTypes = true, reason = "CockroachDB has no way to throw an error on duplicate json object keys.") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.not supported") public void testJsonObjectAggUniqueKeys(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -626,7 +621,6 @@ public void testJsonInsertWithExisting(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonMergepatch.class) - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss has different function definition") public void testJsonMergepatch(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -644,7 +638,6 @@ public void testJsonMergepatch(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonMergepatch.class) - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss has different behavior") public void testJsonMergepatchVarargs(SessionFactoryScope scope) { scope.inTransaction( session -> { From f37da37858006412877e95db2afc98f1cc86e4a6 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 13:47:09 +0800 Subject: [PATCH 13/33] [HHH-19365]unused skip annotations --- .../org/hibernate/orm/test/query/hql/XmlFunctionTests.java | 3 --- .../orm/test/query/hql/set/UnionOfPartitionResultsTest.java | 3 --- .../orm/test/stateless/StatelessSessionVersioningTest.java | 3 --- 3 files changed, 9 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java index eb8b98a3c7c8..450c41744979 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/XmlFunctionTests.java @@ -24,7 +24,6 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.cfg.QuerySettings; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.type.SqlTypes; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; @@ -36,7 +35,6 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; -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; @@ -60,7 +58,6 @@ @ServiceRegistry(settings = @Setting(name = QuerySettings.XML_FUNCTIONS_ENABLED, value = "true")) @SessionFactory @Jira("https://hibernate.atlassian.net/browse/HHH-18497") -@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class XmlFunctionTests { XmlHolder entity; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java index bddec3ac301c..4d17d92e0fb1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java @@ -6,7 +6,6 @@ import java.time.LocalDate; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.testing.orm.junit.DialectFeatureChecks; @@ -15,7 +14,6 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; 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.Test; import jakarta.persistence.Entity; @@ -32,7 +30,6 @@ ) @SessionFactory @JiraKey( "HHH-18069" ) -@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class UnionOfPartitionResultsTest { @Test diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java index cfc5d33490e5..ed05a5092eca 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java @@ -16,8 +16,6 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; -import org.hibernate.testing.orm.junit.SkipForDialect; -import org.hibernate.dialect.GaussDBDialect; import org.junit.jupiter.api.Test; import java.util.UUID; @@ -31,7 +29,6 @@ @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) public class StatelessSessionVersioningTest { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") void testIdentity(SessionFactoryScope scope) { Dialect dialect = scope.getMetadataImplementor().getDatabase().getDialect(); scope.inStatelessTransaction(s -> { From 65d165144b9cd3e3971d4f96b35786c862fdcdf4 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 14:58:29 +0800 Subject: [PATCH 14/33] [HHH-19365]unused skip functions --- .../org/hibernate/dialect/GaussDBDialect.java | 2 +- .../function/CommonFunctionFactory.java | 65 +------------- .../json/GaussDBJsonArrayAggFunction.java | 63 -------------- .../json/GaussDBJsonArrayFunction.java | 84 ------------------- .../json/GaussDBJsonArrayInsertFunction.java | 48 ----------- .../json/GaussDBJsonInsertFunction.java | 47 ----------- .../json/GaussDBJsonQueryFunction.java | 64 -------------- .../json/GaussDBJsonRemoveFunction.java | 50 ----------- .../json/GaussDBJsonReplaceFunction.java | 47 ----------- .../function/json/GaussDBJsonSetFunction.java | 47 ----------- .../json/GaussDBJsonValueFunction.java | 72 ---------------- ...bleWithEntityWithEntityCollectionTest.java | 8 +- .../annotations/query/QueryAndSQLTest.java | 4 - .../scanning/PackagedEntityManagerTest.java | 3 - 14 files changed, 4 insertions(+), 600 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 4f0b2007fb2a..fa2691725f07 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -552,7 +552,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.insert_overlay(); functionFactory.overlay(); functionFactory.soundex(); //was introduced apparently - functionFactory.format_toChar_gauss(); + functionFactory.format_toChar_gaussdb(); functionFactory.locate_positionSubstring(); functionFactory.windowFunctions(); 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 dad6835f9fca..0737ac678806 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 @@ -2577,7 +2577,7 @@ public void format_toChar() { * * @see org.hibernate.dialect.OracleDialect#datetimeFormat */ - public void format_toChar_gauss() { + public void format_toChar_gaussdb() { functionRegistry.register( "format", new GaussDBFormatFunction( "to_char", typeConfiguration ) ); } @@ -3440,13 +3440,6 @@ public void jsonValue_postgresql(boolean supportsStandard) { functionRegistry.register( "json_value", new PostgreSQLJsonValueFunction( supportsStandard, typeConfiguration ) ); } - /** - * GaussDB json_value() function - */ - public void jsonValue_gaussdb(boolean supportsStandard) { - functionRegistry.register( "json_value", new GaussDBJsonValueFunction( typeConfiguration ) ); - } - /** * CockroachDB json_value() function */ @@ -3489,13 +3482,6 @@ public void jsonQuery() { functionRegistry.register( "json_query", new JsonQueryFunction( typeConfiguration, true, true ) ); } - /** - * GaussDB json_query() function - */ - public void jsonQuery_gaussdb() { - functionRegistry.register( "json_query", new GaussDBJsonQueryFunction( typeConfiguration ) ); - } - /** * json_query() function */ @@ -3706,13 +3692,6 @@ public void jsonArray_sqlserver(boolean supportsExtendedJson) { functionRegistry.register( "json_array", new SQLServerJsonArrayFunction( supportsExtendedJson, typeConfiguration ) ); } - /** - * GaussDB json_array() function - */ - public void jsonArray_gaussdb() { - functionRegistry.register( "json_array", new GaussDBJsonArrayFunction( typeConfiguration ) ); - } - /** * SAP HANA json_array() function */ @@ -3776,13 +3755,6 @@ public void jsonArrayAgg_postgresql(boolean supportsStandard) { functionRegistry.register( "json_arrayagg", new PostgreSQLJsonArrayAggFunction( supportsStandard, typeConfiguration ) ); } - /** - * GaussDB json_arrayagg() function - */ - public void jsonArrayAgg_gaussdb(boolean supportsStandard) { - functionRegistry.register( "json_arrayagg", new GaussDBJsonArrayAggFunction( supportsStandard, typeConfiguration ) ); - } - /** * SQL Server json_arrayagg() function */ @@ -3881,13 +3853,6 @@ public void jsonSet_postgresql() { functionRegistry.register( "json_set", new PostgreSQLJsonSetFunction( typeConfiguration ) ); } - /** - * GaussDB json_set() function - */ - public void jsonSet_gaussdb() { - functionRegistry.register( "json_set", new GaussDBJsonSetFunction( typeConfiguration ) ); - } - /** * MySQL json_set() function */ @@ -3926,13 +3891,6 @@ public void jsonRemove_postgresql() { functionRegistry.register( "json_remove", new PostgreSQLJsonRemoveFunction( typeConfiguration ) ); } - /** - * GaussDB json_remove() function - */ - public void jsonRemove_gaussdb() { - functionRegistry.register( "json_remove", new GaussDBJsonRemoveFunction( typeConfiguration ) ); - } - /** * CockroachDB json_remove() function */ @@ -3977,13 +3935,6 @@ public void jsonReplace_postgresql() { functionRegistry.register( "json_replace", new PostgreSQLJsonReplaceFunction( typeConfiguration ) ); } - /** - * GaussDB json_replace() function - */ - public void jsonReplace_gaussdb() { - functionRegistry.register( "json_replace", new GaussDBJsonReplaceFunction( typeConfiguration ) ); - } - /** * MySQL json_replace() function */ @@ -4022,13 +3973,6 @@ public void jsonInsert_postgresql() { functionRegistry.register( "json_insert", new PostgreSQLJsonInsertFunction( typeConfiguration ) ); } - /** - * GaussDB json_insert() function - */ - public void jsonInsert_gaussdb() { - functionRegistry.register( "json_insert", new GaussDBJsonInsertFunction( typeConfiguration ) ); - } - /** * MySQL json_insert() function */ @@ -4142,13 +4086,6 @@ public void jsonArrayInsert_postgresql() { functionRegistry.register( "json_array_insert", new PostgreSQLJsonArrayInsertFunction( typeConfiguration ) ); } - /** - * gauss json_array_insert() function - */ - public void jsonArrayInsert_gauss() { - functionRegistry.register( "json_array_insert", new GaussDBJsonArrayInsertFunction( typeConfiguration ) ); - } - /** * MySQL json_array_insert() function */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java deleted file mode 100644 index 75bf0e8c893c..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayAggFunction.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.model.domain.ReturnableType; -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.Expression; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.select.SortSpecification; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_arrayagg function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonArrayAggFunction. - */ -public class GaussDBJsonArrayAggFunction extends JsonArrayAggFunction { - - private final boolean supportsStandard; - - public GaussDBJsonArrayAggFunction(boolean supportsStandard, TypeConfiguration typeConfiguration) { - super( true, typeConfiguration ); - this.supportsStandard = supportsStandard; - } - - @Override - public void render( - SqlAppender sqlAppender, - List sqlAstArguments, - Predicate filter, - List withinGroup, - ReturnableType returnType, - SqlAstTranslator translator) { - - final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); - - sqlAppender.appendSql( "to_jsonb( array_agg( CASE WHEN " ); - arrayExpression.accept( translator ); - sqlAppender.appendSql( " IS NOT NULL THEN " ); - arrayExpression.accept( translator ); - sqlAppender.appendSql( "::text ELSE NULL END" ); - if ( withinGroup != null && !withinGroup.isEmpty() ) { - translator.getCurrentClauseStack().push( Clause.WITHIN_GROUP ); - sqlAppender.appendSql( " order by " ); - withinGroup.get( 0 ).accept( translator ); - for ( int i = 1; i < withinGroup.size(); i++ ) { - sqlAppender.appendSql( ',' ); - withinGroup.get( i ).accept( translator ); - } - translator.getCurrentClauseStack().pop(); - } - sqlAppender.appendSql( ") ) AS result" ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java deleted file mode 100644 index 9a918b1946a6..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayFunction.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; -import org.hibernate.query.sqm.function.FunctionKind; -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.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JsonNullBehavior; -import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.type.SqlTypes; -import org.hibernate.type.spi.TypeConfiguration; - -import java.util.List; - - -/** - * Notes: Original code of this class is based on JsonArrayFunction. - */ -public class GaussDBJsonArrayFunction extends AbstractSqmSelfRenderingFunctionDescriptor { - - public GaussDBJsonArrayFunction(TypeConfiguration typeConfiguration) { - super( - "json_array", - FunctionKind.NORMAL, - null, - StandardFunctionReturnTypeResolvers.invariant( - typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON ) - ), - null - ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List sqlAstArguments, - ReturnableType returnType, - SqlAstTranslator walker) { - sqlAppender.appendSql( "json_array" ); - char separator = '('; - if ( sqlAstArguments.isEmpty() ) { - sqlAppender.appendSql( separator ); - renderReturningClause( sqlAppender, walker ); - } - else { - final SqlAstNode lastArgument = sqlAstArguments.get( sqlAstArguments.size() - 1 ); - final JsonNullBehavior nullBehavior; - final int argumentsCount; - if ( lastArgument instanceof JsonNullBehavior ) { - nullBehavior = (JsonNullBehavior) lastArgument; - argumentsCount = sqlAstArguments.size() - 1; - } - else { - nullBehavior = JsonNullBehavior.ABSENT; - argumentsCount = sqlAstArguments.size(); - } - for ( int i = 0; i < argumentsCount; i++ ) { - Expression valueNode = (Expression) sqlAstArguments.get( i ); - if ( nullBehavior == JsonNullBehavior.ABSENT && valueNode instanceof Literal ) { - Object literalValue = ((Literal) valueNode).getLiteralValue(); - if ( literalValue == null ) { - continue; - } - } - sqlAppender.appendSql( separator ); - valueNode.accept( walker ); - separator = ','; - } - renderReturningClause( sqlAppender, walker ); - } - sqlAppender.appendSql( ')' ); - } - - protected void renderReturningClause(SqlAppender sqlAppender, SqlAstTranslator walker) { - // No-op - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java deleted file mode 100644 index dd9399e00750..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonArrayInsertFunction.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_array_insert function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonArrayInsertFunction. - */ -public class GaussDBJsonArrayInsertFunction extends AbstractJsonArrayInsertFunction { - - public GaussDBJsonArrayInsertFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List arguments, - ReturnableType returnType, - SqlAstTranslator translator) { - - final Expression json = (Expression) arguments.get( 0 ); - final Expression jsonPath = (Expression) arguments.get( 1 ); - final SqlAstNode value = arguments.get( 2 ); - - sqlAppender.append( "json_array_insert(" ); - json.accept( translator ); - sqlAppender.append( "," ); - jsonPath.accept( translator ); - sqlAppender.append( "," ); - value.accept( translator ); - sqlAppender.append( ")" ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java deleted file mode 100644 index c15d4c1be072..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonInsertFunction.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_insert function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonInsertFunction. - */ -public class GaussDBJsonInsertFunction extends AbstractJsonInsertFunction { - - public GaussDBJsonInsertFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List arguments, - ReturnableType returnType, - SqlAstTranslator translator) { - - final Expression json = (Expression) arguments.get( 0 ); - final Expression jsonPath = (Expression) arguments.get( 1 ); - final SqlAstNode value = arguments.get( 2 ); - sqlAppender.appendSql( "json_insert(" ); - json.accept( translator ); - sqlAppender.appendSql( "," ); - jsonPath.accept( translator ); - sqlAppender.appendSql( "," ); - value.accept( translator ); - sqlAppender.appendSql( ")" ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java deleted file mode 100644 index 6809d5196f20..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonQueryFunction.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; -import org.hibernate.sql.ast.tree.expression.JsonQueryWrapMode; -import org.hibernate.type.descriptor.jdbc.JsonHelper; -import org.hibernate.type.spi.TypeConfiguration; - -import java.util.Iterator; -import java.util.Map; - -/** - * GaussDB json_query function. - */ -public class GaussDBJsonQueryFunction extends JsonQueryFunction { - - public GaussDBJsonQueryFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration, true, true ); - } - - @Override - protected void render( - SqlAppender sqlAppender, - JsonQueryArguments arguments, - ReturnableType returnType, - SqlAstTranslator walker) { - - if ( arguments.wrapMode() == JsonQueryWrapMode.WITH_WRAPPER ) { - sqlAppender.appendSql( "json_build_array(" ); - } - arguments.jsonDocument().accept( walker ); - sqlAppender.appendSql( "::json #> '{" ); - String literalValue = walker.getLiteralValue( arguments.jsonPath() ); - - final JsonPathPassingClause passingClause = arguments.passingClause(); - if ( passingClause != null ) { - final Map passingExpressions = passingClause.getPassingExpressions(); - final Iterator> iterator = passingExpressions.entrySet().iterator(); - Map.Entry entry = iterator.next(); - literalValue = literalValue.replace( "$" + entry.getKey(), - walker.getLiteralValue( entry.getValue() ).toString() ); - while ( iterator.hasNext() ) { - entry = iterator.next(); - sqlAppender.appendSql( ',' ); - literalValue = literalValue.replace( "$" + entry.getKey(), - walker.getLiteralValue( entry.getValue() ).toString() ); - } - } - - sqlAppender.append( JsonHelper.parseJsonPath( literalValue ) ); - sqlAppender.appendSql( "}'" ); - if ( arguments.wrapMode() == JsonQueryWrapMode.WITH_WRAPPER ) { - sqlAppender.appendSql( ")" ); - } - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java deleted file mode 100644 index 1dd82c67b24b..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonRemoveFunction.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.mapping.JdbcMappingContainer; -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_remove function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonSetFunction. - */ -public class GaussDBJsonRemoveFunction extends AbstractJsonRemoveFunction { - - public GaussDBJsonRemoveFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List arguments, - ReturnableType returnType, - SqlAstTranslator translator) { - - final Expression json = (Expression) arguments.get( 0 ); - final Expression jsonPath = (Expression) arguments.get( 1 ); - sqlAppender.appendSql( "json_remove(" ); - json.accept( translator ); - sqlAppender.appendSql( "," ); - jsonPath.accept( translator ); - sqlAppender.appendSql( ")" ); - } - - private boolean isJsonType(Expression expression) { - final JdbcMappingContainer expressionType = expression.getExpressionType(); - return expressionType != null && expressionType.getSingleJdbcMapping().getJdbcType().isJson(); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java deleted file mode 100644 index 11109fbb24a9..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonReplaceFunction.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_replace function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonSetFunction. - */ -public class GaussDBJsonReplaceFunction extends AbstractJsonReplaceFunction { - - public GaussDBJsonReplaceFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List arguments, - ReturnableType returnType, - SqlAstTranslator translator) { - - final Expression json = (Expression) arguments.get( 0 ); - final Expression jsonPath = (Expression) arguments.get( 1 ); - final SqlAstNode value = arguments.get( 2 ); - sqlAppender.appendSql( "json_replace(" ); - json.accept( translator ); - sqlAppender.appendSql( "," ); - jsonPath.accept( translator ); - sqlAppender.appendSql( "," ); - value.accept( translator ); - sqlAppender.appendSql( ")" ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java deleted file mode 100644 index cc481f992309..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonSetFunction.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.List; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.SqlAstNode; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_set function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonSetFunction. - */ -public class GaussDBJsonSetFunction extends AbstractJsonSetFunction { - - public GaussDBJsonSetFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration ); - } - - @Override - public void render( - SqlAppender sqlAppender, - List arguments, - ReturnableType returnType, - SqlAstTranslator translator) { - - final Expression json = (Expression) arguments.get( 0 ); - final Expression jsonPath = (Expression) arguments.get( 1 ); - final SqlAstNode value = arguments.get( 2 ); - sqlAppender.appendSql( "json_set(" ); - json.accept( translator ); - sqlAppender.appendSql( "," ); - jsonPath.accept( translator ); - sqlAppender.appendSql( "," ); - value.accept( translator ); - sqlAppender.appendSql( ")" ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java deleted file mode 100644 index 91e88a603e28..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonValueFunction.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.Iterator; -import java.util.Map; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; -import org.hibernate.type.descriptor.jdbc.JsonHelper; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * GaussDB json_value function. - * - * @author liubao - * - * Notes: Original code of this class is based on PostgreSQLJsonValueFunction. - */ -public class GaussDBJsonValueFunction extends JsonValueFunction { - - - public GaussDBJsonValueFunction(TypeConfiguration typeConfiguration) { - super( typeConfiguration, true, true ); - } - - @Override - protected void render( - SqlAppender sqlAppender, - JsonValueArguments arguments, - ReturnableType returnType, - SqlAstTranslator walker) { - - if ( arguments.returningType() != null ) { - sqlAppender.appendSql( "(" ); - } - arguments.jsonDocument().accept( walker ); - sqlAppender.appendSql( "::json #>> '{" ); - String literalValue = walker.getLiteralValue( arguments.jsonPath() ); - - final JsonPathPassingClause passingClause = arguments.passingClause(); - if ( passingClause != null ) { - final Map passingExpressions = passingClause.getPassingExpressions(); - final Iterator> iterator = passingExpressions.entrySet().iterator(); - Map.Entry entry = iterator.next(); - literalValue = literalValue.replace( "$" + entry.getKey(), - walker.getLiteralValue( entry.getValue() ).toString() ); - while ( iterator.hasNext() ) { - entry = iterator.next(); - sqlAppender.appendSql( ',' ); - literalValue = literalValue.replace( "$" + entry.getKey(), - walker.getLiteralValue( entry.getValue() ).toString() ); - } - } - - sqlAppender.append( JsonHelper.parseJsonPath( literalValue ) ); - sqlAppender.appendSql( "}'" ); - if ( arguments.returningType() != null ) { - sqlAppender.appendSql( ")::" ); - arguments.returningType().accept( walker ); - } - } - - @Override - protected void renderReturningClause(SqlAppender sqlAppender, JsonValueArguments arguments, SqlAstTranslator walker) { - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java index e1433a70ed0b..a3554c13560b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.java @@ -13,14 +13,12 @@ import org.hibernate.orm.test.annotations.collectionelement.ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.Event; import org.hibernate.orm.test.annotations.collectionelement.ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.Plan; import org.hibernate.orm.test.annotations.collectionelement.ElementCollectionOfEmbeddableWithEntityWithEntityCollectionTest.SubPlan; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.jdbc.SQLStatementInspector; import org.hibernate.testing.orm.junit.DomainModel; 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.BeforeEach; import org.junit.jupiter.api.Test; @@ -82,9 +80,6 @@ public void setUp(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, - reason = "type:resolved.If you operate a table with the same name as the system view under the schema, " - + "you will be redirected to the system view and an error will be reported.") public void testInitializeCollection(SessionFactoryScope scope) { final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); statementInspector.clear(); @@ -127,7 +122,8 @@ public void testInitializeCollection(SessionFactoryScope scope) { } @Entity(name = "Plan") - @Table(name = "PLAN_TABLE") + // add a table prefix to avoid conflict with system view + @Table(name = "PLAN_TEST_TABLE") public static class Plan { @Id public Integer id; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java index f5a09b2dba4c..4678d372f113 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/query/QueryAndSQLTest.java @@ -14,7 +14,6 @@ import org.hibernate.Transaction; import org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -561,9 +560,6 @@ public void testNativeQueryAndCompositePKAndComponents(SessionFactoryScope scope } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, - reason = "type:resolved.If you operate a table with the same name as the system view under the schema, " - + "you will be redirected to the system view and an error will be reported.") public void testDiscriminator(SessionFactoryScope scope) { scope.inSession( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java index 9d4def6e6c46..29f4ef580ea1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/PackagedEntityManagerTest.java @@ -11,7 +11,6 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventType; @@ -38,7 +37,6 @@ import org.hibernate.orm.test.jpa.pack.various.Seat; import org.hibernate.stat.Statistics; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.testing.transaction.TransactionUtil; import org.hibernate.testing.util.ServiceRegistryUtil; import org.junit.jupiter.api.AfterEach; @@ -65,7 +63,6 @@ * @author Gavin King * @author Hardy Ferentschik */ -@SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public class PackagedEntityManagerTest extends PackagingTestCase { private EntityManagerFactory emf; @AfterEach From f99f64d0f1c8614cd1cea82ca0cedef3b3b5e54a Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sat, 26 Apr 2025 19:36:56 +0800 Subject: [PATCH 15/33] [HHH-19365]unused skip functions --- .../hibernate/orm/test/bootstrap/scanning/ScannerTest.java | 3 --- .../org/hibernate/orm/test/lob/LobStringFunctionsTest.java | 6 ------ .../java/org/hibernate/orm/test/lob/LongByteArrayTest.java | 4 ---- .../orm/test/locking/paging/PagingAndLockingTest.java | 4 ---- 4 files changed, 17 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java index 5a395f0b1088..1c978909d5fa 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScannerTest.java @@ -33,8 +33,6 @@ import org.hibernate.orm.test.jpa.pack.defaultpar.Version; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.util.ServiceRegistryUtil; -import org.hibernate.testing.orm.junit.SkipForDialect; -import org.hibernate.dialect.GaussDBDialect; import org.junit.jupiter.api.Test; @@ -82,7 +80,6 @@ private void assertClassesContained(ScanResult scanResult, Class classToCheckFor } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testCustomScanner() throws Exception { File defaultPar = buildDefaultPar(); File explicitPar = buildExplicitPar(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java index 507acaa04179..a9f290bdc3d7 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobStringFunctionsTest.java @@ -7,14 +7,12 @@ import java.sql.Clob; import java.util.List; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.DomainModel; 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,7 +67,6 @@ public void tearDown(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class) public void testLengthFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { final Query query = session.createQuery( @@ -89,7 +86,6 @@ public void testLengthFunction(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class) public void testOctetLengthFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { final Query query = session.createQuery( @@ -110,7 +106,6 @@ public void testOctetLengthFunction(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class) public void testBitLengthFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { final Query query = session.createQuery( @@ -131,7 +126,6 @@ public void testBitLengthFunction(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class) public void testConcatFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { // Use trim('') instead of '' since Sybase interprets that as single space string... diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java index 237077c7598f..3c5b445a43d5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LongByteArrayTest.java @@ -6,7 +6,6 @@ import java.util.Arrays; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.testing.orm.junit.SessionFactory; @@ -30,7 +29,6 @@ public abstract class LongByteArrayTest { private static final int ARRAY_SIZE = 10000; @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testBoundedLongByteArrayAccess(SessionFactoryScope scope) { byte[] original = buildRecursively( ARRAY_SIZE, true ); byte[] changed = buildRecursively( ARRAY_SIZE, false ); @@ -81,7 +79,6 @@ public void testBoundedLongByteArrayAccess(SessionFactoryScope scope) { @Test @SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "Sybase returns byte[]{0}") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testEmptyArray(SessionFactoryScope scope) { byte[] empty = new byte[] {}; @@ -105,7 +102,6 @@ public void testEmptyArray(SessionFactoryScope scope) { } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testSaving(SessionFactoryScope scope) { byte[] value = buildRecursively( ARRAY_SIZE, true ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java index 997e66713a13..347bd781e62a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java @@ -10,13 +10,11 @@ import jakarta.persistence.criteria.CriteriaQuery; import org.hibernate.LockMode; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -73,7 +71,6 @@ public void testHql() { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.gauss's bug") public void testCriteria() { inTransaction( s -> { @@ -99,7 +96,6 @@ public void testCriteria() { @Test // @Ignore( "Support for locking on native-sql queries not yet implemented" ) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.not support") public void testNativeSql() { inTransaction( session -> { From cad776f9b4b6e76b6c2a5b423e0aacc988349c03 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Sun, 27 Apr 2025 11:45:36 +0800 Subject: [PATCH 16/33] HHH-19365 - add author --- docker_db.sh | 2 +- .../org/hibernate/dialect/function/GaussDBFormatFunction.java | 2 +- .../function/array/GaussDBArrayContainsOperatorFunction.java | 1 + .../dialect/function/array/GaussDBArrayRemoveFunction.java | 1 + .../function/array/GaussDBArrayRemoveIndexFunction.java | 3 ++- .../dialect/function/array/GaussDBArrayReplaceFunction.java | 1 + .../dialect/function/array/GaussDBArraySetFunction.java | 1 + .../dialect/function/array/GaussDBArrayTrimFunction.java | 1 + .../dialect/function/json/GaussDBJsonExistsFunction.java | 2 ++ .../dialect/function/json/GaussDBJsonObjectFunction.java | 1 + 10 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docker_db.sh b/docker_db.sh index 27c8d45dd9de..d180b71765d2 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -1130,7 +1130,7 @@ if [ -z ${1} ]; then echo -e "\toracle" echo -e "\toracle_23" echo -e "\toracle_21" - echo -e "\tgauss" + echo -e "\tgaussdb" echo -e "\tpostgresql" echo -e "\tpostgresql_17" echo -e "\tpostgresql_16" diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java index 34ddc3301382..8d512dd87604 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java @@ -58,7 +58,7 @@ /** * A format function with support for composite temporal expressions. * - * @author Christian Beikov + * @author chenzhida * * Notes: Original code of this class is based on FormatFunction. * diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java index f93e94efce5c..8a24678c9a73 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayContainsOperatorFunction.java @@ -19,6 +19,7 @@ /** * Special array contains function that also applies a cast to the element argument. PostgreSQL needs this, * because by default it assumes a {@code text[]}, which is not compatible with {@code varchar[]}. + * @author chenzhida * * Notes: Original code of this class is based on ArrayContainsOperatorFunction. */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java index 4882f57dd959..caab9549b219 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java @@ -15,6 +15,7 @@ /** * Gaussdb array_remove function. + * @author chenzhida */ public class GaussDBArrayRemoveFunction extends AbstractArrayRemoveFunction { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java index 60c081d047d3..780428515068 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java @@ -14,7 +14,8 @@ import java.util.List; /** - * Gaussdb array_remove function. + * Gaussdb array_remove index function. + * @author chenzhida */ public class GaussDBArrayRemoveIndexFunction extends ArrayRemoveIndexUnnestFunction { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java index e153e72b57b6..58b1404bf2f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java @@ -15,6 +15,7 @@ /** * Gaussdb array_replace function. + * @author chenzhida */ public class GaussDBArrayReplaceFunction extends ArrayReplaceUnnestFunction { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java index 3daa6399af49..b147de38c238 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java @@ -21,6 +21,7 @@ /** * Gaussdb array_set function. + * @author chenzhida */ public class GaussDBArraySetFunction extends AbstractSqmSelfRenderingFunctionDescriptor { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java index 00b88437c64c..bbc0871bca89 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java @@ -14,6 +14,7 @@ /** * Gaussdb array_trim function. + * @author chenzhida * * Notes: Original code of this class is based on PostgreSQLArrayTrimEmulation. */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java index 4ac6a7ca07f6..0e35be9a0b95 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java @@ -17,6 +17,8 @@ /** * PostgreSQL json_query function. + * @author chenzhida + * */ public class GaussDBJsonExistsFunction extends JsonExistsFunction { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java index cc5a75d2aa12..8e90c620f4d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java @@ -16,6 +16,7 @@ /** * Gaussdb json_object function. + * @author chenzhida * * Notes: Original code of this class is based on PostgreSQLJsonObjectFunction. */ From e811b763bb5c8bcb8964ba1a939090d883cfdc21 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Sun, 27 Apr 2025 15:42:13 +0800 Subject: [PATCH 17/33] HHH-19365 - delete some skip gaussdialect case --- .../dialect/identity/GaussDBIdentityColumnSupport.java | 3 ++- .../bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java | 4 +--- .../bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java | 4 ++-- .../org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java | 2 +- .../org/hibernate/orm/test/jpa/compliance/NamedQueryTest.java | 3 --- .../orm/test/merge/BidirectionalOneToManyMergeTest.java | 2 +- .../org/hibernate/orm/test/query/SubQueryInFromTests.java | 3 --- .../criteria/CriteriaBuilderNonStandardFunctionsTest.java | 1 - .../test/java/org/hibernate/orm/test/type/DateArrayTest.java | 3 --- .../orm/test/version/DetachedEntityWithNullVersionTest.java | 3 --- 10 files changed, 7 insertions(+), 21 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java index fee0f0472c1c..7cadc99ff734 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -4,6 +4,7 @@ */ package org.hibernate.dialect.identity; + /** * @author liubao * @@ -20,6 +21,6 @@ public String getIdentitySelectString(String table, String column, int type) { @Override public String getIdentityColumnString(int type) { - return ""; + return ""; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java index 052f99fa2269..ec5dd5b3d67c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java @@ -20,8 +20,6 @@ import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; -import org.hibernate.testing.orm.junit.SkipForDialect; -import org.hibernate.dialect.GaussDBDialect; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -43,7 +41,7 @@ public class LazyBasicFieldMergeTest { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") +// @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void test(SessionFactoryScope scope) { scope.inTransaction( session -> { Manager manager = new Manager(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java index 97aef0acd867..a068b742063f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java @@ -9,8 +9,8 @@ import java.util.List; import java.util.Set; -import org.hibernate.dialect.GaussDBDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.Jira; @@ -109,7 +109,7 @@ public void testMergeParentWithoutChildren(SessionFactoryScope scope) { @Test @Jira("HHH-18177") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support identity generation") public void testMergeTransientInstanceWithGeneratedId(SessionFactoryScope scope) { Book merged = scope.fromTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java index 9fae651509d0..a542432244a4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java @@ -41,7 +41,7 @@ public class CompositeIdAndMergeTest { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support identity key generation") public void testMerge(SessionFactoryScope scope) { Integer lineItemIndex = 2; Order persistedOrder = scope.fromTransaction( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/NamedQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/NamedQueryTest.java index c60405012b6a..3f0c669dbdba 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/NamedQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/NamedQueryTest.java @@ -9,11 +9,9 @@ import org.hibernate.cfg.AvailableSettings; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Setting; -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; @@ -82,7 +80,6 @@ public void testNameQueryCreationFromCriteria(EntityManagerFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.gauss's bug") public void testNativeWithMaxResults(EntityManagerFactoryScope scope) { scope.inTransaction( entityManager -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java index e781bd02dab3..781f093b83c3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java @@ -48,7 +48,7 @@ public void setUp() { } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support identity key generation") public void testMerge() { doInJPA(this::entityManagerFactory, entityManager -> { Post post = entityManager.find(Post.class, 1L); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java index cd52da779c5b..6755b4d67cab 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/SubQueryInFromTests.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.function.Consumer; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.query.common.JoinType; import org.hibernate.query.criteria.HibernateCriteriaBuilder; @@ -28,7 +27,6 @@ import org.hibernate.testing.orm.junit.DomainModel; 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; @@ -218,7 +216,6 @@ public void testEmbeddedRoot(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsSubqueryInOnClause.class) @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsOrderByInCorrelatedSubquery.class) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resovling.not support") public void testEmbedded(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java index 7ce55c128bba..7b756a443ff2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java @@ -345,7 +345,6 @@ public void testCollatePostgreSQL(SessionFactoryScope scope) { @Test @SkipForDialect(dialectClass = CockroachDialect.class, reason = "Cockroach has unreliable support for numeric types in log function") - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Unsupported function.") public void testLog(SessionFactoryScope scope) { scope.inTransaction( session -> { HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java index a849afb63611..06ef5286d9a1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/DateArrayTest.java @@ -89,7 +89,6 @@ public void startUp(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testById(SessionFactoryScope scope) { scope.inSession( em -> { TableWithDateArrays tableRecord; @@ -108,7 +107,6 @@ public void testById(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testQueryById(SessionFactoryScope scope) { scope.inSession( em -> { TypedQuery tq = em.createNamedQuery( "TableWithDateArrays.JPQL.getById", TableWithDateArrays.class ); @@ -131,7 +129,6 @@ public void testQuery(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "maybe concurrency") public void testNativeQueryById(SessionFactoryScope scope) { scope.inSession( em -> { TypedQuery tq = em.createNamedQuery( "TableWithDateArrays.Native.getById", TableWithDateArrays.class ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java index b8f4aab303a1..4d23ac402df6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/version/DetachedEntityWithNullVersionTest.java @@ -8,7 +8,6 @@ import java.util.UUID; import org.hibernate.PropertyValueException; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.testing.orm.junit.DialectFeatureChecks; @@ -17,7 +16,6 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature; 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.Assertions; import org.junit.jupiter.api.Test; @@ -49,7 +47,6 @@ public class DetachedEntityWithNullVersionTest { private static final String ITEM_UPDATED_NAME = "updated name"; @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void testMergeDetachedEntityWithIdentityId(SessionFactoryScope scope) { IdentityGeneratedIdItem item = new IdentityGeneratedIdItem(); persistItem( scope, item ); From 1074b17e164d17d5269d8fce74b0793749429018 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Sun, 27 Apr 2025 16:54:35 +0800 Subject: [PATCH 18/33] HHH-19365 - format code --- .../dialect/identity/GaussDBIdentityColumnSupport.java | 10 ++++++++-- .../enhancement/lazy/LazyBasicFieldMergeTest.java | 4 +++- .../enhancement/merge/MergeUnsavedEntitiesTest.java | 1 - 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java index 7cadc99ff734..17ad3535085e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -4,6 +4,7 @@ */ package org.hibernate.dialect.identity; +import static org.hibernate.internal.util.StringHelper.unquote; /** * @author liubao @@ -14,13 +15,18 @@ public class GaussDBIdentityColumnSupport extends IdentityColumnSupportImpl { public static final GaussDBIdentityColumnSupport INSTANCE = new GaussDBIdentityColumnSupport(); + @Override + public boolean supportsIdentityColumns() { + return false; + } + @Override public String getIdentitySelectString(String table, String column, int type) { - return ""; + return "select currval('" + unquote(table) + '_' + unquote(column) + "_seq')"; } @Override public String getIdentityColumnString(int type) { - return ""; + return "generated always as identity"; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java index ec5dd5b3d67c..773e3f1c3861 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java @@ -15,6 +15,7 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; @@ -24,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; /** @@ -41,7 +43,7 @@ public class LazyBasicFieldMergeTest { @Test -// @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") public void test(SessionFactoryScope scope) { scope.inTransaction( session -> { Manager manager = new Manager(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java index a068b742063f..5cdda67425b9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.Set; - import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; import org.hibernate.testing.orm.junit.DomainModel; From 30891e2ccd2eb494657af7f783d2a3f0bbf4b50b Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Sun, 27 Apr 2025 17:03:03 +0800 Subject: [PATCH 19/33] HHH-19365 - format code --- .../bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java index 773e3f1c3861..64e96a371060 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java @@ -15,17 +15,17 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; -import org.hibernate.dialect.GaussDBDialect; -import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; + import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.dialect.GaussDBDialect; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; /** From 2aaf7ff3dabace3e6ba22e2d11dd226a6df758ce Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Sun, 27 Apr 2025 17:07:44 +0800 Subject: [PATCH 20/33] HHH-19365 - format code --- .../test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java index 64e96a371060..052f99fa2269 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java @@ -15,7 +15,7 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; - import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; +import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; From f1be602d43a7b3512e3d996461d7a73d1e451d7f Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sun, 27 Apr 2025 18:01:57 +0800 Subject: [PATCH 21/33] [HHH-19365]optimize transaction test skip and remove not used code --- .../AgroalTransactionIsolationConfigTest.java | 2 + .../C3p0TransactionIsolationConfigTest.java | 2 + .../json/GaussDBJsonExistsFunction.java | 67 ------------------- .../HikariTransactionIsolationConfigTest.java | 2 + .../BaseTransactionIsolationConfigTest.java | 6 -- 5 files changed, 6 insertions(+), 73 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java diff --git a/hibernate-agroal/src/test/java/org/hibernate/test/agroal/AgroalTransactionIsolationConfigTest.java b/hibernate-agroal/src/test/java/org/hibernate/test/agroal/AgroalTransactionIsolationConfigTest.java index b95ae6c3cb26..f9c8cdd34184 100644 --- a/hibernate-agroal/src/test/java/org/hibernate/test/agroal/AgroalTransactionIsolationConfigTest.java +++ b/hibernate-agroal/src/test/java/org/hibernate/test/agroal/AgroalTransactionIsolationConfigTest.java @@ -6,6 +6,7 @@ import org.hibernate.community.dialect.AltibaseDialect; import org.hibernate.community.dialect.TiDBDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.agroal.internal.AgroalConnectionProvider; @@ -17,6 +18,7 @@ */ @SkipForDialect(value = TiDBDialect.class, comment = "Doesn't support SERIALIZABLE isolation") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase cannot change isolation level in autocommit mode") +@SkipForDialect(value = GaussDBDialect.class, comment = "GaussDB query serialization level of SERIALIZABLE has some problem") public class AgroalTransactionIsolationConfigTest extends BaseTransactionIsolationConfigTest { @Override protected ConnectionProvider getConnectionProviderUnderTest() { diff --git a/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3p0TransactionIsolationConfigTest.java b/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3p0TransactionIsolationConfigTest.java index 7e002cc81ca5..2dcbe9ed51e8 100644 --- a/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3p0TransactionIsolationConfigTest.java +++ b/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3p0TransactionIsolationConfigTest.java @@ -8,6 +8,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.c3p0.internal.C3P0ConnectionProvider; import org.hibernate.community.dialect.AltibaseDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.community.dialect.TiDBDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; @@ -23,6 +24,7 @@ @SkipForDialect(value = TiDBDialect.class, comment = "Doesn't support SERIALIZABLE isolation") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase cannot change isolation level in autocommit mode") @SkipForDialect(value = SybaseASEDialect.class, comment = "JtdsConnection.isValid not implemented") +@SkipForDialect(value = GaussDBDialect.class, comment = "GaussDB query serialization level of SERIALIZABLE has some problem") public class C3p0TransactionIsolationConfigTest extends BaseTransactionIsolationConfigTest { private StandardServiceRegistry ssr; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java deleted file mode 100644 index 0e35be9a0b95..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonExistsFunction.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.dialect.function.json; - -import java.util.Iterator; -import java.util.Map; - -import org.hibernate.metamodel.model.domain.ReturnableType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; -import org.hibernate.type.spi.TypeConfiguration; - - -/** - * PostgreSQL json_query function. - * @author chenzhida - * - */ -public class GaussDBJsonExistsFunction extends JsonExistsFunction { - - public GaussDBJsonExistsFunction( - TypeConfiguration typeConfiguration, - boolean supportsJsonPathExpression, - boolean supportsJsonPathPassingClause) { - super( typeConfiguration, supportsJsonPathExpression, supportsJsonPathPassingClause ); - } - - @Override - protected void render( - SqlAppender sqlAppender, - JsonExistsArguments arguments, - ReturnableType returnType, - SqlAstTranslator walker) { - - sqlAppender.appendSql( "json_contains_path(" ); - arguments.jsonDocument().accept( walker ); - sqlAppender.appendSql( ",'one', '" ); - - String literalValue = walker.getLiteralValue( arguments.jsonPath() ); - final JsonPathPassingClause passingClause = arguments.passingClause(); - if ( passingClause != null ) { - final Map passingExpressions = passingClause.getPassingExpressions(); - final Iterator> iterator = passingExpressions.entrySet().iterator(); - Map.Entry entry = iterator.next(); - literalValue = literalValue.replace( - "$" + entry.getKey(), - walker.getLiteralValue( entry.getValue() ).toString() - ); - while ( iterator.hasNext() ) { - entry = iterator.next(); - sqlAppender.appendSql( ',' ); - literalValue = literalValue.replace( - "$" + entry.getKey(), - walker.getLiteralValue( entry.getValue() ).toString() - ); - } - } - - sqlAppender.appendSql( literalValue ); - sqlAppender.appendSql( "') = 1" ); - } - -} diff --git a/hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariTransactionIsolationConfigTest.java b/hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariTransactionIsolationConfigTest.java index 49669c70d2a5..a3d152d933e2 100644 --- a/hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariTransactionIsolationConfigTest.java +++ b/hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariTransactionIsolationConfigTest.java @@ -5,6 +5,7 @@ package org.hibernate.test.hikaricp; import org.hibernate.community.dialect.AltibaseDialect; +import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.community.dialect.TiDBDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; @@ -19,6 +20,7 @@ @SkipForDialect(value = SybaseDialect.class, comment = "The jTDS driver doesn't implement Connection#getNetworkTimeout() so this fails") @SkipForDialect(value = TiDBDialect.class, comment = "Doesn't support SERIALIZABLE isolation") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase cannot change isolation level in autocommit mode") +@SkipForDialect(value = GaussDBDialect.class, comment = "GaussDB query serialization level of SERIALIZABLE has some problem") public class HikariTransactionIsolationConfigTest extends BaseTransactionIsolationConfigTest { @Override protected ConnectionProvider getConnectionProviderUnderTest() { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java b/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java index 3afcbeace26a..88d39c68db8a 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/common/connections/BaseTransactionIsolationConfigTest.java @@ -9,7 +9,6 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.service.spi.Configurable; @@ -17,7 +16,6 @@ import org.hibernate.service.spi.Stoppable; import org.hibernate.testing.junit4.BaseUnitTestCase; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -32,7 +30,6 @@ protected void augmentConfigurationSettings(Properties properties) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsNumeric() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); @@ -57,7 +54,6 @@ public void testSettingIsolationAsNumeric() throws Exception { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsNumericString() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); @@ -82,7 +78,6 @@ public void testSettingIsolationAsNumericString() throws Exception { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsName() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); @@ -107,7 +102,6 @@ public void testSettingIsolationAsName() throws Exception { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "Looks like SERIALIZABLE is not supported") public void testSettingIsolationAsNameAlt() throws Exception { Properties properties = Environment.getProperties(); augmentConfigurationSettings( properties ); From f6b8ca91de4c03e4a7d33b351ff8035bf86569ab Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Sun, 27 Apr 2025 18:53:48 +0800 Subject: [PATCH 22/33] HHH-19365 - restore code --- .../java/org/hibernate/dialect/GaussDBDialect.java | 1 + .../identity/GaussDBIdentityColumnSupport.java | 11 ++--------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index fa2691725f07..4210f84566eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -176,6 +176,7 @@ public GaussDBDialect(DatabaseVersion version) { this.optionalTableUpdateStrategy = determineOptionalTableUpdateStrategy( version ); } + @Override public boolean supportsColumnCheck() { return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java index 17ad3535085e..fee0f0472c1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -4,8 +4,6 @@ */ package org.hibernate.dialect.identity; -import static org.hibernate.internal.util.StringHelper.unquote; - /** * @author liubao * @@ -15,18 +13,13 @@ public class GaussDBIdentityColumnSupport extends IdentityColumnSupportImpl { public static final GaussDBIdentityColumnSupport INSTANCE = new GaussDBIdentityColumnSupport(); - @Override - public boolean supportsIdentityColumns() { - return false; - } - @Override public String getIdentitySelectString(String table, String column, int type) { - return "select currval('" + unquote(table) + '_' + unquote(column) + "_seq')"; + return ""; } @Override public String getIdentityColumnString(int type) { - return "generated always as identity"; + return ""; } } From e166acc2bf27d9091fdc7a6223fd46c0f9046a24 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Sun, 27 Apr 2025 20:39:15 +0800 Subject: [PATCH 23/33] [HHH-19365]date type operation --- .../org/hibernate/dialect/GaussDBDialect.java | 37 +++++++------------ .../test/query/hql/StandardFunctionTests.java | 2 - 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java index 4210f84566eb..bac224e9688e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBDialect.java @@ -479,30 +479,19 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT if ( unit == null ) { return "(?3-?2)"; } - if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) { - // special case: subtraction of two dates - // results in an integer number of days - // instead of an INTERVAL - return switch (unit) { - case YEAR, MONTH, QUARTER -> "extract(" + translateDurationField( unit ) + " from age(?3,?2))"; - default -> "(?3-?2)" + DAY.conversionFactor( unit, this ); - }; - } - else { - return switch (unit) { - case YEAR -> "extract(year from ?3-?2)"; - case QUARTER -> "(extract(year from ?3-?2)*4+extract(month from ?3-?2)/3)"; - case MONTH -> "(extract(year from ?3-?2)*12+extract(month from ?3-?2))"; - case WEEK -> "(extract(day from ?3-?2)/7)"; // week is not supported by extract() when the argument is a duration - case DAY -> "extract(day from ?3-?2)"; - // in order to avoid multiple calls to extract(), - // we use extract(epoch from x - y) * factor for - // all the following units: - case HOUR, MINUTE, SECOND, NANOSECOND, NATIVE -> - "extract(epoch from ?3-?2)" + EPOCH.conversionFactor( unit, this ); - default -> throw new SemanticException( "Unrecognized field: " + unit ); - }; - } + return switch (unit) { + case YEAR -> "extract(year from ?3-?2)"; + case QUARTER -> "(extract(year from ?3-?2)*4+extract(month from ?3-?2)/3)"; + case MONTH -> "(extract(year from ?3-?2)*12+extract(month from ?3-?2))"; + case WEEK -> "(extract(day from ?3-?2)/7)"; // week is not supported by extract() when the argument is a duration + case DAY -> "extract(day from ?3-?2)"; + // in order to avoid multiple calls to extract(), + // we use extract(epoch from x - y) * factor for + // all the following units: + case HOUR, MINUTE, SECOND, NANOSECOND, NATIVE -> + "extract(epoch from ?3-?2)" + EPOCH.conversionFactor( unit, this ); + default -> throw new SemanticException( "Unrecognized field: " + unit ); + }; } @Override 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 6b3ad9c1d350..2bec6419a9b7 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 @@ -16,7 +16,6 @@ import org.hibernate.dialect.CockroachDialect; import org.hamcrest.number.IsCloseTo; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; import org.hibernate.testing.orm.junit.DialectFeatureChecks; @@ -517,7 +516,6 @@ public void testIntervalScaleExpressions(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.Bad value for type long : 00:00:00") public void testIntervalDiffExpressions(SessionFactoryScope scope) { scope.inTransaction( session -> { From 3e7585ac97ba280f3df2bf1c9704e13d34389f26 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Mon, 28 Apr 2025 09:07:15 +0800 Subject: [PATCH 24/33] [HHH-19365]like default escape adapt --- .../java/org/hibernate/dialect/GaussDBSqlAstTranslator.java | 2 ++ .../hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java index 277a65a2fe6a..6ffa086e7680 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java @@ -289,6 +289,8 @@ public void visitLikePredicate(LikePredicate likePredicate) { if ( likePredicate.getEscapeCharacter() != null ) { appendSql( " escape " ); likePredicate.getEscapeCharacter().accept( this ); + } else { + appendSql( " escape ''''" ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java index 6ee74d2c8ff5..f0bb2c58d37c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LikeEscapeDefaultTest.java @@ -6,7 +6,6 @@ import java.util.List; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.Query; import org.hibernate.testing.orm.domain.StandardDomainModel; @@ -15,7 +14,6 @@ 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; @@ -53,7 +51,6 @@ public void tearDown(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss's has different behavior") public void testDefaultEscapeBackslash(SessionFactoryScope scope) { scope.inTransaction( session -> { Query q = session.createQuery( @@ -67,7 +64,6 @@ public void testDefaultEscapeBackslash(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss's has different behavior") public void testDefaultEscapeBackslashLiteral(SessionFactoryScope scope) { scope.inTransaction( session -> { Query q = session.createQuery( @@ -81,7 +77,6 @@ public void testDefaultEscapeBackslashLiteral(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss's has different behavior") public void testDefaultEscapeNoResults(SessionFactoryScope scope) { scope.inTransaction( session -> { Query q = session.createQuery( From a823e3fbf702d7636e27fdb25ad49ef115926871 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Mon, 28 Apr 2025 11:23:46 +0800 Subject: [PATCH 25/33] [HHH-19365]identity column support --- .../identity/GaussDBIdentityColumnSupport.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java index fee0f0472c1c..175f7d8a2280 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GaussDBIdentityColumnSupport.java @@ -4,6 +4,8 @@ */ package org.hibernate.dialect.identity; +import static org.hibernate.internal.util.StringHelper.unquote; + /** * @author liubao * @@ -13,13 +15,23 @@ public class GaussDBIdentityColumnSupport extends IdentityColumnSupportImpl { public static final GaussDBIdentityColumnSupport INSTANCE = new GaussDBIdentityColumnSupport(); + @Override + public boolean supportsIdentityColumns() { + return true; + } + + @Override + public boolean hasDataTypeInIdentityColumn() { + return false; + } + @Override public String getIdentitySelectString(String table, String column, int type) { - return ""; + return "select currval('" + unquote(table) + '_' + unquote(column) + "_seq')"; } @Override public String getIdentityColumnString(int type) { - return ""; + return "bigserial"; } } From d6896174311d9d3a562ac9532fa153f4463277d8 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Mon, 28 Apr 2025 11:57:37 +0800 Subject: [PATCH 26/33] [HHH-19365]integer division test --- .../java/org/hibernate/dialect/GaussDBSqlAstTranslator.java | 3 +++ .../org/hibernate/orm/test/query/hql/IntegerDivisionTest.java | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java index 6ffa086e7680..e0921f1ab475 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java @@ -296,6 +296,9 @@ public void visitLikePredicate(LikePredicate likePredicate) { @Override public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + if ( isIntegerDivisionEmulationRequired( arithmeticExpression ) ) { + appendSql( "floor" ); + } appendSql( OPEN_PARENTHESIS ); visitArithmeticOperand( arithmeticExpression.getLeftHandOperand() ); appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java index 44c9bda2bb79..8a13b75952b6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java @@ -4,13 +4,11 @@ */ package org.hibernate.orm.test.query.hql; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.DomainModel; 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.Setting; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; import static org.hibernate.cfg.QuerySettings.PORTABLE_INTEGER_DIVISION; @@ -22,7 +20,6 @@ @ServiceRegistry(settings = @Setting(name = PORTABLE_INTEGER_DIVISION, value = "true")) public class IntegerDivisionTest { @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.gauss has different behavior") public void testIntegerDivision(SessionFactoryScope scope) { scope.inTransaction(s -> { assertFalse( s.createQuery("select 1 where 1/2 = 0 and 4/3 = 1", Integer.class) From 12d09d3e12ff5bcec477a37271d559721f4f2fd6 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Mon, 28 Apr 2025 15:50:48 +0800 Subject: [PATCH 27/33] [HHH-19365]on duplicate key update do nothing --- .../dialect/GaussDBSqlAstTranslator.java | 2 +- .../sql/ast/spi/AbstractSqlAstTranslator.java | 37 +++++++++++++++++++ .../test/query/hql/InsertConflictTests.java | 19 ++++++---- ...flictWithCriteriaCopyTreeEnabledTests.java | 3 -- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java index e0921f1ab475..ed83b80d8704 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/GaussDBSqlAstTranslator.java @@ -114,7 +114,7 @@ protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) { @Override protected void visitConflictClause(ConflictClause conflictClause) { - visitStandardConflictClause( conflictClause ); + visitOnDuplicateKeyConflictClauseWithDoNothing( conflictClause ); } @Override 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 abc5089df9eb..2d458e326976 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 @@ -2051,6 +2051,43 @@ protected void visitOnDuplicateKeyConflictClause(ConflictClause conflictClause) clauseStack.pop(); } + protected void visitOnDuplicateKeyConflictClauseWithDoNothing(ConflictClause conflictClause) { + if ( conflictClause == null ) { + return; + } + // The duplicate key clause does not support specifying the constraint name or constraint column names, + // but to allow compatibility, we have to require the user to specify either one in the SQM conflict clause. + // To allow meaningful usage, we simply ignore the constraint column names in this emulation. + // A possible problem with this is when the constraint column names contain the primary key columns, + // but the insert fails due to a unique constraint violation. This emulation will not cause a failure to be + // propagated, but instead will run the respective conflict action. + final String constraintName = conflictClause.getConstraintName(); + if ( constraintName != null ) { + if ( conflictClause.isDoUpdate() ) { + throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" ); + } + else { + return; + } + } + clauseStack.push( Clause.CONFLICT ); + appendSql( " on duplicate key update" ); + final List assignments = conflictClause.getAssignments(); + if ( assignments.isEmpty() ) { + try { + clauseStack.push( Clause.SET ); + appendSql( " nothing " ); + } + finally { + clauseStack.pop(); + } + } + else { + renderPredicatedSetAssignments( assignments, conflictClause.getPredicate() ); + } + clauseStack.pop(); + } + private void renderPredicatedSetAssignments(List assignments, Predicate predicate) { char separator = ' '; try { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java index 051c34595348..d4f453c37052 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictTests.java @@ -61,7 +61,6 @@ public void cleanupData(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict do\"") public void testOnConflictDoNothing(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -86,7 +85,6 @@ public void testOnConflictDoNothing(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(\"") public void testOnConflictDoUpdate(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -114,7 +112,6 @@ public void testOnConflictDoUpdate(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(") public void testOnConflictDoUpdateWithWhere(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -134,6 +131,10 @@ else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof Sy // Sybase seems to report all matched rows as affected and ignores additional predicates assertEquals( 1, updated ); } + else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof GaussDBDialect ) { + // GaussDB seems to report all matched rows as affected and ignores additional predicates + assertEquals( 1, updated ); + } else { assertEquals( 0, updated ); } @@ -145,7 +146,6 @@ else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof Sy @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(") public void testOnConflictDoUpdateWithWhereCriteria(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -171,6 +171,10 @@ else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof Sy // Sybase seems to report all matched rows as affected and ignores additional predicates assertEquals( 1, updated ); } + else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof GaussDBDialect ) { + // GaussDB seems to report all matched rows as affected and ignores additional predicates + assertEquals( 1, updated ); + } else { assertEquals( 0, updated ); } @@ -181,7 +185,6 @@ else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof Sy } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict do\"") public void testOnConflictDoNothingMultiTable(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -209,7 +212,6 @@ public void testOnConflictDoNothingMultiTable(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) @SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "MERGE into a table that has a self-referential FK does not work") - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolving.syntax error at or near \"conflict(") public void testOnConflictDoUpdateMultiTable(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -238,7 +240,6 @@ public void testOnConflictDoUpdateMultiTable(SessionFactoryScope scope) { @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUpsertOrMerge.class) @SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "MERGE into a table that has a self-referential FK does not work") - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "syntax error at or near \"conflict(\"") public void testOnConflictDoUpdateWithWhereMultiTable(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -258,6 +259,10 @@ else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof Sy // Sybase seems to report all matched rows as affected and ignores additional predicates assertEquals( 1, updated ); } + else if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof GaussDBDialect ) { + // GaussDB seems to report all matched rows as affected and ignores additional predicates + assertEquals( 1, updated ); + } else { assertEquals( 0, updated ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java index 9442257a9996..5df94e23a97d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java @@ -8,7 +8,6 @@ import jakarta.persistence.Id; import jakarta.persistence.Tuple; import org.hibernate.cfg.QuerySettings; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsertSelect; import org.hibernate.query.criteria.JpaCriteriaInsertValues; @@ -19,7 +18,6 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.Test; @@ -34,7 +32,6 @@ ) @SessionFactory @JiraKey("HHH-19314") -@SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resovling.not support") public class InsertConflictWithCriteriaCopyTreeEnabledTests { @Test From 3920d37c88c81e34d8367f0d915c2326a36e98ce Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 28 Apr 2025 15:12:39 +0800 Subject: [PATCH 28/33] HHH-19365 - support identity and containsExactlyInAnyOrder --- .../org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java | 3 --- .../hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java | 2 +- .../orm/test/merge/BidirectionalOneToManyMergeTest.java | 3 --- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java index a542432244a4..726894fbb351 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java @@ -7,13 +7,11 @@ import java.util.ArrayList; import java.util.List; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; 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.Test; import jakarta.persistence.CascadeType; @@ -41,7 +39,6 @@ public class CompositeIdAndMergeTest { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support identity key generation") public void testMerge(SessionFactoryScope scope) { Integer lineItemIndex = 2; Order persistedOrder = scope.fromTransaction( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java index 213db39a8b57..67c9bbcf5f32 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java @@ -113,7 +113,7 @@ private void executeQuery(SessionFactoryScope scope, boolean criteria, boolean l final List resultList = query.setHint( HINT_SPEC_FETCH_GRAPH, entityGraph ).getResultList(); assertThat( resultList ).hasSize( 2 ); // No order by, there is no guarantee of the data order. - assertThat( resultList.stream().map( p -> p.getAddress().getId() ) ).contains( 1L, 2L ); + assertThat( resultList.stream().map( p -> p.getAddress().getId() ) ).containsExactlyInAnyOrder( 1L, 2L ); inspector.assertExecutedCount( 1 ); inspector.assertNumberOfOccurrenceInQuery( 0, "join", where ? 2 : 1 ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java index 781f093b83c3..97bb31d15c2d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/BidirectionalOneToManyMergeTest.java @@ -5,8 +5,6 @@ package org.hibernate.orm.test.merge; import org.hibernate.testing.orm.junit.JiraKey; -import org.hibernate.testing.orm.junit.SkipForDialect; -import org.hibernate.dialect.GaussDBDialect; import org.junit.Before; import org.junit.Test; @@ -48,7 +46,6 @@ public void setUp() { } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support identity key generation") public void testMerge() { doInJPA(this::entityManagerFactory, entityManager -> { Post post = entityManager.find(Post.class, 1L); From 761b7af3fe97fcba5dff2b77e17f50989514ba7c Mon Sep 17 00:00:00 2001 From: liubao68 Date: Mon, 28 Apr 2025 16:06:48 +0800 Subject: [PATCH 29/33] [HHH-19365]fix comments and remove unused skip --- .../hibernate/orm/test/mapping/basic/XmlMappingTests.java | 6 +++--- .../criteria/CriteriaBuilderNonStandardFunctionsTest.java | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java index 51708aa645f1..263f79d45209 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/XmlMappingTests.java @@ -100,7 +100,7 @@ public void tearDown(SessionFactoryScope scope) { } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB don't support this xml feature") public void verifyMappings(SessionFactoryScope scope) { final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory() .getRuntimeMetamodels() @@ -125,7 +125,7 @@ public void verifyMappings(SessionFactoryScope scope) { } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB don't support this xml feature") public void verifyReadWorks(SessionFactoryScope scope) { scope.inTransaction( (session) -> { @@ -143,7 +143,7 @@ public void verifyReadWorks(SessionFactoryScope scope) { @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase doesn't support comparing LOBs with the = operator") @SkipForDialect(dialectClass = OracleDialect.class, matchSubTypes = true, reason = "Oracle doesn't support comparing JSON with the = operator") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase doesn't support comparing CLOBs with the = operator") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB doesn't support comparing CLOBs with the = operator") public void verifyComparisonWorks(SessionFactoryScope scope) { scope.inTransaction( (session) -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java index 7b756a443ff2..362a17ae9960 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaBuilderNonStandardFunctionsTest.java @@ -17,7 +17,6 @@ import jakarta.persistence.criteria.ParameterExpression; import org.hibernate.dialect.CockroachDialect; import org.hibernate.community.dialect.DerbyDialect; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaExpression; @@ -418,7 +417,6 @@ public void testAtan2(SessionFactoryScope scope) { } @Test - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.Function sinh(double precision) does not exist.") public void testHyperbolic(SessionFactoryScope scope) { scope.inTransaction( session -> { HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); From 78fbe5e8af72e72ec456a095751c009de0ef732c Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 28 Apr 2025 17:12:08 +0800 Subject: [PATCH 30/33] HHH-19365 - delete unused skip --- .../bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java | 3 --- .../hibernate/orm/test/hql/bitwise/BitwiseFunctionsTest.java | 2 -- .../hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java | 3 --- .../test/java/org/hibernate/orm/test/jpa/query/QueryTest.java | 2 -- .../jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java | 2 +- .../jpa/schemagen/SchemaScriptFileGenerationFailureTest.java | 2 +- 6 files changed, 2 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java index 5cdda67425b9..11c92368019b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/merge/MergeUnsavedEntitiesTest.java @@ -9,14 +9,12 @@ import java.util.List; import java.util.Set; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.JiraKey; 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.Test; import jakarta.persistence.Entity; @@ -108,7 +106,6 @@ public void testMergeParentWithoutChildren(SessionFactoryScope scope) { @Test @Jira("HHH-18177") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support identity generation") public void testMergeTransientInstanceWithGeneratedId(SessionFactoryScope scope) { Book merged = scope.fromTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/bitwise/BitwiseFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/bitwise/BitwiseFunctionsTest.java index 1994bd2ccb95..acfff3049c0b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/bitwise/BitwiseFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/bitwise/BitwiseFunctionsTest.java @@ -5,7 +5,6 @@ package org.hibernate.orm.test.hql.bitwise; import org.hibernate.community.dialect.DerbyDialect; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -22,7 +21,6 @@ public class BitwiseFunctionsTest { @Test @SkipForDialect(dialectClass = DerbyDialect.class) - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "not support") public void test(SessionFactoryScope scope) { IntEntity five = new IntEntity(); five.setIntValue(5); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java index c9fefa608297..4862c0032817 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/MutableNaturalIdTest.java @@ -5,7 +5,6 @@ package org.hibernate.orm.test.jpa.naturalid; import org.hibernate.community.dialect.AltibaseDialect; -import org.hibernate.dialect.GaussDBDialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.OracleDialect; @@ -27,8 +26,6 @@ reason = "Hana do not support identity key generation") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase do not support identity key generation") -@SkipForDialect(dialectClass = GaussDBDialect.class, - reason = "Gauss do not support identity key generation") public class MutableNaturalIdTest extends AbstractJPATest { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java index 17e0ee5b030a..04ee8ebdb369 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java @@ -579,7 +579,6 @@ public void testQueryContainsQuotedSemicolonWithLimit() { @Test @JiraKey("HHH-18033") - @SkipForDialect(value = GaussDBDialect.class, comment = "Doesn't support semicolon as ending of statement") public void testNativeQueryContainsQuotedSemicolonWithLimit() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -606,7 +605,6 @@ public void testNativeQueryContainsQuotedSemicolonWithLimit() { @SkipForDialect(value = SybaseDialect.class, comment = "Doesn't support semicolon as ending of statement") @SkipForDialect(value = DerbyDialect.class, comment = "Doesn't support semicolon as ending of statement") @SkipForDialect(value = DB2Dialect.class, comment = "Doesn't support semicolon as ending of statement") - @SkipForDialect(value = GaussDBDialect.class, comment = "Doesn't support semicolon as ending of statement") public void testNativeQueryContainsQuotedSemicolonAndEndsWithSemicolonWithLimit() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java index 7d6162254dd8..9f5452fd9e3f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaDatabaseFileGenerationFailureTest.java @@ -75,7 +75,7 @@ public void destroy() { @JiraKey(value = "HHH-12192") @SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "on postgres we send 'set client_min_messages = WARNING'") - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.on gauss we send 'set client_min_messages = WARNING'") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "on gauss we send 'set client_min_messages = WARNING'") public void testErrorMessageContainsTheFailingDDLCommand() { try { entityManagerFactoryBuilder.generateSchema(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java index 517176df1004..7803e6f740c5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/SchemaScriptFileGenerationFailureTest.java @@ -66,7 +66,7 @@ public void destroy() { @JiraKey(value = "HHH-12192") @SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "on postgres we send 'set client_min_messages = WARNING'") - @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "type:resolved.on gauss we send 'set client_min_messages = WARNING'") + @SkipForDialect( dialectClass = GaussDBDialect.class, reason = "on gauss we send 'set client_min_messages = WARNING'") public void testErrorMessageContainsTheFailingDDLCommand() { try { entityManagerFactoryBuilder.generateSchema(); From 92bee758872bde4e57754099dbc1940db20b21ab Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 28 Apr 2025 16:02:37 +0800 Subject: [PATCH 31/33] HHH-19365 - delete LessThanPredicate.java --- .../function/GaussDBFormatFunction.java | 8 ++-- .../org/hibernate/sql/ast/SqlAstWalker.java | 2 - .../sql/ast/spi/AbstractSqlAstTranslator.java | 9 ---- .../sql/ast/spi/AbstractSqlAstWalker.java | 6 --- .../ast/spi/ExpressionReplacementWalker.java | 7 ---- .../ast/tree/predicate/LessThanPredicate.java | 41 ------------------- 6 files changed, 5 insertions(+), 68 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java index 8d512dd87604..34d6e2b1902f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/GaussDBFormatFunction.java @@ -33,8 +33,8 @@ import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTupleContainer; +import org.hibernate.sql.ast.tree.predicate.BetweenPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; -import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.type.BasicType; import org.hibernate.type.SqlTypes; import org.hibernate.type.StandardBasicTypes; @@ -481,8 +481,9 @@ private Expression createFullOffset( final CaseSearchedExpression minuteStart = new CaseSearchedExpression( stringType ); minuteStart.getWhenFragments().add( new CaseSearchedExpression.WhenFragment( - new LessThanPredicate( + new BetweenPredicate( minutes, + new QueryLiteral<>( Integer.MIN_VALUE, integerType ), new QueryLiteral<>( 10, integerType ), false, null @@ -546,8 +547,9 @@ private Expression createMediumOffset( final CaseSearchedExpression minuteStart = new CaseSearchedExpression( stringType ); minuteStart.getWhenFragments().add( new CaseSearchedExpression.WhenFragment( - new LessThanPredicate( + new BetweenPredicate( minutes, + new QueryLiteral<>( Integer.MIN_VALUE, integerType ), new QueryLiteral<>( 10, integerType ), false, null diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java index c63bab9e1309..c596ea03eac7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java @@ -59,7 +59,6 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; -import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -188,7 +187,6 @@ public interface SqlAstWalker { void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate); void visitBetweenPredicate(BetweenPredicate betweenPredicate); - void visitLessThanPredicate(LessThanPredicate lessThanPredicate); void visitFilterPredicate(FilterPredicate filterPredicate); void visitFilterFragmentPredicate(FilterPredicate.FilterFragmentPredicate fragmentPredicate); 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 2d458e326976..7f50687262fc 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 @@ -172,7 +172,6 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; -import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -7583,14 +7582,6 @@ public void visitBetweenPredicate(BetweenPredicate betweenPredicate) { betweenPredicate.getUpperBound().accept( this ); } - @Override - public void visitLessThanPredicate(LessThanPredicate lessThanPredicate) { - - lessThanPredicate.getExpression().accept( this ); - appendSql( " < " ); - lessThanPredicate.getUpperBound().accept( this ); - } - @Override public void visitFilterPredicate(FilterPredicate filterPredicate) { // visits each fragment with " and " between them diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java index 28311b3a0aae..bb106f047331 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java @@ -65,7 +65,6 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; -import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -605,9 +604,4 @@ public void visitCustomTableDelete(TableDeleteCustomSql tableDelete) { throw new UnsupportedOperationException(); } - @Override - public void visitLessThanPredicate(LessThanPredicate lessThanPredicate) { - - throw new UnsupportedOperationException(); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java index 72993c01e08c..49572a3f1870 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ExpressionReplacementWalker.java @@ -63,7 +63,6 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; -import org.hibernate.sql.ast.tree.predicate.LessThanPredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; @@ -651,10 +650,4 @@ public void visitCustomTableDelete(TableDeleteCustomSql tableDelete) { public void visitColumnWriteFragment(ColumnWriteFragment columnWriteFragment) { throw new UnsupportedOperationException(); } - - @Override - public void visitLessThanPredicate(LessThanPredicate lessThanPredicate) { - - throw new UnsupportedOperationException(); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java deleted file mode 100644 index cb243abbd2ea..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/LessThanPredicate.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.sql.ast.tree.predicate; - -import org.hibernate.metamodel.mapping.JdbcMappingContainer; -import org.hibernate.sql.ast.SqlAstWalker; -import org.hibernate.sql.ast.tree.expression.Expression; - -/** - * @author Steve Ebersole - */ -public class LessThanPredicate extends AbstractPredicate { - - private final Expression expression; - private final Expression upperBound; - - public LessThanPredicate( - Expression expression, - Expression upperBound, - boolean negated, - JdbcMappingContainer expressionType) { - super( expressionType, negated ); - this.expression = expression; - this.upperBound = upperBound; - } - - public Expression getExpression() { - return expression; - } - - public Expression getUpperBound() { - return upperBound; - } - - @Override - public void accept(SqlAstWalker sqlTreeWalker) { - sqlTreeWalker.visitLessThanPredicate( this ); - } -} From c94ad9355275f63f0477c0813c7aeb4f6aa9facd Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 28 Apr 2025 17:49:14 +0800 Subject: [PATCH 32/33] HHH-19365 - canonical name --- .../dialect/function/array/GaussDBArrayRemoveFunction.java | 2 +- .../dialect/function/array/GaussDBArrayRemoveIndexFunction.java | 2 +- .../dialect/function/array/GaussDBArrayReplaceFunction.java | 2 +- .../dialect/function/array/GaussDBArraySetFunction.java | 2 +- .../dialect/function/array/GaussDBArrayTrimFunction.java | 2 +- .../dialect/function/json/GaussDBJsonObjectFunction.java | 2 +- .../test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java | 2 +- .../lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java | 2 +- .../test/jpa/query/NativeQueryResultTypeAutoDiscoveryTest.java | 2 +- .../org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java | 2 +- .../hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java | 2 +- .../orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java | 2 +- .../java/org/hibernate/orm/test/query/hql/FunctionTests.java | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java index caab9549b219..1ed86b03cdcf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveFunction.java @@ -14,7 +14,7 @@ import java.util.List; /** - * Gaussdb array_remove function. + * GaussDB array_remove function. * @author chenzhida */ public class GaussDBArrayRemoveFunction extends AbstractArrayRemoveFunction { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java index 780428515068..375b2aaf3ca9 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayRemoveIndexFunction.java @@ -14,7 +14,7 @@ import java.util.List; /** - * Gaussdb array_remove index function. + * GaussDB array_remove index function. * @author chenzhida */ public class GaussDBArrayRemoveIndexFunction extends ArrayRemoveIndexUnnestFunction { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java index 58b1404bf2f4..f48540f8b81b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayReplaceFunction.java @@ -14,7 +14,7 @@ import java.util.List; /** - * Gaussdb array_replace function. + * GaussDB array_replace function. * @author chenzhida */ public class GaussDBArrayReplaceFunction extends ArrayReplaceUnnestFunction { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java index b147de38c238..9b4dfddddce4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArraySetFunction.java @@ -20,7 +20,7 @@ import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; /** - * Gaussdb array_set function. + * GaussDB array_set function. * @author chenzhida */ public class GaussDBArraySetFunction extends AbstractSqmSelfRenderingFunctionDescriptor { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java index bbc0871bca89..24ca596e3d9f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/GaussDBArrayTrimFunction.java @@ -13,7 +13,7 @@ import java.util.List; /** - * Gaussdb array_trim function. + * GaussDB array_trim function. * @author chenzhida * * Notes: Original code of this class is based on PostgreSQLArrayTrimEmulation. diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java index 8e90c620f4d4..3d0fe3a85afa 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/json/GaussDBJsonObjectFunction.java @@ -15,7 +15,7 @@ import java.util.List; /** - * Gaussdb json_object function. + * GaussDB json_object function. * @author chenzhida * * Notes: Original code of this class is based on PostgreSQLJsonObjectFunction. diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java index 052f99fa2269..74d919893e43 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyBasicFieldMergeTest.java @@ -43,7 +43,7 @@ public class LazyBasicFieldMergeTest { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB does not support byte array operations through lob type") public void test(SessionFactoryScope scope) { scope.inTransaction( session -> { Manager manager = new Manager(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java index f776f3c67a21..f7cfa442f134 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyInitializationWithoutInlineDirtyTrackingTest.java @@ -40,7 +40,7 @@ public class LazyInitializationWithoutInlineDirtyTrackingTest { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB does not support byte array operations through lob type") public void test(SessionFactoryScope scope) { scope.inTransaction( s -> { File file = new File(); 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 08f94d6d7061..954d9f0a964d 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 @@ -299,7 +299,7 @@ public void lobTypes() { @SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "EDB maps DATE and TIME to TIMESTAMP") @SkipForDialect(dialectClass = SybaseDialect.class, reason = "Sybase maps DATE and TIME to TIMESTAMP", matchSubTypes = true) @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase maps DATE and TIME to TIMESTAMP") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.Gaussdb's Oracle model maps DATE and TIME to TIMESTAMP") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "type:resolved.GaussDB's Oracle model 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/basic/BlobByteArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java index 539b0760dd78..473ce9e5bb7d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/BlobByteArrayTest.java @@ -30,7 +30,7 @@ protected Class[] getAnnotatedClasses() { } @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB does not support byte array operations through lob type") public void test() { Integer productId = doInJPA(this::entityManagerFactory, entityManager -> { final Product product = new Product(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java index 35193d76e9c6..2c1dbbcf2356 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ByteArrayMappingTests.java @@ -49,7 +49,7 @@ public class ByteArrayMappingTests { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB does not support byte array operations through lob type") public void verifyMappings(SessionFactoryScope scope) { final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory() .getRuntimeMetamodels() diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java index be2761e9e709..52ea6dcbe25a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/WrapperArrayHandlingLegacyTests.java @@ -49,7 +49,7 @@ public class WrapperArrayHandlingLegacyTests { @Test - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "opengauss don't support") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB does not support byte array operations through lob type") public void verifyByteArrayMappings(SessionFactoryScope scope) { final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory() .getRuntimeMetamodels() 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 22fea36d5881..ab30a3196799 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 @@ -1170,7 +1170,7 @@ public void testCastFunctionWithLength(SessionFactoryScope scope) { @SkipForDialect(dialectClass = DB2Dialect.class, majorVersion = 10, minorVersion = 5, reason = "On this version the length of the cast to the parameter appears to be > 2") @SkipForDialect( dialectClass = AltibaseDialect.class, reason = "Altibase cast to raw does not do truncatation") @SkipForDialect(dialectClass = HSQLDialect.class, reason = "HSQL interprets string as hex literal and produces error") - @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "Gaussdb bytea doesn't have a length") + @SkipForDialect(dialectClass = GaussDBDialect.class, reason = "GaussDB bytea doesn't have a length") public void testCastBinaryWithLength(SessionFactoryScope scope) { scope.inTransaction( session -> { From 280aecae5e8c10629756434fdf0179b8402ccd48 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Mon, 28 Apr 2025 19:29:24 +0800 Subject: [PATCH 33/33] [HHH-19365]fix indention (#29) --- .../org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java | 1 - .../hibernate/orm/test/cid/CompositeIdAndMergeTest.java | 1 - .../test/stateless/StatelessSessionVersioningTest.java | 3 +-- .../src/main/groovy/local.databases.gradle | 8 ++++---- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java index bb106f047331..ceefcf50c63d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java @@ -603,5 +603,4 @@ public void visitStandardTableDelete(TableDeleteStandard tableDelete) { public void visitCustomTableDelete(TableDeleteCustomSql tableDelete) { throw new UnsupportedOperationException(); } - } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java index 726894fbb351..ef62a5bef7ea 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/CompositeIdAndMergeTest.java @@ -7,7 +7,6 @@ import java.util.ArrayList; import java.util.List; - import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java index ed05a5092eca..bb8491a1f32f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionVersioningTest.java @@ -28,8 +28,7 @@ StatelessSessionVersioningTest.UUIDVersioned.class}) @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) public class StatelessSessionVersioningTest { - @Test - void testIdentity(SessionFactoryScope scope) { + @Test void testIdentity(SessionFactoryScope scope) { Dialect dialect = scope.getMetadataImplementor().getDatabase().getDialect(); scope.inStatelessTransaction(s -> { IdentityVersioned v = new IdentityVersioned(); diff --git a/local-build-plugins/src/main/groovy/local.databases.gradle b/local-build-plugins/src/main/groovy/local.databases.gradle index 70b27d437cea..f78dc26ede60 100644 --- a/local-build-plugins/src/main/groovy/local.databases.gradle +++ b/local-build-plugins/src/main/groovy/local.databases.gradle @@ -75,11 +75,11 @@ ext { // 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', 'connection.init_sql' : '' ], - gaussdb : [ + gaussdb: [ 'db.dialect' : 'org.hibernate.dialect.GaussDBDialect', 'jdbc.driver' : 'com.huawei.gaussdb.jdbc.Driver', - 'jdbc.user' : 'hibernate_orm_test', - 'jdbc.pass' : 'Hibernate_orm_test@1234', + 'jdbc.user' : 'hibernate_orm_test', + 'jdbc.pass' : 'Hibernate_orm_test@1234', // Disable prepared statement caching to avoid issues with changing schemas // Make batch verification work, see https://bbs.huaweicloud.com/forum/thread-02104174303512776081-1-1.html 'jdbc.url' : 'jdbc:gaussdb://' + dbHost + '/hibernate_orm_test?currentSchema=test&preparedStatementCacheQueries=0&batchMode=off', @@ -386,4 +386,4 @@ ext { 'jdbc.datasource' : 'Altibase.jdbc.driver.AltibaseDriver' ], ] -} \ No newline at end of file +}