From e0615b13a89fbcfb96e96080357d97ecf2a8f83b Mon Sep 17 00:00:00 2001 From: Karel Maesen Date: Fri, 27 May 2022 16:22:06 +0200 Subject: [PATCH] HHH-15294 cockroachdb fixes * HHH-15294 Fix setting of untyped null values in CockroachDB dialect Solution exactly same as in PostgresqlDialect * HHH-15294 Change CockroachDB multitable mutation strategies to CTE Align with the Postgresql strategy * HHH-15294 Fix missing IdentityColumnSupport in CockroachDB Dialect * HHH-15294 Skip test for CockRoachDB due to unsupported automatic type conversion * HHH-15294 CockroachDB doesn't support value propagation --- .../hibernate/dialect/CockroachDialect.java | 59 ++++++++++++++++++- ... => CockroachDBIdentityColumnSupport.java} | 2 +- .../annotations/lob/MaterializedBlobTest.java | 5 ++ .../orm/test/hql/BulkManipulationTest.java | 6 +- 4 files changed, 69 insertions(+), 3 deletions(-) rename hibernate-core/src/main/java/org/hibernate/dialect/identity/{CockroachDB1920IdentityColumnSupport.java => CockroachDBIdentityColumnSupport.java} (91%) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index d5640a29b8..e3cac022cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -8,12 +8,14 @@ package org.hibernate.dialect; import java.sql.DatabaseMetaData; import java.sql.SQLException; +import java.sql.Types; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.Map; import java.util.TimeZone; + import jakarta.persistence.TemporalType; import org.hibernate.LockMode; @@ -21,6 +23,8 @@ import org.hibernate.LockOptions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.FormatFunction; +import org.hibernate.dialect.identity.CockroachDBIdentityColumnSupport; +import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; import org.hibernate.dialect.sequence.PostgreSQLSequenceSupport; @@ -31,10 +35,16 @@ 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.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.NullOrdering; import org.hibernate.query.sqm.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.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; @@ -42,9 +52,15 @@ 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.type.JavaObjectType; +import org.hibernate.type.descriptor.jdbc.BlobJdbcType; +import org.hibernate.type.descriptor.jdbc.ClobJdbcType; import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; +import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType; +import org.hibernate.type.descriptor.jdbc.VarcharJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl; import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType; @@ -84,8 +100,8 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithM */ public class CockroachDialect extends Dialect { + private static final CockroachDBIdentityColumnSupport IDENTITY_COLUMN_SUPPORT = new CockroachDBIdentityColumnSupport(); // KNOWN LIMITATIONS: - // * no support for java.sql.Clob private final PostgreSQLDriverKind driverKind; @@ -223,6 +239,23 @@ public class CockroachDialect extends Dialect { jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLJsonJdbcType.INSTANCE ); } } + + // Force Blob binding to byte[] for CockroachDB + jdbcTypeRegistry.addDescriptor( Types.BLOB, VarbinaryJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( Types.CLOB, VarcharJdbcType.INSTANCE ); + + // The next two contributions are the same as for Postgresql + 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 ) + ) + ); } @Override @@ -296,6 +329,11 @@ public class CockroachDialect extends Dialect { return false; } + @Override + public IdentityColumnSupport getIdentityColumnSupport() { + return IDENTITY_COLUMN_SUPPORT; + } + @Override public boolean supportsValuesList() { return true; @@ -362,6 +400,11 @@ public class CockroachDialect extends Dialect { return "select sequence_name,sequence_schema,sequence_catalog,start_value,minimum_value,maximum_value,increment from information_schema.sequences"; } + @Override + public boolean supportsLobValueChangePropagation() { + return false; + } + @Override public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { return new StandardSqlAstTranslatorFactory() { @@ -746,4 +789,18 @@ public class CockroachDialect extends Dialect { 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 ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/CockroachDB1920IdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/CockroachDBIdentityColumnSupport.java similarity index 91% rename from hibernate-core/src/main/java/org/hibernate/dialect/identity/CockroachDB1920IdentityColumnSupport.java rename to hibernate-core/src/main/java/org/hibernate/dialect/identity/CockroachDBIdentityColumnSupport.java index fa0cebb4e4..6dbc582f1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/CockroachDB1920IdentityColumnSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/CockroachDBIdentityColumnSupport.java @@ -8,7 +8,7 @@ package org.hibernate.dialect.identity; import java.sql.Types; -public class CockroachDB1920IdentityColumnSupport extends IdentityColumnSupportImpl { +public class CockroachDBIdentityColumnSupport extends IdentityColumnSupportImpl { @Override public boolean supportsIdentityColumns() { // Full support requires setting the sql.defaults.serial_normalization=sql_sequence in CockroachDB. diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/lob/MaterializedBlobTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/lob/MaterializedBlobTest.java index a74534897b..07274e7921 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/lob/MaterializedBlobTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/lob/MaterializedBlobTest.java @@ -11,10 +11,14 @@ import java.util.Arrays; import org.junit.Test; import org.hibernate.Session; + import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import org.hibernate.dialect.CockroachDialect; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType; import org.hibernate.type.descriptor.jdbc.BlobJdbcType; @@ -32,6 +36,7 @@ public class MaterializedBlobTest extends BaseCoreFunctionalTestCase { } @Test + @SkipForDialect(value = CockroachDialect.class, comment = "Blob in CockroachDB is same as a varbinary, to assertions will fail") public void testTypeSelection() { int index = sessionFactory().getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(MaterializedBlobEntity.class.getName()).getEntityMetamodel().getPropertyIndex( "theBytes" ); BasicType type = (BasicType) sessionFactory().getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(MaterializedBlobEntity.class.getName()).getEntityMetamodel().getProperties()[index].getType(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java index 48206b43b0..672957674e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java @@ -19,6 +19,7 @@ import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.exception.ConstraintViolationException; @@ -30,6 +31,7 @@ import org.hibernate.query.Query; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -81,6 +83,7 @@ public class BulkManipulationTest extends BaseCoreFunctionalTestCase { } @Test + @SkipForDialect(value = CockroachDialect.class, comment = "https://github.com/cockroachdb/cockroach/issues/41943") public void testUpdateWithSubquery() { Session s = openSession(); s.beginTransaction(); @@ -518,7 +521,8 @@ public class BulkManipulationTest extends BaseCoreFunctionalTestCase { } @Test - @RequiresDialectFeature( value = DialectChecks.SupportsTemporaryTableIdentity.class, comment = "The use of the native generator leads to using identity which also needs to be supported on temporary tables") + @SkipForDialect(value = CockroachDialect.class, comment = "https://github.com/cockroachdb/cockroach/issues/75101") + @RequiresDialectFeature(value = DialectChecks.SupportsTemporaryTableIdentity.class, comment = "The use of the native generator leads to using identity which also needs to be supported on temporary tables") public void testInsertIntoSuperclassPropertiesFails() { TestData data = new TestData(); data.prepare();