All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha3
Show newest version
/*
 * 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.internal;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Tuple;
import javax.persistence.criteria.CollectionJoin;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.criteria.SetJoin;
import javax.persistence.criteria.Subquery;

import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.NullPrecedence;
import org.hibernate.QueryException;
import org.hibernate.SortOrder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
import org.hibernate.query.BinaryArithmeticOperator;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.TrimSpec;
import org.hibernate.query.UnaryArithmeticOperator;
import org.hibernate.query.criteria.JpaCoalesce;
import org.hibernate.query.criteria.JpaCompoundSelection;
import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.produce.function.SqmFunctionTemplate;
import org.hibernate.query.sqm.function.SqmCastTarget;
import org.hibernate.query.sqm.function.SqmDistinct;
import org.hibernate.query.sqm.function.SqmTrimSpecification;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.domain.SqmBagJoin;
import org.hibernate.query.sqm.tree.domain.SqmListJoin;
import org.hibernate.query.sqm.tree.domain.SqmMapJoin;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
import org.hibernate.query.sqm.tree.expression.SqmCaseSimple;
import org.hibernate.query.sqm.tree.expression.SqmCollectionSize;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmTuple;
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
import org.hibernate.query.sqm.function.SqmCoalesce;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.predicate.SqmAndPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBooleanExpressionPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmInListPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmInPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmInSubQueryPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmLikePredicate;
import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation;
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument;
import org.hibernate.query.sqm.tree.select.SqmJpaCompoundSelection;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.query.sqm.function.SqmFunction;
import org.hibernate.type.StandardBasicTypes;

import static java.util.Arrays.asList;
import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType;

/**
 * Acts as a JPA {@link javax.persistence.criteria.CriteriaBuilder} by
 * using SQM nodes as the JPA Criteria nodes
 * 
 * @author Steve Ebersole
 */
public class SqmCriteriaNodeBuilder implements NodeBuilder {
	public static SqmCriteriaNodeBuilder create(SessionFactoryImplementor sf) {
		return new SqmCriteriaNodeBuilder( 
				sf.getQueryEngine(),
				sf.getMetamodel().getJpaMetamodel(),
				sf.getServiceRegistry()
		);
	}

	private final QueryEngine queryEngine;
	private final JpaMetamodel domainModel;
	private final ServiceRegistry serviceRegistry;

	public SqmCriteriaNodeBuilder(
			QueryEngine queryEngine,
			JpaMetamodel domainModel,
			ServiceRegistry serviceRegistry) {
		this.queryEngine = queryEngine;
		this.domainModel = domainModel;
		this.serviceRegistry = serviceRegistry;
	}

	@Override
	public JpaMetamodel getDomainModel() {
		return domainModel;
	}

	@Override
	public ServiceRegistry getServiceRegistry() {
		return serviceRegistry;
	}

	@Override
	public QueryEngine getQueryEngine() {
		return queryEngine;
	}

	public void close() {
		// for potential future use
	}

	@Override
	public SqmSelectStatement createQuery() {
		return new SqmSelectStatement<>( Object.class, this );
	}

	@Override
	public  SqmSelectStatement createQuery(Class resultClass) {
		return new SqmSelectStatement<>( resultClass, this );
	}

	@Override
	public SqmSelectStatement createTupleQuery() {
		return new SqmSelectStatement<>( Tuple.class, this );
	}

	@Override
	public  SqmUpdateStatement createCriteriaUpdate(Class targetEntity) {
		return new SqmUpdateStatement<>( targetEntity, this );
	}

	@Override
	public  SqmDeleteStatement createCriteriaDelete(Class targetEntity) {
		return new SqmDeleteStatement<>( targetEntity, SqmQuerySource.CRITERIA, this );
	}

	@Override
	public  SqmInsertSelectStatement createCriteriaInsertSelect(Class targetEntity) {
		return new SqmInsertSelectStatement<>( targetEntity, this );
	}

	@Override
	public 	 SqmExpression cast(JpaExpression expression, Class castTargetJavaType) {
		//noinspection unchecked
		BasicDomainType type = getTypeConfiguration().standardBasicTypeForJavaType( castTargetJavaType );
		//noinspection unchecked
		return getFunctionTemplate("cast").makeSqmFunctionExpression(
				asList( (SqmTypedNode) expression, new SqmCastTarget<>( type, this ) ),
				type,
				queryEngine
		);
	}

	@Override
	public SqmPredicate wrap(Expression expression) {
		if ( expression instanceof SqmPredicate ) {
			return (SqmPredicate) expression;
		}

		return new SqmBooleanExpressionPredicate( (SqmExpression) expression, this );
	}

	// todo (6.0) : wrapping a non-`SqmPredicate` `Expression` expression should be as easy as an impl
	@Override
	public SqmPredicate wrap(Expression... expressions) {
		SqmPredicate lhs = (SqmPredicate) expressions[0];
		SqmPredicate rhs = (SqmPredicate) expressions[0];
		SqmPredicate predicate = new SqmAndPredicate( lhs, rhs, this );
		if ( expressions.length > 2 ) {
			for ( Expression expression : expressions ) {
				final SqmPredicate newRhs;
				if ( expression instanceof SqmPredicate ) {
					newRhs = (SqmPredicate) expression;
				}
				else {
					//noinspection unchecked
					return new SqmBooleanExpressionPredicate( (SqmExpression) expression, this );
				}

				predicate = new SqmAndPredicate( predicate, newRhs, this );
			}
		}
		return predicate;
	}

	@Override
	public  SqmPath treat(Path path, Class type) {
		//noinspection unchecked
		return ( (SqmPath) path ).treatAs( type );
	}

	@Override
	public  SqmRoot treat(Root root, Class type) {
		//noinspection unchecked
		return ( (SqmRoot) root ).treatAs( type );
	}

	@Override
	public  SqmSingularJoin treat(Join join, Class type) {
		//noinspection unchecked
		return ( (SqmSingularJoin) join ).treatAs( type );
	}

	@Override
	public  SqmBagJoin treat(CollectionJoin join, Class type) {
		//noinspection unchecked
		return ( (SqmBagJoin) join ).treatAs( type );
	}

	@Override
	public  SqmSetJoin treat(SetJoin join, Class type) {
		//noinspection unchecked
		return ( (SqmSetJoin) join ).treatAs( type );
	}

	@Override
	public  SqmListJoin treat(ListJoin join, Class type) {
		//noinspection unchecked
		return ( (SqmListJoin) join ).treatAs( type );
	}

	@Override
	public  SqmMapJoin treat(MapJoin join, Class type) {
		//noinspection unchecked
		return ( (SqmMapJoin) join ).treatAs( type );
	}

	@Override
	public  JpaCompoundSelection construct(Class resultClass, Selection[] arguments) {
		final SqmDynamicInstantiation instantiation;
		if ( List.class.equals( resultClass ) ) {
			instantiation = SqmDynamicInstantiation.forListInstantiation( this );
		}
		else if ( Map.class.equals( resultClass ) ) {
			instantiation = SqmDynamicInstantiation.forMapInstantiation( this );
		}
		else {
			instantiation = SqmDynamicInstantiation.forClassInstantiation( resultClass, this );
		}

		for ( Selection argument : arguments ) {
			//noinspection unchecked
			instantiation.addArgument(
					new SqmDynamicInstantiationArgument(
							(SqmSelectableNode) argument,
							argument.getAlias(),
							this
					)
			);
		}

		//noinspection unchecked
		return instantiation;
	}

	@Override
	public  JpaCompoundSelection construct(Class resultClass, List> arguments) {
		final SqmDynamicInstantiation instantiation;
		if ( List.class.equals( resultClass ) ) {
			instantiation = SqmDynamicInstantiation.forListInstantiation( this );
		}
		else if ( Map.class.equals( resultClass ) ) {
			instantiation = SqmDynamicInstantiation.forMapInstantiation( this );
		}
		else {
			instantiation = SqmDynamicInstantiation.forClassInstantiation( resultClass, this );
		}

		for ( Selection argument : arguments ) {
			//noinspection unchecked
			instantiation.addArgument(
					new SqmDynamicInstantiationArgument(
							(SqmSelectableNode) argument,
							argument.getAlias(),
							this
					)
			);
		}

		//noinspection unchecked
		return instantiation;
	}



	@Override
	public SqmSortSpecification sort(JpaExpression sortExpression, SortOrder sortOrder, NullPrecedence nullPrecedence) {
		return new SqmSortSpecification( (SqmExpression) sortExpression, sortOrder, nullPrecedence );
	}

	@Override
	public SqmSortSpecification sort(JpaExpression sortExpression, SortOrder sortOrder) {
		return new SqmSortSpecification( (SqmExpression) sortExpression, sortOrder );
	}

	@Override
	public SqmSortSpecification sort(JpaExpression sortExpression) {
		return new SqmSortSpecification( (SqmExpression) sortExpression );
	}

	@Override
	public SqmSortSpecification asc(Expression x) {
		return new SqmSortSpecification( (SqmExpression) x, SortOrder.ASCENDING );
	}

	@Override
	public SqmSortSpecification desc(Expression x) {
		return new SqmSortSpecification( (SqmExpression) x, SortOrder.DESCENDING );
	}

	@Override
	public JpaCompoundSelection tuple(Selection[] selections) {
		//noinspection unchecked
		return new SqmJpaCompoundSelection(
				ArrayHelper.toList( selections ),
				getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor( Tuple.class ),
				this
		);
	}

	@Override
	public JpaCompoundSelection tuple(List> selections) {
		//noinspection unchecked
		return new SqmJpaCompoundSelection(
				selections,
				getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor( Tuple.class ),
				this
		);
	}

	@Override
	public  SqmTuple tuple(Class tupleType, JpaExpression... expressions) {
		//noinspection unchecked
		return new SqmTuple(
				(List) asList( expressions ),
//				getTypeConfiguration().standardExpressableTypeForJavaType( tupleType ),
				this
		);
	}

	@Override
	public  SqmTuple tuple(Class tupleType, List> expressions) {
		//noinspection unchecked
		return new SqmTuple(
				(List) expressions,
//				getTypeConfiguration().standardExpressableTypeForJavaType( tupleType ),
				this
		);
	}

	@Override
	public  SqmTuple tuple(DomainType tupleType, JpaExpression... expressions) {
		//noinspection unchecked
		return new SqmTuple(
				(List) asList( expressions ),
				tupleType,
				this
		);
	}

	@Override
	public  SqmTuple tuple(
			DomainType tupleType, List> expressions) {
		//noinspection unchecked
		return new SqmTuple(
				new ArrayList<>((List) expressions),
				tupleType,
				this
		);
	}

	@Override
	public JpaCompoundSelection array(Selection[] selections) {
		//noinspection unchecked
		return new SqmJpaCompoundSelection(
				ArrayHelper.toList( selections ),
				getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor( Object[].class ),
				this
		);
	}

	@Override
	public JpaCompoundSelection array(List> selections) {
		//noinspection unchecked
		return new SqmJpaCompoundSelection(
				selections,
				getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor( Object[].class ),
				this
		);
	}

	@Override
	public  SqmExpression avg(Expression argument) {
		//noinspection unchecked
		return getFunctionTemplate("avg").makeSqmFunctionExpression(
				(SqmTypedNode) argument,
				StandardBasicTypes.DOUBLE,
				queryEngine
		);
	}

	@Override
	public  SqmExpression sum(Expression argument) {
		return getFunctionTemplate("sum").makeSqmFunctionExpression(
				(SqmTypedNode) argument,
				(AllowableFunctionReturnType) ((SqmExpression) argument).getNodeType(),
				queryEngine
		);
	}

	@Override
	public SqmExpression sumAsLong(Expression argument) {
		return cast( sum( argument ), Long.class );
	}

	@Override
	public SqmExpression sumAsDouble(Expression argument) {
		return cast( sum( argument ), Double.class );
	}

	@Override
	public  SqmExpression max(Expression argument) {
		return getFunctionTemplate("max").makeSqmFunctionExpression(
				(SqmTypedNode) argument,
				(AllowableFunctionReturnType) ((SqmExpression) argument).getNodeType(),
				queryEngine
		);
	}

	@Override
	public  SqmExpression min(Expression argument) {
		return getFunctionTemplate("min").makeSqmFunctionExpression(
				(SqmTypedNode) argument,
				(AllowableFunctionReturnType) ((SqmExpression) argument).getNodeType(),
				queryEngine
		);
	}

	@Override
	public > SqmExpression greatest(Expression argument) {
		throw new NotYetImplementedFor6Exception();
	}

	@Override
	public > SqmExpression least(Expression argument) {
		throw new NotYetImplementedFor6Exception();
	}

	@Override
	public SqmExpression count(Expression argument) {
		//noinspection unchecked
		return getFunctionTemplate("count").makeSqmFunctionExpression(
				(SqmTypedNode) argument,
				StandardBasicTypes.LONG,
				queryEngine
		);
	}

	@Override
	public SqmExpression countDistinct(Expression argument) {
		//noinspection unchecked
		return getFunctionTemplate("count").makeSqmFunctionExpression(
				new SqmDistinct<>( (SqmExpression) argument, getQueryEngine().getCriteriaBuilder() ),
				StandardBasicTypes.LONG,
				queryEngine
		);
	}

	@Override
	public  SqmExpression neg(Expression x) {
		final SqmExpression sqmExpression = (SqmExpression) x;
		return new SqmUnaryOperation<>(
				UnaryArithmeticOperator.UNARY_MINUS,
				sqmExpression
		);
	}

	@Override
	public  SqmExpression abs(Expression x) {
		return getFunctionTemplate("abs").makeSqmFunctionExpression(
				(SqmTypedNode) x,
				(AllowableFunctionReturnType) ((SqmExpression) x).getNodeType(),
				queryEngine
		);
	}

	@Override
	public  SqmExpression sum(Expression x, Expression y) {
		return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, (SqmExpression) x, (SqmExpression) y );
	}

	private  SqmExpression createSqmArithmeticNode(
			BinaryArithmeticOperator operator,
			SqmExpression leftHandExpression,
			SqmExpression rightHandExpression) {
		//noinspection unchecked
		return new SqmBinaryArithmetic(
				operator,
				leftHandExpression,
				rightHandExpression,
				getDomainModel().getTypeConfiguration().resolveArithmeticType(
						leftHandExpression.getNodeType(),
						rightHandExpression.getNodeType(),
						operator
				),
				this
		);
	}

	@Override
	public  SqmExpression sum(Expression x, N y) {
		return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, (SqmExpression) x, literal( y ) );
	}

	@Override
	public  SqmExpression sum(N x, Expression y) {
		return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, literal( x ), (SqmExpression) y );
	}

	@Override
	public  SqmExpression prod(Expression x, Expression y) {
		return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, literal( x ), (SqmExpression) y );
	}

	@Override
	public  SqmExpression prod(Expression x, N y) {
		return sum( x, y );
	}

	@Override
	public  SqmExpression prod(N x, Expression y) {
		return sum( x, y );
	}

	@Override
	public  SqmExpression diff(Expression x, Expression y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.SUBTRACT,
				(SqmExpression) x,
				(SqmExpression) y
		);
	}

	@Override
	public  SqmExpression diff(Expression x, N y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.SUBTRACT,
				(SqmExpression) x,
				literal( y )
		);
	}

	@Override
	public  SqmExpression diff(N x, Expression y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.SUBTRACT,
				literal( x ),
				(SqmExpression) y
		);
	}

	@Override
	public SqmExpression quot(Expression x, Expression y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.QUOT,
				(SqmExpression) x,
				(SqmExpression) y
		);
	}

	@Override
	public SqmExpression quot(Expression x, Number y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.QUOT,
				(SqmExpression) x,
				literal( y )
		);
	}

	@Override
	public SqmExpression quot(Number x, Expression y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.QUOT,
				literal( x ),
				(SqmExpression) y
		);
	}

	@Override
	public SqmExpression mod(Expression x, Expression y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.MODULO,
				(SqmExpression) x,
				(SqmExpression) y
		);
	}

	@Override
	public SqmExpression mod(Expression x, Integer y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.MODULO,
				(SqmExpression) x,
				literal( y )
		);
	}

	@Override
	public SqmExpression mod(Integer x, Expression y) {
		return createSqmArithmeticNode(
				BinaryArithmeticOperator.MODULO,
				literal( x ),
				(SqmExpression) y
		);
	}

	@Override
	public SqmExpression sqrt(Expression x) {
		//noinspection unchecked
		return getFunctionTemplate("sqrt").makeSqmFunctionExpression(
				(SqmTypedNode) x,
				(AllowableFunctionReturnType) QueryHelper.highestPrecedenceType2(
						((SqmExpression) x).getNodeType(),
						StandardBasicTypes.DOUBLE
				),
				queryEngine
		);
	}

	@Override
	@SuppressWarnings("unchecked")
	public SqmExpression toLong(Expression number) {
		return ( (SqmExpression) number ).asLong();
	}

	@Override
	@SuppressWarnings("unchecked")
	public SqmExpression toInteger(Expression number) {
		return ( (SqmExpression) number ).asInteger();
	}

	@Override
	@SuppressWarnings("unchecked")
	public SqmExpression toFloat(Expression number) {
		return ( (SqmExpression) number ).asFloat();
	}

	@Override
	@SuppressWarnings("unchecked")
	public SqmExpression toDouble(Expression number) {
		return ( (SqmExpression) number ).asDouble();
	}

	@Override
	@SuppressWarnings("unchecked")
	public SqmExpression toBigDecimal(Expression number) {
		return ( (SqmExpression) number ).asBigDecimal();
	}

	@Override
	@SuppressWarnings("unchecked")
	public SqmExpression toBigInteger(Expression number) {
		return ( (SqmExpression) number ).asBigInteger();
	}

	@Override
	@SuppressWarnings("unchecked")
	public SqmExpression toString(Expression character) {
		return ( (SqmExpression) character ).asString();
	}

	@Override
	@SuppressWarnings("unchecked")
	public  SqmLiteral literal(T value) {
		if ( value == null ) {
			return (SqmLiteral) new SqmLiteralNull( this );
		}

		return new SqmLiteral(
				value,
				getTypeConfiguration().standardBasicTypeForJavaType( value.getClass() ),
				this
		);
	}

	@Override
	public  List> literals(T[] values) {
		if ( values == null || values.length == 0 ) {
			return Collections.emptyList();
		}

		final List> literals = new ArrayList<>();
		for ( T value : values ) {
			literals.add( literal( value ) );
		}
		return literals;
	}

	@Override
	public  List> literals(List values) {
		if ( values == null || values.isEmpty() ) {
			return Collections.emptyList();
		}

		final List> literals = new ArrayList<>();
		for ( T value : values ) {
			literals.add( literal( value ) );
		}
		return literals;
	}

	@Override
	public  SqmExpression nullLiteral(Class resultClass) {
		//noinspection unchecked
		return new SqmLiteralNull( getTypeConfiguration().standardBasicTypeForJavaType( resultClass ), this );
	}

	@Override
	public  JpaCriteriaParameter parameter(Class paramClass) {
		//noinspection unchecked
		return new JpaCriteriaParameter<>(
				getTypeConfiguration().standardBasicTypeForJavaType( paramClass ),
				false,
				this
		);
	}

	@Override
	public  JpaCriteriaParameter parameter(Class paramClass, String name) {
		//noinspection unchecked
		return new JpaCriteriaParameter<>(
				name,
				getTypeConfiguration().standardBasicTypeForJavaType( paramClass ),
				false,
				this
		);
	}

	@Override
	public SqmExpression concat(Expression x, Expression y) {
		final SqmExpression xSqmExpression = (SqmExpression) x;
		final SqmExpression ySqmExpression = (SqmExpression) y;
		//noinspection unchecked
		return getFunctionTemplate( "concat" ).makeSqmFunctionExpression(
				asList( xSqmExpression, ySqmExpression ),
				(AllowableFunctionReturnType) highestPrecedenceType(
						xSqmExpression.getNodeType(),
						ySqmExpression.getNodeType(),
						StandardBasicTypes.STRING
				),
				getQueryEngine()
		);
	}

	@Override
	public SqmExpression concat(Expression x, String y) {
		final SqmExpression xSqmExpression = (SqmExpression) x;
		final SqmExpression ySqmExpression = literal( y );
		//noinspection unchecked
		return getFunctionTemplate( "concat" ).makeSqmFunctionExpression(
				asList( xSqmExpression, ySqmExpression ),
				(AllowableFunctionReturnType) highestPrecedenceType(
						xSqmExpression.getNodeType(),
						ySqmExpression.getNodeType(),
						StandardBasicTypes.STRING
				),
				getQueryEngine()
		);
	}

	@Override
	public SqmExpression concat(String x, Expression y) {
		final SqmExpression xSqmExpression = literal( x );
		final SqmExpression ySqmExpression = (SqmExpression) y;
		//noinspection unchecked
		return getFunctionTemplate( "concat" ).makeSqmFunctionExpression(
				asList( xSqmExpression, ySqmExpression ),
				(AllowableFunctionReturnType) highestPrecedenceType(
						xSqmExpression.getNodeType(),
						ySqmExpression.getNodeType(),
						StandardBasicTypes.STRING
				),
				getQueryEngine()
		);
	}

	@Override
	public SqmExpression concat(String x, String y) {
		final SqmExpression xSqmExpression = literal( x );
		final SqmExpression ySqmExpression = literal( y );
		//noinspection unchecked
		return getFunctionTemplate( "concat" ).makeSqmFunctionExpression(
				asList( xSqmExpression, ySqmExpression ),
				(AllowableFunctionReturnType) highestPrecedenceType(
						xSqmExpression.getNodeType(),
						ySqmExpression.getNodeType(),
						StandardBasicTypes.STRING
				),
				getQueryEngine()
		);
	}

	@Override
	public SqmFunction substring(Expression source, Expression from) {
		return createSubstringNode(
				(SqmExpression) source,
				(SqmExpression) from,
				null
		);
	}

	private SqmFunction createSubstringNode(SqmExpression source, SqmExpression from, SqmExpression len) {
		final AllowableFunctionReturnType resultType = (AllowableFunctionReturnType)  QueryHelper.highestPrecedenceType2(
				source.getNodeType(),
				StandardBasicTypes.STRING
		);

		//noinspection unchecked
		return getFunctionTemplate( "substring" ).makeSqmFunctionExpression(
				len==null ? asList( source, from ) : asList( source, from, len ),
				resultType,
				getQueryEngine()
		);
	}

	@Override
	public SqmFunction substring(Expression source, int from) {
		return createSubstringNode(
				(SqmExpression) source,
				literal( from ),
				null
		);
	}

	@Override
	public SqmFunction substring(Expression source, Expression from, Expression len) {
		return createSubstringNode(
				(SqmExpression) source,
				(SqmExpression) from,
				(SqmExpression) len
		);
	}

	@Override
	public SqmFunction substring(Expression source, int from, int len) {
		return createSubstringNode(
				(SqmExpression) source,
				literal( from ),
				literal( len )
		);
	}

	@Override
	public SqmFunction trim(Expression source) {
		return createTrimNode( null, null, (SqmExpression) source );
	}

	private SqmFunction createTrimNode(TrimSpec trimSpecification, SqmExpression trimCharacter, SqmExpression source) {

		final ArrayList> arguments = new ArrayList<>();
			if ( trimSpecification != null ) {
				arguments.add(
						new SqmTrimSpecification( trimSpecification, this )
				);
			}

			if ( trimCharacter != null ) {
				arguments.add( trimCharacter );
			}

			arguments.add( source );

			//noinspection unchecked
			return getFunctionTemplate( "trim" ).makeSqmFunctionExpression(
					arguments,
					(AllowableFunctionReturnType) QueryHelper.highestPrecedenceType2( source.getNodeType(), StandardBasicTypes.STRING ),
					getQueryEngine()
			);
	}

	@Override
	public SqmFunction trim(Trimspec ts, Expression source) {
		return createTrimNode( convertTrimSpec( ts ), null, (SqmExpression) source );
	}

	private static TrimSpec convertTrimSpec(Trimspec jpaTs) {
		if ( jpaTs == null ) {
			return null;
		}

		switch ( jpaTs ) {
			case BOTH: {
				return TrimSpec.BOTH;
			}
			case LEADING: {
				return TrimSpec.LEADING;
			}
			case TRAILING: {
				return TrimSpec.TRAILING;
			}
		}

		throw new QueryException( "Could not resolve JPA TrimSpec : " + jpaTs );
	}

	@Override
	public SqmFunction trim(Expression trimChar, Expression source) {
		return createTrimNode( null, (SqmExpression) trimChar, (SqmExpression) source );
	}

	@Override
	public SqmFunction trim(Trimspec ts, Expression trimChar, Expression source) {
		return createTrimNode( convertTrimSpec( ts ), (SqmExpression) trimChar, (SqmExpression) source );
	}

	@Override
	public SqmFunction trim(char trimChar, Expression source) {
		return createTrimNode( null, literal( trimChar ), (SqmExpression) source );
	}

	@Override
	public SqmFunction trim(Trimspec ts, char trimChar, Expression source) {
		return createTrimNode( convertTrimSpec( ts ), literal( trimChar ), (SqmExpression) source );
	}

	@Override
	public SqmFunction lower(Expression x) {

		final AllowableFunctionReturnType type = (AllowableFunctionReturnType)  highestPrecedenceType(
				((SqmExpression) x).getNodeType(),
				StandardBasicTypes.STRING
		);

		//noinspection unchecked
		return getFunctionTemplate( "lower" ).makeSqmFunctionExpression(
				(SqmExpression) x,
				type,
				getQueryEngine()
		);
	}

	@Override
	public SqmFunction upper(Expression x) {
		final AllowableFunctionReturnType type = (AllowableFunctionReturnType) highestPrecedenceType(
				((SqmExpression) x).getNodeType(),
				StandardBasicTypes.STRING
		);

		//noinspection unchecked
		return getFunctionTemplate( "upper" ).makeSqmFunctionExpression(
				(SqmExpression) x,
				type,
				getQueryEngine()
		);
	}

	@Override
	public SqmFunction length(Expression argument) {

		//noinspection unchecked
		return getFunctionTemplate( "length" ).makeSqmFunctionExpression(
				(SqmExpression) argument,
				(AllowableFunctionReturnType) highestPrecedenceType(
						((SqmExpression) argument).getNodeType(),
						StandardBasicTypes.INTEGER
				),
				getQueryEngine()
		);
	}

	@Override
	public SqmFunction locate(Expression source, Expression pattern) {
		return createLocateFunctionNode(
				(SqmExpression) source,
				(SqmExpression) pattern,
				null
		);
	}

	private SqmFunction createLocateFunctionNode(
			SqmExpression source,
			SqmExpression pattern,
			SqmExpression startPosition) {
		final AllowableFunctionReturnType type = (AllowableFunctionReturnType) highestPrecedenceType(
				source.getNodeType(),
				StandardBasicTypes.INTEGER
		);

		final List> arguments;
		if ( startPosition == null ) {
			arguments = asList(
					source,
					pattern
			);
		}
		else {
			arguments = asList(
					source,
					pattern
			);
		}

		//noinspection unchecked
		return getFunctionTemplate("locate").makeSqmFunctionExpression(
				arguments,
				type,
				getQueryEngine()
		);

	}

	@Override
	public SqmFunction locate(Expression source, String pattern) {
		return createLocateFunctionNode(
				(SqmExpression) source,
				literal( pattern ),
				null
		);
	}

	@Override
	public SqmFunction locate(Expression source, Expression pattern, Expression startPosition) {
		return createLocateFunctionNode(
				(SqmExpression) source,
				(SqmExpression) pattern,
				(SqmExpression) startPosition
		);
	}

	@Override
	public SqmFunction locate(Expression source, String pattern, int startPosition) {
		return createLocateFunctionNode(
				(SqmExpression) source,
				literal( pattern ),
				literal( startPosition )
		);
	}

	@Override
	public SqmFunction currentDate() {
		//noinspection unchecked
//		return getFunctionTemplate("current_date").makeSqmFunctionExpression(
//				StandardBasicTypes.DATE,
//				queryEngine
//		);

		throw new NotYetImplementedFor6Exception( getClass() );
	}

	@Override
	public SqmFunction currentTimestamp() {
		//noinspection unchecked
//		return getFunctionTemplate("current_timestamp").makeSqmFunctionExpression(
//				StandardBasicTypes.TIMESTAMP,
//				queryEngine
//		);
		throw new NotYetImplementedFor6Exception( getClass() );
	}

	@Override
	public SqmFunction