re-enable tests
re-organize some tests case expression and nested expression type inference preliminary support for "type precedence"
This commit is contained in:
parent
3958ee2360
commit
c2f59beb64
|
@ -43,6 +43,7 @@ public interface MappingMetamodel {
|
|||
* todo (6.0) : POC!!! Intended for use in SQM -> SQL translation
|
||||
*/
|
||||
MappingModelExpressable resolveMappingExpressable(SqmExpressable<?> sqmExpressable, Function<NavigablePath, TableGroup> tableGroupLocator);
|
||||
MappingModelExpressable lenientlyResolveMappingExpressable(SqmExpressable<?> sqmExpressable, Function<NavigablePath, TableGroup> tableGroupLocator);
|
||||
|
||||
/**
|
||||
* Given a Java type, determine the corresponding AllowableParameterType to
|
||||
|
|
|
@ -708,6 +708,17 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen
|
|||
return results.toArray( new String[results.size()] );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingModelExpressable lenientlyResolveMappingExpressable(SqmExpressable<?> sqmExpressable, Function<NavigablePath, TableGroup> tableGroupLocator) {
|
||||
try {
|
||||
return resolveMappingExpressable( sqmExpressable, tableGroupLocator );
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MappingModelExpressable resolveMappingExpressable(SqmExpressable<?> sqmExpressable, Function<NavigablePath, TableGroup> tableGroupLocator) {
|
||||
if ( sqmExpressable instanceof SqmPath ) {
|
||||
|
|
|
@ -103,6 +103,7 @@ import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
|
|||
import org.hibernate.query.sqm.sql.internal.SqmMapEntryResult;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.TypeHelper;
|
||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
|
||||
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
||||
|
@ -2432,6 +2433,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
);
|
||||
}
|
||||
|
||||
protected MappingModelExpressable<?> lenientlyResolveMappingExpressable(SqmExpressable<?> nodeType) {
|
||||
try {
|
||||
return resolveMappingExpressable( nodeType );
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// todo (6.0) : log?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected MappingModelExpressable<?> resolveMappingExpressable(SqmExpressable<?> nodeType) {
|
||||
final MappingModelExpressable valueMapping = getCreationContext().getDomainModel().resolveMappingExpressable(
|
||||
nodeType,
|
||||
|
@ -3547,60 +3558,121 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
@Override
|
||||
public CaseSimpleExpression visitSimpleCaseExpression(SqmCaseSimple<?, ?> expression) {
|
||||
List<CaseSimpleExpression.WhenFragment> whenFragments = new ArrayList<>( expression.getWhenFragments().size() );
|
||||
for ( SqmCaseSimple.WhenFragment<?, ?> whenFragment : expression.getWhenFragments() ) {
|
||||
whenFragments.add(
|
||||
new CaseSimpleExpression.WhenFragment(
|
||||
(Expression) whenFragment.getCheckValue().accept( this ),
|
||||
visitWithInferredType( whenFragment.getResult(), expression )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final MappingModelExpressable<?> alreadyKnown = creationContext
|
||||
.getDomainModel()
|
||||
.lenientlyResolveMappingExpressable( expression.getNodeType(), getFromClauseIndex()::findTableGroup );
|
||||
MappingModelExpressable<?> resolved = alreadyKnown;
|
||||
|
||||
inferrableTypeAccessStack.push( () -> alreadyKnown );
|
||||
|
||||
Expression otherwise = null;
|
||||
if ( expression.getOtherwise() != null ) {
|
||||
otherwise = visitWithInferredType( expression.getOtherwise(), expression );
|
||||
try {
|
||||
for ( SqmCaseSimple.WhenFragment<?, ?> whenFragment : expression.getWhenFragments() ) {
|
||||
final Expression resultExpression = (Expression) whenFragment.getResult().accept( this );
|
||||
resolved = TypeHelper.highestPrecedence( resolved, resultExpression.getExpressionType() );
|
||||
|
||||
whenFragments.add(
|
||||
new CaseSimpleExpression.WhenFragment(
|
||||
(Expression) whenFragment.getCheckValue().accept( this ),
|
||||
resultExpression
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( expression.getOtherwise() != null ) {
|
||||
otherwise = (Expression) expression.getOtherwise().accept( this );
|
||||
resolved = TypeHelper.highestPrecedence( resolved, otherwise.getExpressionType() );
|
||||
}
|
||||
}
|
||||
finally {
|
||||
inferrableTypeAccessStack.pop();
|
||||
}
|
||||
|
||||
final CaseSimpleExpression result = new CaseSimpleExpression(
|
||||
resolveMappingExpressable( expression.getNodeType() ),
|
||||
return new CaseSimpleExpression(
|
||||
resolved,
|
||||
(Expression) expression.getFixture().accept( this ),
|
||||
whenFragments,
|
||||
otherwise
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseSearchedExpression visitSearchedCaseExpression(SqmCaseSearched<?> expression) {
|
||||
List<CaseSearchedExpression.WhenFragment> whenFragments = new ArrayList<>( expression.getWhenFragments().size() );
|
||||
for ( SqmCaseSearched.WhenFragment<?> whenFragment : expression.getWhenFragments() ) {
|
||||
whenFragments.add(
|
||||
new CaseSearchedExpression.WhenFragment(
|
||||
(Predicate) whenFragment.getPredicate().accept( this ),
|
||||
visitWithInferredType( whenFragment.getResult(), expression )
|
||||
)
|
||||
);
|
||||
}
|
||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = new ArrayList<>( expression.getWhenFragments().size() );
|
||||
|
||||
final MappingModelExpressable<?> alreadyKnown = creationContext
|
||||
.getDomainModel()
|
||||
.lenientlyResolveMappingExpressable( expression.getNodeType(), getFromClauseIndex()::findTableGroup );
|
||||
MappingModelExpressable<?> resolved = alreadyKnown;
|
||||
|
||||
inferrableTypeAccessStack.push( () -> alreadyKnown );
|
||||
|
||||
Expression otherwise = null;
|
||||
if ( expression.getOtherwise() != null ) {
|
||||
otherwise = visitWithInferredType( expression.getOtherwise(), expression );
|
||||
try {
|
||||
for ( SqmCaseSearched.WhenFragment<?> whenFragment : expression.getWhenFragments() ) {
|
||||
final Predicate whenPredicate = (Predicate) whenFragment.getPredicate().accept( this );
|
||||
final Expression resultExpression = (Expression) whenFragment.getResult().accept( this );
|
||||
resolved = TypeHelper.highestPrecedence( resolved, resultExpression.getExpressionType() );
|
||||
|
||||
whenFragments.add( new CaseSearchedExpression.WhenFragment( whenPredicate, resultExpression ) );
|
||||
}
|
||||
|
||||
if ( expression.getOtherwise() != null ) {
|
||||
otherwise = (Expression) expression.getOtherwise().accept( this );
|
||||
resolved = TypeHelper.highestPrecedence( resolved, otherwise.getExpressionType() );
|
||||
}
|
||||
}
|
||||
finally {
|
||||
inferrableTypeAccessStack.pop();
|
||||
}
|
||||
|
||||
final CaseSearchedExpression result = new CaseSearchedExpression(
|
||||
resolveMappingExpressable( expression.getNodeType() ),
|
||||
whenFragments,
|
||||
otherwise
|
||||
);
|
||||
|
||||
return result;
|
||||
return new CaseSearchedExpression( resolved, whenFragments, otherwise );
|
||||
}
|
||||
|
||||
private <T> T visitWithInferredType(SqmExpression<?> expression, SqmExpression<?> inferred) {
|
||||
private <X> X visitWithInferredType(SqmExpression<?> expression, SqmExpression<?> inferred) {
|
||||
inferrableTypeAccessStack.push( () -> determineValueMapping( inferred ) );
|
||||
try {
|
||||
return (T) expression.accept( this );
|
||||
return (X) expression.accept( this );
|
||||
}
|
||||
finally {
|
||||
inferrableTypeAccessStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private <X> X visitWithLenientInferredType(SqmExpression<?> expression, SqmExpression<?> inferred) {
|
||||
inferrableTypeAccessStack.push(
|
||||
() -> {
|
||||
try {
|
||||
final MappingModelExpressable<?> definedType = creationContext
|
||||
.getDomainModel()
|
||||
.resolveMappingExpressable( expression.getNodeType(), getFromClauseIndex()::findTableGroup );
|
||||
if ( definedType != null ) {
|
||||
return definedType;
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException ignore) {
|
||||
// todo (6.0) : log?
|
||||
}
|
||||
|
||||
try {
|
||||
final MappingModelExpressable<?> definedType = creationContext
|
||||
.getDomainModel()
|
||||
.lenientlyResolveMappingExpressable( inferred.getNodeType(), getFromClauseIndex()::findTableGroup );
|
||||
if ( definedType != null ) {
|
||||
return definedType;
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException ignore) {
|
||||
// todo (6.0) : log?
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
return (X) expression.accept( this );
|
||||
}
|
||||
finally {
|
||||
inferrableTypeAccessStack.pop();
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.query.sqm.sql.internal;
|
||||
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.query.sqm.SqmExpressable;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class TypeHelper {
|
||||
public static MappingModelExpressable<?> highestPrecedence(MappingModelExpressable<?> type1, MappingModelExpressable<?> type2) {
|
||||
if ( type1 == null ) {
|
||||
return type2;
|
||||
}
|
||||
|
||||
if ( type2 == null ) {
|
||||
return type1;
|
||||
}
|
||||
|
||||
if ( type1 instanceof ModelPart ) {
|
||||
return type1;
|
||||
}
|
||||
|
||||
if ( type2 instanceof ModelPart ) {
|
||||
return type2;
|
||||
}
|
||||
|
||||
// todo (6.0) : we probably want a precedence based on generic resolutions such as those based on Serializable
|
||||
|
||||
// todo (6.0) : anything else to consider?
|
||||
|
||||
return type1;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ package org.hibernate.sql.ast.tree.expression;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
|
@ -24,17 +26,17 @@ import org.hibernate.type.BasicType;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CaseSearchedExpression implements Expression, DomainResultProducer {
|
||||
private final BasicType type;
|
||||
private final BasicValuedMapping type;
|
||||
|
||||
private List<WhenFragment> whenFragments = new ArrayList<>();
|
||||
private Expression otherwise;
|
||||
|
||||
public CaseSearchedExpression(MappingModelExpressable type) {
|
||||
this.type = (BasicType) type;
|
||||
this.type = (BasicValuedMapping) type;
|
||||
}
|
||||
|
||||
public CaseSearchedExpression(MappingModelExpressable type, List<WhenFragment> whenFragments, Expression otherwise) {
|
||||
this.type = (BasicType) type;
|
||||
this.type = (BasicValuedMapping) type;
|
||||
this.whenFragments = whenFragments;
|
||||
this.otherwise = otherwise;
|
||||
}
|
||||
|
|
|
@ -17,12 +17,16 @@ import org.hibernate.engine.spi.SessionImplementor;
|
|||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
|
@ -35,6 +39,14 @@ public class CaseStatementTest extends BaseCoreFunctionalTestCase {
|
|||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
private Person() {
|
||||
}
|
||||
|
||||
public Person(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,55 +81,25 @@ public class CaseStatementTest extends BaseCoreFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
public void testSimpleCaseStatementWithParamAllResults() {
|
||||
try ( final SessionImplementor s = (SessionImplementor) openSession() ) {
|
||||
inTransaction(
|
||||
s,
|
||||
session-> {
|
||||
try {
|
||||
s.createQuery( "select case p.name when 'Steve' then :opt1 else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
fail( "was expecting an exception" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertTyping( QueryException.class, e.getCause() );
|
||||
}
|
||||
catch (QueryException expected) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
s,
|
||||
session-> {
|
||||
s.createQuery( "select case p.name when 'Steve' then cast( :opt1 as string ) else cast( :opt2 as string) end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
}
|
||||
);
|
||||
inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "select case p.name when 'Steve' then :opt1 else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
|
||||
inTransaction(
|
||||
s,
|
||||
session -> {
|
||||
try {
|
||||
s.createQuery( "select case p.name when 'Steve' then :opt1 else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
fail( "was expecting an exception" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertTyping( QueryException.class, e.getCause() );
|
||||
}
|
||||
catch (QueryException expected) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
session.createQuery( "select case p.name when 'Steve' then cast( :opt1 as string ) else cast( :opt2 as string) end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
|
||||
session.createQuery( "select case p.name when 'Steve' then :opt1 else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -170,36 +152,39 @@ public class CaseStatementTest extends BaseCoreFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
public void testSearchedCaseStatementWithAllParamResults() {
|
||||
try ( final SessionImplementor s = (SessionImplementor) openSession() ) {
|
||||
inTransaction(
|
||||
s,
|
||||
session-> {
|
||||
try {
|
||||
s.createQuery( "select case when p.name = 'Steve' then :opt1 else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
fail( "was expecting an exception" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertTyping( QueryException.class, e.getCause() );
|
||||
}
|
||||
catch (QueryException expected) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
);
|
||||
inTransaction(
|
||||
(session) -> {
|
||||
session.persist( new Person( 1, "Steve" ) );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
s,
|
||||
session-> {
|
||||
s.createQuery( "select case when p.name = 'Steve' then cast( :opt1 as string) else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
inTransaction(
|
||||
(session) -> {
|
||||
final List list = session.createQuery( "select case when p.name = 'Steve' then :opt1 else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
assertThat( list.size(), is( 1 ) );
|
||||
assertThat( list.get( 0 ), is( "x" ) );
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
inTransaction(
|
||||
(session) -> {
|
||||
final List list = session.createQuery( "select case when p.name = 'Steve' then cast( :opt1 as string) else :opt2 end from Person p" )
|
||||
.setParameter( "opt1", "x" )
|
||||
.setParameter( "opt2", "y" )
|
||||
.list();
|
||||
assertThat( list.size(), is( 1 ) );
|
||||
assertThat( list.get( 0 ), is( "x" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void dropTestData() {
|
||||
inTransaction(
|
||||
(session) -> session.createQuery( "delete Person" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue