2828import org .hibernate .query .sqm .tree .expression .SqmLiteralNull ;
2929import org .hibernate .type .BasicPluralType ;
3030import org .hibernate .type .BasicType ;
31+ import org .hibernate .type .ConvertedBasicType ;
3132import org .hibernate .type .QueryParameterJavaObjectType ;
3233import org .hibernate .type .descriptor .converter .spi .BasicValueConverter ;
3334import org .hibernate .type .descriptor .jdbc .JdbcType ;
3435
3536import java .time .temporal .Temporal ;
3637import java .time .temporal .TemporalAmount ;
38+ import java .util .Objects ;
3739
3840import 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