tidy up TypecheckUtil

This commit is contained in:
Gavin King 2024-09-10 22:39:14 +02:00
parent 58a6afe78d
commit 47270d4157
1 changed files with 67 additions and 70 deletions

View File

@ -29,6 +29,7 @@ import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.QueryParameterJavaObjectType; import org.hibernate.type.QueryParameterJavaObjectType;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
import java.time.temporal.Temporal; import java.time.temporal.Temporal;
@ -137,8 +138,8 @@ public class TypecheckUtil {
// for tuple constructors, we must check each element // for tuple constructors, we must check each element
if ( lhsDomainType instanceof TupleType && rhsDomainType instanceof TupleType ) { if ( lhsDomainType instanceof TupleType<?> lhsTuple && rhsDomainType instanceof TupleType<?> rhsTuple ) {
return areTupleTypesComparable(bindingContext, (TupleType<?>) lhsDomainType, (TupleType<?>) rhsDomainType ); return areTupleTypesComparable( bindingContext, lhsTuple, rhsTuple );
} }
// allow comparing an embeddable against a tuple literal // allow comparing an embeddable against a tuple literal
@ -151,18 +152,18 @@ public class TypecheckUtil {
// entities can be compared if they belong to the same inheritance hierarchy // entities can be compared if they belong to the same inheritance hierarchy
if ( lhsDomainType instanceof EntityType && rhsDomainType instanceof EntityType ) { if ( lhsDomainType instanceof EntityType<?> lhsEntity && rhsDomainType instanceof EntityType<?> rhsEntity ) {
return areEntityTypesComparable( (EntityType<?>) lhsDomainType, (EntityType<?>) rhsDomainType, bindingContext); return areEntityTypesComparable( lhsEntity, rhsEntity, bindingContext);
} }
// entities can be compared to discriminators if they belong to // entities can be compared to discriminators if they belong to
// the same inheritance hierarchy // the same inheritance hierarchy
if ( lhsDomainType instanceof EntityDiscriminatorSqmPathSource ) { if ( lhsDomainType instanceof EntityDiscriminatorSqmPathSource<?> discriminatorSource ) {
return isDiscriminatorTypeComparable( (EntityDiscriminatorSqmPathSource<?>) lhsDomainType, rhsDomainType, bindingContext); return isDiscriminatorTypeComparable( discriminatorSource, rhsDomainType, bindingContext);
} }
if ( rhsDomainType instanceof EntityDiscriminatorSqmPathSource ) { if ( rhsDomainType instanceof EntityDiscriminatorSqmPathSource<?> discriminatorSource ) {
return isDiscriminatorTypeComparable( (EntityDiscriminatorSqmPathSource<?>) rhsDomainType, lhsDomainType, bindingContext); return isDiscriminatorTypeComparable( discriminatorSource, lhsDomainType, bindingContext);
} }
// Treat the expressions as comparable if they belong to the same // Treat the expressions as comparable if they belong to the same
@ -171,10 +172,9 @@ public class TypecheckUtil {
// decent approach which allows comparison between literals and // decent approach which allows comparison between literals and
// enums, user-defined types, etc. // enums, user-defined types, etc.
if ( lhsDomainType instanceof JdbcMapping && rhsDomainType instanceof JdbcMapping ) { if ( lhsDomainType instanceof JdbcMapping lhsMapping && rhsDomainType instanceof JdbcMapping rhsMapping
if ( areJdbcMappingsComparable( (JdbcMapping) lhsDomainType, (JdbcMapping) rhsDomainType, bindingContext) ) { && areJdbcMappingsComparable( lhsMapping, rhsMapping, bindingContext ) ) {
return true; return true;
}
} }
// Workaround: these are needed for a handful of slightly "weird" cases // Workaround: these are needed for a handful of slightly "weird" cases
@ -183,7 +183,7 @@ public class TypecheckUtil {
// sort of hole warned about above, and accepts many things which are // sort of hole warned about above, and accepts many things which are
// not well-typed. // not well-typed.
// TODO: sort all this out, and remove this branch // // TODO: sort all this out, and remove this branch
if ( isSameJavaType( lhsType, rhsType ) ) { if ( isSameJavaType( lhsType, rhsType ) ) {
return true; return true;
} }
@ -202,28 +202,27 @@ public class TypecheckUtil {
else if ( lhsJdbcMapping.getValueConverter() != null || rhsJdbcMapping.getValueConverter() != null ) { else if ( lhsJdbcMapping.getValueConverter() != null || rhsJdbcMapping.getValueConverter() != null ) {
final JdbcMapping lhsDomainMapping = getDomainJdbcType( lhsJdbcMapping, bindingContext); final JdbcMapping lhsDomainMapping = getDomainJdbcType( lhsJdbcMapping, bindingContext);
final JdbcMapping rhsDomainMapping = getDomainJdbcType( rhsJdbcMapping, bindingContext); final JdbcMapping rhsDomainMapping = getDomainJdbcType( rhsJdbcMapping, bindingContext);
return lhsDomainMapping != null && rhsDomainMapping != null && areJdbcTypesComparable( return lhsDomainMapping != null && rhsDomainMapping != null
lhsDomainMapping.getJdbcType(), && areJdbcTypesComparable( lhsDomainMapping.getJdbcType(), rhsDomainMapping.getJdbcType() );
rhsDomainMapping.getJdbcType()
);
} }
return false; return false;
} }
private static boolean areJdbcTypesComparable(JdbcType lhsJdbcType, JdbcType rhsJdbcType) { private static boolean areJdbcTypesComparable(JdbcType lhsJdbcType, JdbcType rhsJdbcType) {
return lhsJdbcType.getJdbcTypeCode() == rhsJdbcType.getJdbcTypeCode() return lhsJdbcType.getJdbcTypeCode() == rhsJdbcType.getJdbcTypeCode()
// "families" of implicitly-convertible JDBC types // "families" of implicitly-convertible JDBC types
// (this list might need to be extended in future) // (this list might need to be extended in future)
|| lhsJdbcType.isStringLike() && rhsJdbcType.isStringLike() || lhsJdbcType.isStringLike() && rhsJdbcType.isStringLike()
|| lhsJdbcType.isTemporal() && rhsJdbcType.isTemporal() || lhsJdbcType.isTemporal() && rhsJdbcType.isTemporal()
|| lhsJdbcType.isNumber() && rhsJdbcType.isNumber(); || lhsJdbcType.isNumber() && rhsJdbcType.isNumber();
} }
private static JdbcMapping getDomainJdbcType(JdbcMapping jdbcMapping, BindingContext bindingContext) { private static JdbcMapping getDomainJdbcType(JdbcMapping jdbcMapping, BindingContext bindingContext) {
if ( jdbcMapping.getValueConverter() != null ) { final BasicValueConverter<?,?> valueConverter = jdbcMapping.getValueConverter();
if ( valueConverter != null ) {
final BasicType<?> basicType = final BasicType<?> basicType =
bindingContext.getTypeConfiguration() bindingContext.getTypeConfiguration()
.getBasicTypeForJavaType( jdbcMapping.getValueConverter().getDomainJavaType().getJavaType() ); .getBasicTypeForJavaType( valueConverter.getDomainJavaType().getJavaType() );
if ( basicType != null ) { if ( basicType != null ) {
return basicType.getJdbcMapping(); return basicType.getJdbcMapping();
} }
@ -232,17 +231,14 @@ public class TypecheckUtil {
} }
private static EmbeddableDomainType<?> getEmbeddableType(SqmExpressible<?> expressible) { private static EmbeddableDomainType<?> getEmbeddableType(SqmExpressible<?> expressible) {
return expressible instanceof EmbeddableDomainType<?> ? (EmbeddableDomainType<?>) expressible : null; return expressible instanceof EmbeddableDomainType<?> embeddableDomainType ? embeddableDomainType : null;
} }
private static boolean areEmbeddableTypesComparable( private static boolean areEmbeddableTypesComparable(
EmbeddableDomainType<?> lhsType, EmbeddableDomainType<?> lhsType,
EmbeddableDomainType<?> rhsType) { EmbeddableDomainType<?> rhsType) {
if ( rhsType.getJavaType() == lhsType.getJavaType() ) { return rhsType.getJavaType() == lhsType.getJavaType()
return true; || lhsType.isPolymorphic() && getRootEmbeddableType( lhsType ) == getRootEmbeddableType( rhsType );
}
return lhsType.isPolymorphic() && getRootEmbeddableType( lhsType ) == getRootEmbeddableType( rhsType );
} }
private static ManagedDomainType<?> getRootEmbeddableType(EmbeddableDomainType<?> embeddableType) { private static ManagedDomainType<?> getRootEmbeddableType(EmbeddableDomainType<?> embeddableType) {
@ -262,7 +258,7 @@ public class TypecheckUtil {
} }
else { else {
for ( int i = 0; i < lhsTuple.componentCount(); i++ ) { for ( int i = 0; i < lhsTuple.componentCount(); i++ ) {
if ( !areTypesComparable( lhsTuple.get(i), rhsTuple.get(i), bindingContext) ) { if ( !areTypesComparable( lhsTuple.get(i), rhsTuple.get(i), bindingContext ) ) {
return false; return false;
} }
} }
@ -273,29 +269,28 @@ public class TypecheckUtil {
private static boolean areEntityTypesComparable( private static boolean areEntityTypesComparable(
EntityType<?> lhsType, EntityType<?> rhsType, EntityType<?> lhsType, EntityType<?> rhsType,
BindingContext bindingContext) { BindingContext bindingContext) {
EntityPersister lhsEntity = getEntityDescriptor(bindingContext, lhsType.getName() ); final EntityPersister lhsEntity = getEntityDescriptor( bindingContext, lhsType.getName() );
EntityPersister rhsEntity = getEntityDescriptor(bindingContext, rhsType.getName() ); final EntityPersister rhsEntity = getEntityDescriptor( bindingContext, rhsType.getName() );
return lhsEntity.getRootEntityName().equals( rhsEntity.getRootEntityName() ); return lhsEntity.getRootEntityName().equals( rhsEntity.getRootEntityName() );
} }
private static boolean isDiscriminatorTypeComparable( private static boolean isDiscriminatorTypeComparable(
EntityDiscriminatorSqmPathSource<?> lhsDiscriminator, SqmExpressible<?> rhsType, EntityDiscriminatorSqmPathSource<?> lhsDiscriminator, SqmExpressible<?> rhsType,
BindingContext bindingContext) { BindingContext bindingContext) {
String entityName = lhsDiscriminator.getEntityDomainType().getHibernateEntityName(); final String entityName = lhsDiscriminator.getEntityDomainType().getHibernateEntityName();
EntityPersister lhsEntity = bindingContext.getMappingMetamodel().getEntityDescriptor( entityName ); final EntityPersister lhsEntity = bindingContext.getMappingMetamodel().getEntityDescriptor( entityName );
if ( rhsType instanceof EntityType ) { if ( rhsType instanceof EntityType<?> entityType ) {
String rhsEntityName = ((EntityType<?>) rhsType).getName(); final String rhsEntityName = entityType.getName();
EntityPersister rhsEntity = getEntityDescriptor(bindingContext, rhsEntityName ); final EntityPersister rhsEntity = getEntityDescriptor( bindingContext, rhsEntityName );
return lhsEntity.getRootEntityName().equals( rhsEntity.getRootEntityName() ); return lhsEntity.getRootEntityName().equals( rhsEntity.getRootEntityName() );
} }
else if ( rhsType instanceof EntityDiscriminatorSqmPathSource ) { else if ( rhsType instanceof EntityDiscriminatorSqmPathSource<?> discriminator ) {
EntityDiscriminatorSqmPathSource<?> discriminator = (EntityDiscriminatorSqmPathSource<?>) rhsType; final String rhsEntityName = discriminator.getEntityDomainType().getHibernateEntityName();
String rhsEntityName = discriminator.getEntityDomainType().getHibernateEntityName(); final EntityPersister rhsEntity = bindingContext.getMappingMetamodel().getEntityDescriptor( rhsEntityName );
EntityPersister rhsEntity = bindingContext.getMappingMetamodel().getEntityDescriptor( rhsEntityName );
return rhsEntity.getRootEntityName().equals( lhsEntity.getRootEntityName() ); return rhsEntity.getRootEntityName().equals( lhsEntity.getRootEntityName() );
} }
else { else {
BasicType<?> discriminatorType = (BasicType<?>) final BasicType<?> discriminatorType = (BasicType<?>)
lhsDiscriminator.getEntityMapping().getDiscriminatorMapping().getMappedType(); lhsDiscriminator.getEntityMapping().getDiscriminatorMapping().getMappedType();
return areTypesComparable( discriminatorType, rhsType, bindingContext); return areTypesComparable( discriminatorType, rhsType, bindingContext);
} }
@ -317,8 +312,9 @@ public class TypecheckUtil {
// entities can be assigned if they belong to the same inheritance hierarchy // entities can be assigned if they belong to the same inheritance hierarchy
if ( targetType instanceof EntityType && expressionType instanceof EntityType ) { if ( targetType instanceof EntityType<?> targetEntity
return isEntityTypeAssignable( (EntityType<?>) targetType, (EntityType<?>) expressionType, bindingContext); && expressionType instanceof EntityType<?> expressionEntity ) {
return isEntityTypeAssignable( targetEntity, expressionEntity, bindingContext);
} }
// Treat the expression as assignable to the target path if they belong // Treat the expression as assignable to the target path if they belong
@ -327,11 +323,11 @@ public class TypecheckUtil {
// decent approach which allows comparison between literals and enums, // decent approach which allows comparison between literals and enums,
// user-defined types, etc. // user-defined types, etc.
DomainType<?> lhsDomainType = targetType.getSqmType(); final DomainType<?> lhsDomainType = targetType.getSqmType();
DomainType<?> rhsDomainType = expressionType.getSqmType(); final DomainType<?> rhsDomainType = expressionType.getSqmType();
if ( lhsDomainType instanceof JdbcMapping && rhsDomainType instanceof JdbcMapping ) { if ( lhsDomainType instanceof JdbcMapping lhsMapping && rhsDomainType instanceof JdbcMapping rhsMapping ) {
JdbcType lhsJdbcType = ((JdbcMapping) lhsDomainType).getJdbcType(); final JdbcType lhsJdbcType = lhsMapping.getJdbcType();
JdbcType rhsJdbcType = ((JdbcMapping) rhsDomainType).getJdbcType(); final JdbcType rhsJdbcType = rhsMapping.getJdbcType();
if ( lhsJdbcType.getJdbcTypeCode() == rhsJdbcType.getJdbcTypeCode() if ( lhsJdbcType.getJdbcTypeCode() == rhsJdbcType.getJdbcTypeCode()
// "families" of implicitly-convertible JDBC types // "families" of implicitly-convertible JDBC types
// (this list might need to be extended in future) // (this list might need to be extended in future)
@ -367,8 +363,8 @@ public class TypecheckUtil {
private static boolean isEntityTypeAssignable( private static boolean isEntityTypeAssignable(
EntityType<?> lhsType, EntityType<?> rhsType, EntityType<?> lhsType, EntityType<?> rhsType,
BindingContext bindingContext) { BindingContext bindingContext) {
EntityPersister lhsEntity = getEntityDescriptor(bindingContext, lhsType.getName() ); final EntityPersister lhsEntity = getEntityDescriptor( bindingContext, lhsType.getName() );
EntityPersister rhsEntity = getEntityDescriptor(bindingContext, rhsType.getName() ); final EntityPersister rhsEntity = getEntityDescriptor( bindingContext, rhsType.getName() );
return lhsEntity.isSubclassEntityName( rhsEntity.getEntityName() ); return lhsEntity.isSubclassEntityName( rhsEntity.getEntityName() );
} }
@ -384,23 +380,23 @@ public class TypecheckUtil {
final SqmExpression<?> left = (SqmExpression<?>) x; final SqmExpression<?> left = (SqmExpression<?>) x;
final SqmExpression<?> right = (SqmExpression<?>) y; final SqmExpression<?> right = (SqmExpression<?>) y;
final Integer leftTupleLength = left.getTupleLength(); final Integer leftTupleLength = left.getTupleLength();
final Integer rightTupleLength; final Integer rightTupleLength = right.getTupleLength();
if ( leftTupleLength != null && ( rightTupleLength = right.getTupleLength() ) != null if ( leftTupleLength != null && rightTupleLength != null
&& leftTupleLength.intValue() != rightTupleLength.intValue() ) { && leftTupleLength.intValue() != rightTupleLength.intValue() ) {
throw new SemanticException( "Cannot compare tuples of different lengths" ); throw new SemanticException( "Cannot compare tuples of different lengths" );
} }
// SqmMemerOfPredicate is the only one allowing multi-valued paths, its comparability is now evaluated in areTypesComparable // SqmMemberOfPredicate is the only one allowing multivalued paths, its comparability is now evaluated in areTypesComparable
// i.e. without calling this method, so we can check this here for other Predicates that do call this // i.e. without calling this method, so we can check this here for other Predicates that do call this
if ( left instanceof SqmPluralValuedSimplePath || right instanceof SqmPluralValuedSimplePath ) { if ( left instanceof SqmPluralValuedSimplePath || right instanceof SqmPluralValuedSimplePath ) {
throw new SemanticException( "Multi valued paths are only allowed for the member of operator" ); throw new SemanticException( "Multivalued paths are only allowed for the 'member of' operator" );
} }
// allow comparing literal null to things // allow comparing literal null to things
if ( !( left instanceof SqmLiteralNull ) && !( right instanceof SqmLiteralNull ) ) { if ( !( left instanceof SqmLiteralNull ) && !( right instanceof SqmLiteralNull ) ) {
final SqmExpressible<?> leftType = left.getExpressible(); final SqmExpressible<?> leftType = left.getExpressible();
final SqmExpressible<?> rightType = right.getExpressible(); final SqmExpressible<?> rightType = right.getExpressible();
if ( !areTypesComparable( leftType, rightType, bindingContext) ) { if ( !areTypesComparable( leftType, rightType, bindingContext ) ) {
throw new SemanticException( throw new SemanticException(
String.format( String.format(
"Cannot compare left expression of type '%s' with right expression of type '%s'", "Cannot compare left expression of type '%s' with right expression of type '%s'",
@ -424,8 +420,8 @@ public class TypecheckUtil {
// TODO: check that the target path is nullable // TODO: check that the target path is nullable
} }
else { else {
SqmPathSource<?> targetType = targetPath.getNodeType(); final SqmPathSource<?> targetType = targetPath.getNodeType();
SqmExpressible<?> expressionType = expression.getNodeType(); final SqmExpressible<?> expressionType = expression.getNodeType();
if ( !isTypeAssignable( targetType, expressionType, bindingContext) ) { if ( !isTypeAssignable( targetType, expressionType, bindingContext) ) {
throw new SemanticException( throw new SemanticException(
String.format( String.format(
@ -542,11 +538,12 @@ public class TypecheckUtil {
} }
public static boolean isNumberArray(SqmExpressible<?> expressible) { public static boolean isNumberArray(SqmExpressible<?> expressible) {
final DomainType<?> domainType; if ( expressible != null ) {
if ( expressible != null && ( domainType = expressible.getSqmType() ) != null ) { final DomainType<?> domainType = expressible.getSqmType();
return domainType instanceof BasicPluralType<?, ?> && Number.class.isAssignableFrom( if ( domainType != null ) {
( (BasicPluralType<?, ?>) domainType ).getElementType().getJavaType() return domainType instanceof BasicPluralType<?, ?> basicPluralType
); && Number.class.isAssignableFrom( basicPluralType.getElementType().getJavaType() );
}
} }
return false; return false;
} }
@ -554,8 +551,8 @@ public class TypecheckUtil {
public static void assertString(SqmExpression<?> expression) { public static void assertString(SqmExpression<?> expression) {
final SqmExpressible<?> nodeType = expression.getNodeType(); final SqmExpressible<?> nodeType = expression.getNodeType();
if ( nodeType != null ) { if ( nodeType != null ) {
final DomainType<?> domainType = nodeType.getSqmType(); if ( !( nodeType.getSqmType() instanceof JdbcMapping jdbcMapping )
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isStringLike() ) { || !jdbcMapping.getJdbcType().isStringLike() ) {
throw new SemanticException( throw new SemanticException(
"Operand of 'like' is of type '" + nodeType.getTypeName() + "Operand of 'like' is of type '" + nodeType.getTypeName() +
"' which is not a string (its JDBC type code is not string-like)" "' which is not a string (its JDBC type code is not string-like)"
@ -567,8 +564,8 @@ public class TypecheckUtil {
public static void assertDuration(SqmExpression<?> expression) { public static void assertDuration(SqmExpression<?> expression) {
final SqmExpressible<?> nodeType = expression.getNodeType(); final SqmExpressible<?> nodeType = expression.getNodeType();
if ( nodeType != null ) { if ( nodeType != null ) {
final DomainType<?> domainType = nodeType.getSqmType(); if ( !( nodeType.getSqmType() instanceof JdbcMapping jdbcMapping )
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isDuration() ) { || !jdbcMapping.getJdbcType().isDuration() ) {
throw new SemanticException( throw new SemanticException(
"Operand of 'by' is of type '" + nodeType.getTypeName() + "Operand of 'by' is of type '" + nodeType.getTypeName() +
"' which is not a duration (its JDBC type code is not duration-like)" "' which is not a duration (its JDBC type code is not duration-like)"
@ -580,8 +577,8 @@ public class TypecheckUtil {
public static void assertNumeric(SqmExpression<?> expression, UnaryArithmeticOperator op) { public static void assertNumeric(SqmExpression<?> expression, UnaryArithmeticOperator op) {
final SqmExpressible<?> nodeType = expression.getNodeType(); final SqmExpressible<?> nodeType = expression.getNodeType();
if ( nodeType != null ) { if ( nodeType != null ) {
final DomainType<?> domainType = nodeType.getSqmType(); if ( !( nodeType.getSqmType() instanceof JdbcMapping jdbcMapping )
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isNumber() ) { || !jdbcMapping.getJdbcType().isNumber() ) {
throw new SemanticException( throw new SemanticException(
"Operand of " + op.getOperatorChar() + " is of type '" + nodeType.getTypeName() + "Operand of " + op.getOperatorChar() + " is of type '" + nodeType.getTypeName() +
"' which is not a numeric type (its JDBC type code is not numeric)" "' which is not a numeric type (its JDBC type code is not numeric)"