From 1cd5ea61f682ce6ce4ae7efe089aab7901d88327 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 29 Jan 2020 18:18:22 +0100 Subject: [PATCH] Fix type inference for case expressions and simple case SQL rendering issue --- .../domain/internal/MappingMetamodelImpl.java | 5 ++ .../sqm/sql/BaseSqmToSqlAstConverter.java | 79 ++++++++++++++----- .../sql/ast/spi/AbstractSqlAstWalker.java | 2 +- .../expression/CaseSearchedExpression.java | 6 ++ .../tree/expression/CaseSimpleExpression.java | 7 ++ 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index ad4a320cee..54b868e595 100755 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -47,6 +47,7 @@ import org.hibernate.metamodel.internal.JpaStaticMetaModelPopulationSetting; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.model.domain.AllowableParameterType; +import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.EmbeddableDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; @@ -697,6 +698,10 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen return (BasicType) sqmExpressable; } + if ( sqmExpressable instanceof BasicSqmPathSource ) { + return getTypeConfiguration().getBasicTypeForJavaType(((BasicSqmPathSource) sqmExpressable).getJavaType()); + } + if ( sqmExpressable instanceof CompositeSqmPathSource ) { throw new NotYetImplementedFor6Exception( "Resolution of embedded-valued SqmExpressable nodes not yet implemented" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index c4bbd1edfb..8677b5c426 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -31,6 +31,7 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.BinaryArithmeticOperator; import org.hibernate.query.NavigablePath; import org.hibernate.query.UnaryArithmeticOperator; +import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; @@ -838,6 +839,23 @@ public abstract class BaseSqmToSqlAstConverter ); } + protected MappingModelExpressable resolveMappingExpressable(SqmExpressable nodeType) { + final MappingModelExpressable valueMapping = getCreationContext().getDomainModel().resolveMappingExpressable( nodeType ); + + if ( valueMapping == null ) { + final Supplier currentExpressableSupplier = inferableTypeAccessStack.getCurrent(); + if ( currentExpressableSupplier != null ) { + return currentExpressableSupplier.get(); + } + } + + if ( valueMapping == null ) { + throw new ConversionException( "Could not determine ValueMapping for SqmExpressable: " + nodeType ); + } + + return valueMapping; + } + protected MappingModelExpressable determineValueMapping(SqmExpression sqmExpression) { if ( sqmExpression instanceof SqmParameter ) { return determineValueMapping( (SqmParameter) sqmExpression ); @@ -871,7 +889,6 @@ public abstract class BaseSqmToSqlAstConverter return valueMapping; } - @SuppressWarnings("WeakerAccess") protected MappingModelExpressable determineValueMapping(SqmParameter sqmParameter) { log.debugf( "Determining mapping-model type for SqmParameter : %s", sqmParameter ); @@ -1486,37 +1503,59 @@ public abstract class BaseSqmToSqlAstConverter @Override public CaseSimpleExpression visitSimpleCaseExpression(SqmCaseSimple expression) { - final CaseSimpleExpression result = new CaseSimpleExpression( - determineValueMapping( expression ), - (Expression) expression.getFixture().accept( this ) - ); - - for ( SqmCaseSimple.WhenFragment whenFragment : expression.getWhenFragments() ) { - result.when( - (Expression) whenFragment.getCheckValue().accept( this ), - (Expression) whenFragment.getResult().accept( this ) + SqmExpressable resultType = expression.getNodeType(); + List whenFragments = new ArrayList<>( expression.getWhenFragments().size() ); + for ( SqmCaseSimple.WhenFragment whenFragment : expression.getWhenFragments() ) { + resultType = QueryHelper.highestPrecedenceType2( resultType, whenFragment.getResult().getNodeType() ); + whenFragments.add( + new CaseSimpleExpression.WhenFragment( + (Expression) whenFragment.getCheckValue().accept(this), + (Expression) whenFragment.getResult().accept(this) + ) ); } - result.otherwise( (Expression) expression.getOtherwise().accept( this ) ); + Expression otherwise = null; + if ( expression.getOtherwise() != null ) { + resultType = QueryHelper.highestPrecedenceType2( resultType, expression.getOtherwise().getNodeType() ); + otherwise = (Expression) expression.getOtherwise().accept(this ); + } + + final CaseSimpleExpression result = new CaseSimpleExpression( + resolveMappingExpressable( resultType ), + (Expression) expression.getFixture().accept( this ), + whenFragments, + otherwise + ); return result; } @Override public CaseSearchedExpression visitSearchedCaseExpression(SqmCaseSearched expression) { - final CaseSearchedExpression result = new CaseSearchedExpression( - determineValueMapping( expression ) - ); - - for ( SqmCaseSearched.WhenFragment whenFragment : expression.getWhenFragments() ) { - result.when( - (Predicate) whenFragment.getPredicate().accept( this ), - (Expression) whenFragment.getResult().accept( this ) + SqmExpressable resultType = expression.getNodeType(); + List whenFragments = new ArrayList<>( expression.getWhenFragments().size() ); + for ( SqmCaseSearched.WhenFragment whenFragment : expression.getWhenFragments() ) { + resultType = QueryHelper.highestPrecedenceType2( resultType, whenFragment.getResult().getNodeType() ); + whenFragments.add( + new CaseSearchedExpression.WhenFragment( + (Predicate) whenFragment.getPredicate().accept(this), + (Expression) whenFragment.getResult().accept(this) + ) ); } - result.otherwise( (Expression) expression.getOtherwise().accept( this ) ); + Expression otherwise = null; + if ( expression.getOtherwise() != null ) { + resultType = QueryHelper.highestPrecedenceType2( resultType, expression.getOtherwise().getNodeType() ); + otherwise = (Expression) expression.getOtherwise().accept(this ); + } + + final CaseSearchedExpression result = new CaseSearchedExpression( + resolveMappingExpressable( resultType ), + whenFragments, + otherwise + ); return result; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java index 6a52794548..ae4490f8e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java @@ -818,7 +818,7 @@ public abstract class AbstractSqlAstWalker @Override public void visitCaseSimpleExpression(CaseSimpleExpression caseSimpleExpression) { - appendSql( " case" ); + appendSql( "case " ); caseSimpleExpression.getFixture().accept( this ); for ( CaseSimpleExpression.WhenFragment whenFragment : caseSimpleExpression.getWhenFragments() ) { appendSql( " when " ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSearchedExpression.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSearchedExpression.java index b405c247e5..e7305711cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSearchedExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSearchedExpression.java @@ -36,6 +36,12 @@ public class CaseSearchedExpression implements Expression, DomainResultProducer this.type = (BasicType) type; } + public CaseSearchedExpression(MappingModelExpressable type, List whenFragments, Expression otherwise) { + this.type = (BasicType) type; + this.whenFragments = whenFragments; + this.otherwise = otherwise; + } + public List getWhenFragments() { return whenFragments; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSimpleExpression.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSimpleExpression.java index 1482db9800..2192fd51cd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSimpleExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSimpleExpression.java @@ -32,6 +32,13 @@ public class CaseSimpleExpression implements Expression, DomainResultProducer { this.fixture = fixture; } + public CaseSimpleExpression(MappingModelExpressable type, Expression fixture, List whenFragments, Expression otherwise) { + this.type = type; + this.fixture = fixture; + this.whenFragments = whenFragments; + this.otherwise = otherwise; + } + public Expression getFixture() { return fixture; }