Skip to content

Commit f80ba1f

Browse files
committed
HHH-16880 nail a bit down the handling of converted types in TypecheckUtil
1 parent 20cd322 commit f80ba1f

File tree

1 file changed

+79
-24
lines changed

1 file changed

+79
-24
lines changed

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
2929
import org.hibernate.type.BasicPluralType;
3030
import org.hibernate.type.BasicType;
31+
import org.hibernate.type.ConvertedBasicType;
3132
import org.hibernate.type.QueryParameterJavaObjectType;
3233
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
3334
import org.hibernate.type.descriptor.jdbc.JdbcType;
3435

3536
import java.time.temporal.Temporal;
3637
import java.time.temporal.TemporalAmount;
38+
import java.util.Objects;
3739

3840
import static org.hibernate.type.descriptor.java.JavaTypeHelper.isUnknown;
3941

@@ -110,7 +112,16 @@ public static boolean areTypesComparable(
110112
return true;
111113
}
112114

113-
// for query with parameters we are unable to resolve the correct JavaType, especially for tuple of parameters
115+
// if one of the types is unknown, assume that this is the result
116+
// of an error that will be detected / reported elsewhere
117+
118+
if ( isUnknown( lhsType.getExpressibleJavaType() )
119+
|| isUnknown( rhsType.getExpressibleJavaType() ) ) {
120+
return true;
121+
}
122+
123+
// for query with parameters we are unable to resolve the correct JavaType,
124+
// especially for tuple of parameters
114125

115126
if ( lhsType instanceof QueryParameterJavaObjectType || rhsType instanceof QueryParameterJavaObjectType) {
116127
return true;
@@ -177,18 +188,17 @@ && areJdbcMappingsComparable( lhsMapping, rhsMapping, bindingContext ) ) {
177188
return true;
178189
}
179190

180-
// Workaround: these are needed for a handful of slightly "weird" cases
181-
// involving Java field literals and converters, where we don't have
182-
// access to the correct JDBC type above. However, this is exactly the
183-
// sort of hole warned about above, and accepts many things which are
184-
// not well-typed.
191+
// if one of the types has a ValueConverter, things get really complicated
192+
// we do something a bit broken here and check that the Java types
185193

186-
// // TODO: sort all this out, and remove this branch
187-
if ( isSameJavaType( lhsType, rhsType ) ) {
194+
if ( ( isConvertedType( lhsType ) || isConvertedType( rhsType ) )
195+
&& sameJavaType( lhsType, rhsType ) ) {
188196
return true;
189197
}
190198

191-
return false;
199+
// if both have the same JDBC type, the assignment is acceptable
200+
201+
return lhsType.getRelationalJavaType() == rhsType.getRelationalJavaType();
192202
}
193203

194204
private static boolean areJdbcMappingsComparable(
@@ -310,6 +320,14 @@ private static boolean isTypeAssignable(
310320
return true;
311321
}
312322

323+
// if one of the types is unknown, assume that this is the result
324+
// of an error that will be detected / reported elsewhere
325+
326+
if ( isUnknown( targetType.getExpressibleJavaType() )
327+
|| isUnknown( expressionType.getExpressibleJavaType() ) ) {
328+
return true;
329+
}
330+
313331
// entities can be assigned if they belong to the same inheritance hierarchy
314332

315333
if ( targetType instanceof EntityType<?> targetEntity
@@ -339,25 +357,39 @@ private static boolean isTypeAssignable(
339357
}
340358
}
341359

342-
// Workaround: these are needed for a handful of slightly "weird" cases
343-
// involving Java field literals and converters, where we don't have
344-
// access to the correct JDBC type above. However, this is exactly the
345-
// sort of hole warned about above, and accepts many things which are
346-
// not well-typed.
360+
// if one of the types has a ValueConverter, things get really complicated
361+
// we do something a bit broken here and check that the Java types
347362

348-
// TODO: sort all this out, and remove this branch
349-
if ( isSameJavaType( targetType, expressionType ) ) {
363+
if ( ( isConvertedType( targetType ) || isConvertedType( expressionType ) )
364+
&& sameJavaType( targetType, expressionType ) ) {
350365
return true;
351366
}
352367

353-
return false;
368+
// if both have the same JDBC type, the assignment is acceptable
369+
370+
return targetType.getRelationalJavaType() == expressionType.getRelationalJavaType();
371+
}
372+
373+
private static boolean sameJavaType(SqmExpressible<?> leftType, SqmExpressible<?> rightType) {
374+
return canonicalize( leftType.getBindableJavaType() ) == canonicalize( rightType.getBindableJavaType() );
354375
}
355376

356-
private static boolean isSameJavaType(SqmExpressible<?> leftType, SqmExpressible<?> rightType) {
357-
return isUnknown( leftType.getExpressibleJavaType() ) || isUnknown( rightType.getExpressibleJavaType() )
358-
|| leftType.getRelationalJavaType() == rightType.getRelationalJavaType()
359-
|| leftType.getExpressibleJavaType() == rightType.getExpressibleJavaType()
360-
|| leftType.getBindableJavaType() == rightType.getBindableJavaType();
377+
private static boolean isConvertedType(SqmExpressible<?> type) {
378+
return type.getSqmType() instanceof ConvertedBasicType<?>;
379+
}
380+
381+
private static Class<?> canonicalize(Class<?> lhs) {
382+
return switch (lhs.getCanonicalName()) {
383+
case "boolean" -> Boolean.class;
384+
case "byte" -> Byte.class;
385+
case "short" -> Short.class;
386+
case "int" -> Integer.class;
387+
case "long" -> Long.class;
388+
case "float" -> Float.class;
389+
case "double" -> Double.class;
390+
case "char" -> Character.class;
391+
default -> lhs;
392+
};
361393
}
362394

363395
private static boolean isEntityTypeAssignable(
@@ -396,7 +428,19 @@ public static void assertComparable(Expression<?> x, Expression<?> y, BindingCon
396428
if ( !( left instanceof SqmLiteralNull ) && !( right instanceof SqmLiteralNull ) ) {
397429
final SqmExpressible<?> leftType = left.getExpressible();
398430
final SqmExpressible<?> rightType = right.getExpressible();
399-
if ( !areTypesComparable( leftType, rightType, bindingContext ) ) {
431+
if ( leftType != null && rightType != null
432+
&& left.isEnum() && right.isEnum() ) {
433+
// this is needed by Hibernate Processor due to the weird
434+
// handling of enumerated types in the annotation processor
435+
if ( !Objects.equals( leftType.getTypeName(), rightType.getTypeName() ) ) {
436+
String.format(
437+
"Cannot compare left expression of enumerated type '%s' with right expression of enumerated type '%s'",
438+
leftType.getTypeName(),
439+
rightType.getTypeName()
440+
);
441+
}
442+
}
443+
else if ( !areTypesComparable( leftType, rightType, bindingContext ) ) {
400444
throw new SemanticException(
401445
String.format(
402446
"Cannot compare left expression of type '%s' with right expression of type '%s'",
@@ -422,7 +466,18 @@ public static void assertAssignable(
422466
else {
423467
final SqmPathSource<?> targetType = targetPath.getNodeType();
424468
final SqmExpressible<?> expressionType = expression.getNodeType();
425-
if ( !isTypeAssignable( targetType, expressionType, bindingContext) ) {
469+
if ( targetType != null && expressionType != null && targetPath.isEnum() ) {
470+
// this is needed by Hibernate Processor due to the weird
471+
// handling of enumerated types in the annotation processor
472+
if ( !Objects.equals( targetType.getTypeName(), expressionType.getTypeName() ) ) {
473+
String.format(
474+
"Cannot compare left expression of enumerated type '%s' with right expression of enumerated type '%s'",
475+
targetType.getTypeName(),
476+
expressionType.getTypeName()
477+
);
478+
}
479+
}
480+
else if ( !isTypeAssignable( targetType, expressionType, bindingContext) ) {
426481
throw new SemanticException(
427482
String.format(
428483
"Cannot assign expression of type '%s' to target path '%s' of type '%s'",

0 commit comments

Comments
 (0)