HHH-13001 - NPE rendering nested criteria expressions

This commit is contained in:
Steve Ebersole 2018-10-15 21:52:17 -05:00
parent 23c6565f72
commit a2b26b81b3
23 changed files with 616 additions and 252 deletions

View File

@ -0,0 +1,65 @@
/*
* 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.internal.util.collections;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @author Steve Ebersole
*/
public class SingletonStack<T> implements Stack<T> {
private final T instance;
public SingletonStack(T instance) {
this.instance = instance;
}
@Override
public void push(T newCurrent) {
throw new UnsupportedOperationException( "Cannot push to a singleton Stack" );
}
@Override
public T pop() {
throw new UnsupportedOperationException( "Cannot pop from a singleton Stack" );
}
@Override
public T getCurrent() {
return instance;
}
@Override
public T getPrevious() {
return null;
}
@Override
public int depth() {
return 1;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void clear() {
}
@Override
public void visitCurrentFirst(Consumer<T> action) {
action.accept( instance );
}
@Override
public <X> X findCurrentFirst(Function<T, X> action) {
return action.apply( instance );
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.internal.util.collections;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Stack implementation exposing useful methods for Hibernate needs.
*
* @param <T> The type of things stored in the stack
*
* @author Steve Ebersole
*/
public interface Stack<T> {
/**
* Push the new element on the top of the stack
*/
void push(T newCurrent);
/**
* Pop (remove and return) the current element off the stack
*/
T pop();
/**
* The element currently at the top of the stack
*/
T getCurrent();
/**
* The element previously at the top of the stack before the current one
*/
T getPrevious();
/**
* How many elements are currently on the stack?
*/
int depth();
/**
* Are there no elements currently in the stack?
*/
boolean isEmpty();
/**
* Remmove all elements from the stack
*/
void clear();
/**
* Visit all elements in the stack, starting with the current and working back
*/
void visitCurrentFirst(Consumer<T> action);
/**
* Find an element on the stack and return a value. The first non-null element
* returned from `action` stops the iteration and is returned from here
*/
<X> X findCurrentFirst(Function<T, X> action);
}

View File

@ -0,0 +1,83 @@
/*
* 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.internal.util.collections;
import java.util.LinkedList;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* A general-purpose stack impl.
*
* @param <T> The type of things stored in the stack
*
* @author Steve Ebersole
*/
public class StandardStack<T> implements Stack<T> {
private LinkedList<T> internalStack = new LinkedList<>();
public StandardStack() {
}
public StandardStack(T initial) {
internalStack.add( initial );
}
@Override
public void push(T newCurrent) {
internalStack.addFirst( newCurrent );
}
@Override
public T pop() {
return internalStack.removeFirst();
}
@Override
public T getCurrent() {
return internalStack.peek();
}
@Override
public T getPrevious() {
if ( internalStack.size() < 2 ) {
return null;
}
return internalStack.get( internalStack.size() - 2 );
}
@Override
public int depth() {
return internalStack.size();
}
@Override
public boolean isEmpty() {
return internalStack.isEmpty();
}
@Override
public void clear() {
internalStack.clear();
}
@Override
public void visitCurrentFirst(Consumer<T> action) {
internalStack.forEach( action );
}
@Override
public <X> X findCurrentFirst(Function<T, X> function) {
for ( T t : internalStack ) {
final X result = function.apply( t );
if ( result != null ) {
return result;
}
}
return null;
}
}

View File

@ -25,6 +25,7 @@ import org.hibernate.query.criteria.internal.compile.InterpretedParameterMetadat
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.path.RootImpl;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.sql.ast.Clause;
/**
* Base class for commonality between {@link javax.persistence.criteria.CriteriaUpdate} and
@ -155,9 +156,17 @@ public abstract class AbstractManipulationCriteriaQuery<T> implements Compilable
}
protected void renderRestrictions(StringBuilder jpaql, RenderingContext renderingContext) {
if ( getRestriction() != null) {
if ( getRestriction() == null ) {
return;
}
renderingContext.getClauseStack().push( Clause.WHERE );
try {
jpaql.append( " where " )
.append( ( (Renderable) getRestriction() ).render( renderingContext ) );
}
finally {
renderingContext.getClauseStack().pop();
}
}
}

View File

@ -33,6 +33,7 @@ import org.hibernate.query.criteria.internal.compile.ImplicitParameterBinding;
import org.hibernate.query.criteria.internal.compile.InterpretedParameterMetadata;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.sql.ast.Clause;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
@ -291,16 +292,7 @@ public class CriteriaQueryImpl<T> extends AbstractNode implements CriteriaQuery<
queryStructure.render( jpaqlBuffer, renderingContext );
if ( ! getOrderList().isEmpty() ) {
jpaqlBuffer.append( " order by " );
String sep = "";
for ( Order orderSpec : getOrderList() ) {
jpaqlBuffer.append( sep )
.append( ( ( Renderable ) orderSpec.getExpression() ).render( renderingContext ) )
.append( orderSpec.isAscending() ? " asc" : " desc" );
sep = ", ";
}
}
renderOrderByClause( renderingContext, jpaqlBuffer );
final String jpaqlString = jpaqlBuffer.toString();
@ -385,4 +377,25 @@ public class CriteriaQueryImpl<T> extends AbstractNode implements CriteriaQuery<
}
};
}
protected void renderOrderByClause(RenderingContext renderingContext, StringBuilder jpaqlBuffer) {
if ( getOrderList().isEmpty() ) {
return;
}
renderingContext.getClauseStack().push( Clause.ORDER );
try {
jpaqlBuffer.append( " order by " );
String sep = "";
for ( Order orderSpec : getOrderList() ) {
jpaqlBuffer.append( sep )
.append( ( (Renderable) orderSpec.getExpression() ).render( renderingContext ) )
.append( orderSpec.isAscending() ? " asc" : " desc" );
sep = ", ";
}
}
finally {
renderingContext.getClauseStack().pop();
}
}
}

View File

@ -27,6 +27,7 @@ import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.expression.DelegatedExpressionImpl;
import org.hibernate.query.criteria.internal.expression.ExpressionImpl;
import org.hibernate.query.criteria.internal.path.RootImpl;
import org.hibernate.sql.ast.Clause;
/**
* The Hibernate implementation of the JPA {@link Subquery} contract. Mostlty a set of delegation to its internal
@ -257,14 +258,13 @@ public class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
@Override
public String render(RenderingContext renderingContext) {
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
throw new IllegalStateException( "Subquery cannot occur in select clause" );
}
StringBuilder subqueryBuffer = new StringBuilder( "(" );
queryStructure.render( subqueryBuffer, renderingContext );
subqueryBuffer.append( ')' );
return subqueryBuffer.toString();
}
@Override
public String renderProjection(RenderingContext renderingContext) {
throw new IllegalStateException( "Subquery cannot occur in select clause" );
}
}

View File

@ -16,6 +16,7 @@ import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.path.SingularAttributePath;
import org.hibernate.sql.ast.Clause;
/**
* Hibernate implementation of the JPA 2.1 {@link CriteriaUpdate} contract.
@ -122,10 +123,13 @@ public class CriteriaUpdateImpl<T> extends AbstractManipulationCriteriaQuery<T>
}
private void renderAssignments(StringBuilder jpaql, RenderingContext renderingContext) {
renderingContext.getClauseStack().push( Clause.UPDATE );
try {
jpaql.append( " set " );
boolean first = true;
for ( Assignment assignment : assignments ) {
if ( ! first ) {
if ( !first ) {
jpaql.append( ", " );
}
jpaql.append( assignment.attributePath.render( renderingContext ) )
@ -134,6 +138,10 @@ public class CriteriaUpdateImpl<T> extends AbstractManipulationCriteriaQuery<T>
first = false;
}
}
finally {
renderingContext.getClauseStack().pop();
}
}
private static class Assignment<A> {
private final SingularAttributePath<A> attributePath;

View File

@ -31,6 +31,7 @@ import javax.persistence.metamodel.EntityType;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.path.RootImpl;
import org.hibernate.query.criteria.internal.path.RootImpl.TreatedRoot;
import org.hibernate.sql.ast.Clause;
/**
* Models basic query structure. Used as a delegate in implementing both
@ -230,37 +231,34 @@ public class QueryStructure<T> implements Serializable {
@SuppressWarnings({ "unchecked" })
public void render(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
jpaqlQuery.append( "select " );
if ( isDistinct() ) {
jpaqlQuery.append( "distinct " );
}
if ( getSelection() == null ) {
jpaqlQuery.append( locateImplicitSelection().renderProjection( renderingContext ) );
}
else {
jpaqlQuery.append( ( (Renderable) getSelection() ).renderProjection( renderingContext ) );
}
renderSelectClause( jpaqlQuery, renderingContext );
renderFromClause( jpaqlQuery, renderingContext );
if ( getRestriction() != null) {
jpaqlQuery.append( " where " )
.append( ( (Renderable) getRestriction() ).render( renderingContext ) );
renderWhereClause( jpaqlQuery, renderingContext );
renderGroupByClause( jpaqlQuery, renderingContext );
}
if ( ! getGroupings().isEmpty() ) {
jpaqlQuery.append( " group by " );
String sep = "";
for ( Expression grouping : getGroupings() ) {
jpaqlQuery.append( sep )
.append( ( (Renderable) grouping ).renderGroupBy( renderingContext ) );
sep = ", ";
protected void renderSelectClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
renderingContext.getClauseStack().push( Clause.SELECT );
try {
jpaqlQuery.append( "select " );
if ( isDistinct() ) {
jpaqlQuery.append( "distinct " );
}
if ( getHaving() != null ) {
jpaqlQuery.append( " having " )
.append( ( (Renderable) getHaving() ).render( renderingContext ) );
if ( getSelection() == null ) {
jpaqlQuery.append( locateImplicitSelection().render( renderingContext ) );
}
else {
jpaqlQuery.append( ( (Renderable) getSelection() ).render( renderingContext ) );
}
}
finally {
renderingContext.getClauseStack().pop();
}
}
@ -290,19 +288,22 @@ public class QueryStructure<T> implements Serializable {
@SuppressWarnings({ "unchecked" })
private void renderFromClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
renderingContext.getClauseStack().push( Clause.FROM );
try {
jpaqlQuery.append( " from " );
String sep = "";
for ( Root root : getRoots() ) {
( (FromImplementor) root ).prepareAlias( renderingContext );
jpaqlQuery.append( sep );
jpaqlQuery.append( ( (FromImplementor) root ).renderTableExpression( renderingContext ) );
sep = ", ";
jpaqlQuery.append( ( (FromImplementor) root ).renderTableExpression( renderingContext ) );
}
for ( Root root : getRoots() ) {
renderJoins( jpaqlQuery, renderingContext, root.getJoins() );
if (root instanceof RootImpl) {
Set<TreatedRoot> treats = ((RootImpl)root).getTreats();
if ( root instanceof RootImpl ) {
Set<TreatedRoot> treats = ( (RootImpl) root ).getTreats();
for ( TreatedRoot treat : treats ) {
renderJoins( jpaqlQuery, renderingContext, treat.getJoins() );
}
@ -312,11 +313,11 @@ public class QueryStructure<T> implements Serializable {
if ( isSubQuery ) {
if ( correlationRoots != null ) {
for ( FromImplementor<?,?> correlationRoot : correlationRoots ) {
for ( FromImplementor<?, ?> correlationRoot : correlationRoots ) {
final FromImplementor correlationParent = correlationRoot.getCorrelationParent();
correlationParent.prepareAlias( renderingContext );
final String correlationRootAlias = correlationParent.getAlias();
for ( Join<?,?> correlationJoin : correlationRoot.getJoins() ) {
for ( Join<?, ?> correlationJoin : correlationRoot.getJoins() ) {
final JoinImplementor correlationJoinImpl = (JoinImplementor) correlationJoin;
// IMPL NOTE: reuse the sep from above!
jpaqlQuery.append( sep );
@ -333,6 +334,61 @@ public class QueryStructure<T> implements Serializable {
}
}
}
finally {
renderingContext.getClauseStack().pop();
}
}
protected void renderWhereClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
if ( getRestriction() == null ) {
return;
}
renderingContext.getClauseStack().push( Clause.WHERE );
try {
jpaqlQuery.append( " where " )
.append( ( (Renderable) getRestriction() ).render( renderingContext ) );
}
finally {
renderingContext.getClauseStack().pop();
}
}
protected void renderGroupByClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
if ( getGroupings().isEmpty() ) {
return;
}
renderingContext.getClauseStack().push( Clause.GROUP );
try {
jpaqlQuery.append( " group by " );
String sep = "";
for ( Expression grouping : getGroupings() ) {
jpaqlQuery.append( sep )
.append( ( (Renderable) grouping ).render( renderingContext ) );
sep = ", ";
}
renderHavingClause( jpaqlQuery, renderingContext );
}
finally {
renderingContext.getClauseStack().pop();
}
}
private void renderHavingClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
if ( getHaving() == null ) {
return;
}
renderingContext.getClauseStack().push( Clause.HAVING );
try {
jpaqlQuery.append( " having " ).append( ( (Renderable) getHaving() ).render( renderingContext ) );
}
finally {
renderingContext.getClauseStack().pop();
}
}
@SuppressWarnings({ "unchecked" })
private void renderJoins(

View File

@ -10,38 +10,15 @@ package org.hibernate.query.criteria.internal;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
/**
* TODO : javadoc
* Contract for nodes in the JPA Criteria tree that can be rendered
* as part of criteria "compilation"
*
* @author Steve Ebersole
*/
public interface Renderable {
/**
* Render clause
*
* @param renderingContext context
* @return rendered expression
* Perform the rendering, returning the rendition
*/
String render(RenderingContext renderingContext);
/**
* Render SELECT clause
*
* @param renderingContext context
* @return rendered expression
*/
default String renderProjection(RenderingContext renderingContext) {
return render( renderingContext );
}
/**
* Render GROUP BY clause
*
* @param renderingContext context
*
* @return rendered expression
*/
default String renderGroupBy(RenderingContext renderingContext) {
return render( renderingContext );
}
}

View File

@ -20,8 +20,12 @@ import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.query.criteria.internal.expression.function.FunctionExpression;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.sql.ast.Clause;
import org.hibernate.type.Type;
/**
@ -63,6 +67,9 @@ public class CriteriaCompiler implements Serializable {
private int aliasCount;
private int explicitParameterCount;
private final Stack<Clause> clauseStack = new StandardStack<>();
private final Stack<FunctionExpression> functionContextStack = new StandardStack<>();
public String generateAlias() {
return "generatedAlias" + aliasCount++;
}
@ -71,6 +78,16 @@ public class CriteriaCompiler implements Serializable {
return "param" + explicitParameterCount++;
}
@Override
public Stack<Clause> getClauseStack() {
return clauseStack;
}
@Override
public Stack<FunctionExpression> getFunctionStack() {
return functionContextStack;
}
@Override
@SuppressWarnings("unchecked")
public ExplicitParameterInfo registerExplicitParameter(ParameterExpression<?> criteriaQueryParameter) {

View File

@ -9,7 +9,10 @@ package org.hibernate.query.criteria.internal.compile;
import javax.persistence.criteria.ParameterExpression;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.query.criteria.internal.expression.function.FunctionExpression;
import org.hibernate.sql.ast.Clause;
/**
* Used to provide a context and services to the rendering.
@ -67,4 +70,8 @@ public interface RenderingContext {
default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return LiteralHandlingMode.AUTO;
}
Stack<Clause> getClauseStack();
Stack<FunctionExpression> getFunctionStack();
}

View File

@ -77,15 +77,17 @@ public class CompoundSelectionImpl<X>
if ( isConstructor ) {
buff.append( "new " ).append( getJavaType().getName() ).append( '(' );
}
String sep = "";
for ( Selection selection : selectionItems ) {
buff.append( sep )
.append( ( (Renderable) selection ).renderProjection( renderingContext ) );
buff.append( sep ).append( ( (Renderable) selection ).render( renderingContext ) );
sep = ", ";
}
if ( isConstructor ) {
buff.append( ')' );
}
return buff.toString();
}
}

View File

@ -47,37 +47,54 @@ public class LiteralExpression<T> extends ExpressionImpl<T> implements Serializa
@SuppressWarnings({ "unchecked" })
public String render(RenderingContext renderingContext) {
switch ( renderingContext.getClauseStack().getCurrent() ) {
case SELECT: {
return renderProjection();
}
case GROUP: {
// technically a literal in the group-by clause
// would be a reference to the position of a selection
//
// but this is what the code used to do...
return renderProjection();
}
default: {
return normalRender( renderingContext );
}
}
}
@SuppressWarnings("unchecked")
private String normalRender(RenderingContext renderingContext) {
LiteralHandlingMode literalHandlingMode = renderingContext.getCriteriaLiteralHandlingMode();
switch ( literalHandlingMode ) {
case AUTO:
case AUTO: {
if ( ValueHandlerFactory.isNumeric( literal ) ) {
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal );
}
else {
return bindLiteral( renderingContext );
}
case BIND:
}
case BIND: {
return bindLiteral( renderingContext );
case INLINE:
}
case INLINE: {
Object literalValue = literal;
if ( String.class.equals( literal.getClass() ) ) {
literalValue = renderingContext.getDialect().inlineLiteral( (String) literal );
}
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literalValue );
default:
}
default: {
throw new IllegalArgumentException( "Unexpected LiteralHandlingMode: " + literalHandlingMode );
}
}
private String bindLiteral(RenderingContext renderingContext) {
final String parameterName = renderingContext.registerLiteralParameterBinding( getLiteral(), getJavaType() );
return ':' + parameterName;
}
@SuppressWarnings({ "unchecked" })
public String renderProjection(RenderingContext renderingContext) {
private String renderProjection() {
// some drivers/servers do not like parameters in the select clause
final ValueHandlerFactory.ValueHandler handler =
ValueHandlerFactory.determineAppropriateHandler( literal.getClass() );
@ -89,9 +106,9 @@ public class LiteralExpression<T> extends ExpressionImpl<T> implements Serializa
}
}
@Override
public String renderGroupBy(RenderingContext renderingContext) {
return renderProjection( renderingContext );
private String bindLiteral(RenderingContext renderingContext) {
final String parameterName = renderingContext.registerLiteralParameterBinding( getLiteral(), getJavaType() );
return ':' + parameterName;
}
@Override

View File

@ -16,6 +16,7 @@ import org.hibernate.query.criteria.internal.ParameterRegistry;
import org.hibernate.query.criteria.internal.PathImplementor;
import org.hibernate.query.criteria.internal.Renderable;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.sql.ast.Clause;
/**
* TODO : javadoc
@ -48,17 +49,17 @@ public class MapEntryExpression<K,V>
}
public String render(RenderingContext renderingContext) {
// don't think this is valid outside of select clause...
throw new IllegalStateException( "illegal reference to map entry outside of select clause." );
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
return "entry(" + path( renderingContext ) + ")";
}
public String renderProjection(RenderingContext renderingContext) {
return "entry(" + path( renderingContext ) + ")";
// don't think this is valid outside of select clause...
throw new IllegalStateException( "illegal reference to map entry outside of select clause." );
}
private String path(RenderingContext renderingContext) {
return origin.getPathIdentifier()
+ '.'
+ ( (Renderable) getAttribute() ).renderProjection( renderingContext );
+ ( (Renderable) getAttribute() ).render( renderingContext );
}
}

View File

@ -12,6 +12,7 @@ import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.ParameterRegistry;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.expression.function.CastFunction;
import org.hibernate.sql.ast.Clause;
/**
* Represents a <tt>NULL</tt>literal expression.
@ -28,10 +29,13 @@ public class NullLiteralExpression<T> extends ExpressionImpl<T> implements Seria
}
public String render(RenderingContext renderingContext) {
return "null";
}
public String renderProjection(RenderingContext renderingContext) {
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
// in the select clause render the ``null` using a cast so the db analyzer/optimizer
// understands the type
return CastFunction.CAST_NAME + "( null as " + renderingContext.getCastType( getJavaType() ) + ')';
}
// otherwise, just render `null`
return "null";
}
}

View File

@ -106,41 +106,18 @@ 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 )
);
}
@Override
public String renderGroupBy(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.renderGroupBy( context )
);
}
private String render(
RenderingContext renderingContext,
BiFunction<Renderable, RenderingContext, String> formatter) {
StringBuilder caseStatement = new StringBuilder( "case" );
for ( WhenClause whenClause : getWhenClauses() ) {
caseStatement.append( " when " )
.append( formatter.apply( (Renderable) whenClause.getCondition(), renderingContext ) )
.append( ( (Renderable) whenClause.getCondition() ).render( renderingContext ) )
.append( " then " )
.append( formatter.apply( ((Renderable) whenClause.getResult()), renderingContext ) );
}
caseStatement.append( " else " )
.append( formatter.apply( (Renderable) getOtherwiseResult(), renderingContext ) )
.append( " end" );
return caseStatement.toString();
.append( ( (Renderable) whenClause.getResult() ).render( renderingContext ) );
}
caseStatement.append( " else " )
.append( ( (Renderable) getOtherwiseResult() ).render( renderingContext ) )
.append( " end" );
return caseStatement.toString();
}
}

View File

@ -9,7 +9,6 @@ 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;
@ -118,44 +117,21 @@ public class SimpleCaseExpression<C,R>
@Override
public String render(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.render( context )
);
}
@Override
public String renderProjection(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.renderProjection( context )
);
}
@Override
public String renderGroupBy(RenderingContext renderingContext) {
return render(
renderingContext,
(Renderable expression, RenderingContext context) -> expression.renderGroupBy( context )
);
}
private String render(
RenderingContext renderingContext,
BiFunction<Renderable, RenderingContext, String> formatter) {
StringBuilder caseExpr = new StringBuilder();
caseExpr.append( "case " )
.append( formatter.apply( (Renderable) getExpression(), renderingContext ) );
.append( ( (Renderable) getExpression() ).render( renderingContext ) );
for ( WhenClause whenClause : getWhenClauses() ) {
caseExpr.append( " when " )
.append( formatter.apply( whenClause.getCondition(), renderingContext ) )
.append( whenClause.getCondition().render( renderingContext ) )
.append( " then " )
.append( formatter.apply( (Renderable) whenClause.getResult(), renderingContext ) );
}
caseExpr.append( " else " )
.append( formatter.apply( (Renderable) getOtherwiseResult(), renderingContext ) )
.append( " end" );
return caseExpr.toString();
.append( ( (Renderable) whenClause.getResult() ).render( renderingContext ) );
}
caseExpr.append( " else " )
.append( ( (Renderable) getOtherwiseResult() ).render( renderingContext ) )
.append( " end" );
return caseExpr.toString();
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.query.criteria.internal.expression.function;
import java.io.Serializable;
import java.util.Locale;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.ParameterRegistry;
@ -47,10 +48,17 @@ public class CastFunction<T,Y>
@Override
public String render(RenderingContext renderingContext) {
return CAST_NAME + '(' +
castSource.render( renderingContext ) +
" as " +
renderingContext.getCastType( getJavaType() ) +
')';
renderingContext.getFunctionStack().push( this );
try {
return String.format(
Locale.ROOT,
"cast(%s as %s)",
castSource.render( renderingContext ),
renderingContext.getCastType( getJavaType() )
);
}
finally {
renderingContext.getFunctionStack().pop();
}
}
}

View File

@ -86,16 +86,26 @@ public class LocateFunction
@Override
public String render(RenderingContext renderingContext) {
StringBuilder buffer = new StringBuilder();
renderingContext.getFunctionStack().push( this );
try {
final StringBuilder buffer = new StringBuilder();
buffer.append( "locate(" )
.append( ( (Renderable) getPattern() ).render( renderingContext ) )
.append( ',' )
.append( ( (Renderable) getString() ).render( renderingContext ) );
if ( getStart() != null ) {
buffer.append( ',' )
.append( ( (Renderable) getStart() ).render( renderingContext ) );
}
buffer.append( ')' );
return buffer.toString();
}
finally {
renderingContext.getFunctionStack().pop();
}
}
}

View File

@ -93,19 +93,26 @@ public class ParameterizedFunctionExpression<X>
@Override
public String render(RenderingContext renderingContext) {
StringBuilder buffer = new StringBuilder();
renderingContext.getFunctionStack().push( this );
try {
final StringBuilder buffer = new StringBuilder();
if ( isStandardJpaFunction() ) {
buffer.append( getFunctionName() )
.append( "(" );
buffer.append( getFunctionName() ).append( "(" );
}
else {
buffer.append( "function('" )
.append( getFunctionName() )
.append( "', " );
}
renderArguments( buffer, renderingContext );
buffer.append( ')' );
return buffer.toString();
return buffer.append( ')' ).toString();
}
finally {
renderingContext.getFunctionStack().pop();
}
}
protected void renderArguments(StringBuilder buffer, RenderingContext renderingContext) {

View File

@ -92,16 +92,24 @@ public class SubstringFunction
}
public String render(RenderingContext renderingContext) {
StringBuilder buffer = new StringBuilder();
renderingContext.getFunctionStack().push( this );
try {
final StringBuilder buffer = new StringBuilder();
buffer.append( "substring(" )
.append( ( (Renderable) getValue() ).render( renderingContext ) )
.append( ',' )
.append( ( (Renderable) getStart() ).render( renderingContext ) );
if ( getLength() != null ) {
buffer.append( ',' )
.append( ( (Renderable) getLength() ).render( renderingContext ) );
}
buffer.append( ')' );
return buffer.toString();
return buffer.append( ')' ).toString();
}
finally {
renderingContext.getFunctionStack().pop();
}
}
}

View File

@ -102,17 +102,18 @@ public class TrimFunction
@Override
public String render(RenderingContext renderingContext) {
renderingContext.getFunctionStack().push( this );
try {
String renderedTrimChar;
if ( trimCharacter.getClass().isAssignableFrom(
LiteralExpression.class ) ) {
if ( trimCharacter.getClass().isAssignableFrom( LiteralExpression.class ) ) {
// If the character is a literal, treat it as one. A few dialects
// do not support parameters as trim() arguments.
renderedTrimChar = '\'' + ( (LiteralExpression<Character>)
trimCharacter ).getLiteral().toString() + '\'';
}
else {
renderedTrimChar = ( (Renderable) trimCharacter ).render(
renderingContext );
renderedTrimChar = ( (Renderable) trimCharacter ).render( renderingContext );
}
return new StringBuilder()
.append( "trim(" )
@ -124,4 +125,8 @@ public class TrimFunction
.append( ')' )
.toString();
}
finally {
renderingContext.getFunctionStack().pop();
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.sql.ast;
import org.hibernate.Incubating;
/**
* Used to indicate which query clause we are currently processing
*
* @author Steve Ebersole
*/
@Incubating
public enum Clause {
/**
* The insert values clause
*/
INSERT,
/**
* The update set clause
*/
UPDATE,
/**
* Not used in 5.x. Intended for use in 6+ as indicator
* of processing predicates (where clause) that occur in a
* delete
*/
DELETE,
SELECT,
FROM,
WHERE,
GROUP,
HAVING,
ORDER,
LIMIT,
CALL,
/**
* Again, not used in 5.x. Used in 6+
*/
IRRELEVANT
}