Skip to content

Commit

Permalink
HHH-18564 Handle attribute converters for query literals
Browse files Browse the repository at this point in the history
Allow both domain and relational forms when an attribute converter is present
  • Loading branch information
mbladel committed Nov 5, 2024
1 parent 6017ae4 commit ab9e671
Showing 1 changed file with 40 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5629,40 +5629,7 @@ else if ( inferableExpressible instanceof BasicValuedMapping ) {
final BasicValuedMapping basicValuedMapping = (BasicValuedMapping) inferableExpressible;
final BasicValueConverter valueConverter = basicValuedMapping.getJdbcMapping().getValueConverter();
if ( valueConverter != null ) {
final Object value = literal.getLiteralValue();
final Object sqlLiteralValue;
// For converted query literals, we support both, the domain and relational java type
if ( value == null || valueConverter.getDomainJavaType().isInstance( value ) ) {
sqlLiteralValue = valueConverter.toRelationalValue( value );
}
else if ( valueConverter.getRelationalJavaType().isInstance( value ) ) {
sqlLiteralValue = value;
}
else if ( Character.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() )
&& value instanceof CharSequence && ( (CharSequence) value ).length() == 1 ) {
sqlLiteralValue = ( (CharSequence) value ).charAt( 0 );
}
// In HQL, number literals might not match the relational java type exactly,
// so we allow coercion between the number types
else if ( Number.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() )
&& value instanceof Number ) {
sqlLiteralValue = valueConverter.getRelationalJavaType().coerce(
value,
creationContext.getSessionFactory()::getTypeConfiguration
);
}
else {
throw new SemanticException(
String.format(
Locale.ROOT,
"Literal type '%s' did not match domain type '%s' nor converted type '%s'",
value.getClass(),
valueConverter.getDomainJavaType().getJavaTypeClass().getName(),
valueConverter.getRelationalJavaType().getJavaTypeClass().getName()
)
);
}
return new QueryLiteral<>( sqlLiteralValue, basicValuedMapping );
return new QueryLiteral<>( sqlLiteralValue( valueConverter, literal.getLiteralValue() ), basicValuedMapping );
}
}

Expand Down Expand Up @@ -5779,6 +5746,40 @@ else if ( expressible instanceof EntityValuedModelPart ) {
}
}

private <D> Object sqlLiteralValue(BasicValueConverter<D,?> valueConverter, D value) {
// For converted query literals, we support both, the domain and relational java type
if ( value == null || valueConverter.getDomainJavaType().isInstance( value ) ) {
return valueConverter.toRelationalValue( value );
}
else if ( valueConverter.getRelationalJavaType().isInstance( value ) ) {
return value;
}
else if ( Character.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() )
&& value instanceof CharSequence && ( (CharSequence) value ).length() == 1 ) {
return ( (CharSequence) value ).charAt( 0 );
}
// In HQL, number literals might not match the relational java type exactly,
// so we allow coercion between the number types
else if ( Number.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() )
&& value instanceof Number ) {
return valueConverter.getRelationalJavaType().coerce(
value,
creationContext.getSessionFactory()::getTypeConfiguration
);
}
else {
throw new SemanticException(
String.format(
Locale.ROOT,
"Literal type '%s' did not match domain type '%s' nor converted type '%s'",
value.getClass(),
valueConverter.getDomainJavaType().getJavaTypeClass().getName(),
valueConverter.getRelationalJavaType().getJavaTypeClass().getName()
)
);
}
}

@Override
public <N extends Number> Expression visitHqlNumericLiteral(SqmHqlNumericLiteral<N> numericLiteral) {
final BasicValuedMapping inferredExpressible = (BasicValuedMapping) getInferredValueMapping();
Expand Down Expand Up @@ -7362,12 +7363,13 @@ private static <T extends Enum<T>> QueryLiteral<T> queryLiteral(
);
}

@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Object visitFieldLiteral(SqmFieldLiteral<?> sqmFieldLiteral) {
return new QueryLiteral<>(
sqmFieldLiteral.getValue(),
(BasicValuedMapping) determineValueMapping( sqmFieldLiteral )
);
final BasicValuedMapping valueMapping = (BasicValuedMapping) determineValueMapping( sqmFieldLiteral );
final Object value = sqmFieldLiteral.getValue();
final BasicValueConverter converter = valueMapping.getJdbcMapping().getValueConverter();
return new QueryLiteral<>( converter != null ? sqlLiteralValue( converter, value ) : value, valueMapping );
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down

0 comments on commit ab9e671

Please sign in to comment.