HHH-10085 HHH-10143 - Fix issues with Criteria and case expressions

This commit is contained in:
Andrea Boriero 2016-05-09 17:04:06 +01:00 committed by Steve Ebersole
parent 73aa359242
commit 114ea15b30
8 changed files with 96 additions and 67 deletions

View File

@ -28,7 +28,8 @@ import antlr.collections.AST;
* @author Gavin King
* @author Steve Ebersole
*/
public class SearchedCaseNode extends AbstractSelectExpression implements SelectExpression {
public class SearchedCaseNode extends AbstractSelectExpression implements SelectExpression, ExpectedTypeAwareNode {
@Override
public Type getDataType() {
// option is used to hold each WHEN/ELSE in turn
@ -58,8 +59,7 @@ public class SearchedCaseNode extends AbstractSelectExpression implements Select
option = option.getNextSibling();
}
throw new QueryException( "Could not determine data type for searched case statement" );
return null;
}
@Override
@ -67,4 +67,27 @@ public class SearchedCaseNode extends AbstractSelectExpression implements Select
ColumnHelper.generateSingleScalarColumn( this, i );
}
@Override
public void setExpectedType(Type expectedType) {
AST option = getFirstChild();
while ( option != null ) {
if ( option.getType() == HqlSqlTokenTypes.WHEN ) {
if ( ParameterNode.class.isAssignableFrom( option.getFirstChild().getNextSibling().getClass() ) ) {
((ParameterNode) option.getFirstChild().getNextSibling()).setExpectedType( expectedType );
}
}
else if ( option.getType() == HqlSqlTokenTypes.ELSE ) {
if ( ParameterNode.class.isAssignableFrom( option.getFirstChild().getClass() ) ) {
((ParameterNode) option.getFirstChild()).setExpectedType( expectedType );
}
}
option = option.getNextSibling();
}
}
@Override
public Type getExpectedType() {
return null;
}
}

View File

@ -155,7 +155,7 @@ public class SelectClause extends SelectExpressionList {
Type type = selectExpression.getDataType();
if ( type == null ) {
throw new IllegalStateException(
throw new QueryException(
"No data type for node: " + selectExpression.getClass().getName() + " "
+ new ASTPrinter( SqlTokenTypes.class ).showAsString( (AST) selectExpression, "" )
);

View File

@ -27,8 +27,8 @@ import antlr.collections.AST;
* @author Gavin King
* @author Steve Ebersole
*/
public class SimpleCaseNode extends AbstractSelectExpression implements SelectExpression {
public class SimpleCaseNode extends AbstractSelectExpression implements SelectExpression, ExpectedTypeAwareNode {
public Type getDataType() {
final AST expression = getFirstChild();
// option is used to hold each WHEN/ELSE in turn
@ -58,12 +58,33 @@ public class SimpleCaseNode extends AbstractSelectExpression implements SelectEx
option = option.getNextSibling();
}
throw new QueryException( "Could not determine data type for simple case statement" );
return null;
}
public void setScalarColumnText(int i) throws SemanticException {
ColumnHelper.generateSingleScalarColumn( this, i );
}
@Override
public void setExpectedType(Type expectedType) {
AST option = getFirstChild();
while ( option != null ) {
if ( option.getType() == HqlSqlTokenTypes.WHEN ) {
if ( ParameterNode.class.isAssignableFrom( option.getFirstChild().getNextSibling().getClass() ) ) {
((ParameterNode) option.getFirstChild().getNextSibling()).setExpectedType( expectedType );
}
}
else if ( option.getType() == HqlSqlTokenTypes.ELSE ) {
if ( ParameterNode.class.isAssignableFrom( option.getFirstChild().getClass() ) ) {
((ParameterNode) option.getFirstChild()).setExpectedType( expectedType );
}
}
option = option.getNextSibling();
}
}
@Override
public Type getExpectedType() {
return null;
}
}

View File

@ -1,38 +0,0 @@
/*
* 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.criteria.internal.expression;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.expression.function.CastFunction;
/**
* @author Andrea Boriero
*/
public class CaseLiteralExpression<T> extends LiteralExpression<T> {
public CaseLiteralExpression(CriteriaBuilderImpl criteriaBuilder, Class<T> type, T literal) {
super( criteriaBuilder, type, literal );
}
@Override
public String render(RenderingContext renderingContext) {
// There's no need to cast a boolean value and it actually breaks on
// MySQL and MariaDB because they don't support casting to bit.
// Skip the cast for a boolean literal.
if ( getJavaType() == Boolean.class && Boolean.class.isInstance( getLiteral() ) ) {
return super.render( renderingContext );
}
// wrapping the result in a cast to determine the node type during the antlr hql parsing phase
return CastFunction.CAST_NAME + '(' +
super.render( renderingContext ) +
" as " +
renderingContext.getCastType( getJavaType() ) +
')';
}
}

View File

@ -49,9 +49,6 @@ public class LiteralExpression<T> extends ExpressionImpl<T> implements Serializa
if ( ValueHandlerFactory.isNumeric( literal ) ) {
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal );
}
else if ( ValueHandlerFactory.isBoolean( literal ) ) {
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal );
}
// else...
final String parameterName = renderingContext.registerLiteralParameterBinding( getLiteral(), getJavaType() );

View File

@ -9,6 +9,7 @@ package org.hibernate.query.criteria.internal.expression;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import javax.persistence.criteria.CriteriaBuilder.Case;
import javax.persistence.criteria.Expression;
@ -70,7 +71,7 @@ public class SearchedCaseExpression<R>
final Class<R> type = result != null
? (Class<R>) result.getClass()
: getJavaType();
return new CaseLiteralExpression<R>( criteriaBuilder(), type, result );
return new LiteralExpression<R>( criteriaBuilder(), type, result );
}
public Case<R> when(Expression<Boolean> condition, Expression<? extends R> result) {
@ -114,20 +115,33 @@ public class SearchedCaseExpression<R>
}
public String render(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.render( context )
);
}
public String renderProjection(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.renderProjection( context )
);
}
private String render(
RenderingContext renderingContext,
BiFunction<Renderable, RenderingContext, String> formatter) {
StringBuilder caseStatement = new StringBuilder( "case" );
for ( WhenClause whenClause : getWhenClauses() ) {
caseStatement.append( " when " )
.append( ( (Renderable) whenClause.getCondition() ).render( renderingContext ) )
.append( formatter.apply( (Renderable) whenClause.getCondition(), renderingContext ) )
.append( " then " )
.append( ( (Renderable) whenClause.getResult() ).render( renderingContext ) );
.append( formatter.apply( ((Renderable) whenClause.getResult()), renderingContext ) );
}
caseStatement.append( " else " )
.append( ( (Renderable) getOtherwiseResult() ).render( renderingContext ) )
.append( formatter.apply( (Renderable) getOtherwiseResult(), renderingContext ) )
.append( " end" );
return caseStatement.toString();
}
public String renderProjection(RenderingContext renderingContext) {
return render( renderingContext );
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.query.criteria.internal.expression;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import javax.persistence.criteria.CriteriaBuilder.SimpleCase;
import javax.persistence.criteria.Expression;
@ -78,7 +79,7 @@ public class SimpleCaseExpression<C,R>
final Class<R> type = result != null
? (Class<R>) result.getClass()
: getJavaType();
return new CaseLiteralExpression<R>( criteriaBuilder(), type, result );
return new LiteralExpression<R>( criteriaBuilder(), type, result );
}
public SimpleCase<C, R> when(C condition, Expression<? extends R> result) {
@ -125,22 +126,35 @@ public class SimpleCaseExpression<C,R>
}
public String render(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.render( context )
);
}
public String renderProjection(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.renderProjection( context )
);
}
private String render(
RenderingContext renderingContext,
BiFunction<Renderable, RenderingContext, String> formatter) {
StringBuilder caseExpr = new StringBuilder();
caseExpr.append( "case " )
.append( ( (Renderable) getExpression() ).render( renderingContext ) );
.append( formatter.apply( (Renderable) getExpression(), renderingContext ) );
for ( WhenClause whenClause : getWhenClauses() ) {
caseExpr.append( " when " )
.append( whenClause.getCondition().render( renderingContext ) )
.append( " then " )
.append( ( (Renderable) whenClause.getResult() ).render( renderingContext ) );
.append( formatter.apply( whenClause.getCondition(), renderingContext ) )
.append( " then " )
.append( formatter.apply( (Renderable) whenClause.getResult(), renderingContext ) );
}
caseExpr.append( " else " )
.append( ( (Renderable) getOtherwiseResult() ).render( renderingContext ) )
.append( formatter.apply( (Renderable) getOtherwiseResult(), renderingContext ) )
.append( " end" );
return caseExpr.toString();
}
public String renderProjection(RenderingContext renderingContext) {
return render( renderingContext );
}
}

View File

@ -50,7 +50,6 @@ public class SelectCaseTest extends BaseEntityManagerFunctionalTestCase {
}
@Test
@RequiresDialect(value = H2Dialect.class, jiraKey = "HHH-10143")
public void selectCaseWithValuesShouldWork() {
EntityManager entityManager = getOrCreateEntityManager();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
@ -86,7 +85,6 @@ public class SelectCaseTest extends BaseEntityManagerFunctionalTestCase {
}
@Test
@RequiresDialect(value = H2Dialect.class, jiraKey = "HHH-10143")
public void simpleSelectCaseWithValuesShouldWork() {
EntityManager entityManager = getOrCreateEntityManager();