From e22dc55adb1830d9b059135c2303658cd52dbe6d Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 23 Mar 2021 16:13:46 +0100 Subject: [PATCH] Various fixes * Resolve return type for SUM according to JPA spec * Specify invariant return types for SQRT and MOD as required by the JPA spec * Fix JPA tuple element access support * Fix join management for JPA related methods * Handle optional escape character for like predicate * Implement type inference for result arms of case expressions * Implement min/max element/index functions as sub-query * Implement min/max function support * Implement emptiness, exists and member of predicate for JPA Criteria * Implement size function as sub-query * Implement group by entity alias by using FK key --- .../function/CommonFunctionFactory.java | 98 ++++++++ .../AbstractSharedSessionContract.java | 11 +- .../SingleUniqueKeyEntityLoaderStandard.java | 20 +- .../domain/internal/ListAttributeImpl.java | 2 +- .../domain/internal/MapAttributeImpl.java | 2 +- .../NamedCallableQueryMementoImpl.java | 7 +- .../internal/NamedHqlQueryMementoImpl.java | 3 +- .../query/hql/spi/NamedHqlQueryMemento.java | 3 +- .../query/named/NamedQueryMemento.java | 2 +- .../org/hibernate/query/spi/QueryEngine.java | 10 + .../internal/NamedNativeQueryMementoImpl.java | 19 +- .../sql/spi/NamedNativeQueryMemento.java | 3 +- .../org/hibernate/query/sqm/NodeBuilder.java | 10 +- .../query/sqm/SemanticQueryWalker.java | 12 +- .../internal/ConcreteSqmSelectQueryPlan.java | 16 +- .../query/sqm/internal/QuerySqmImpl.java | 2 +- .../sqm/internal/SqmCriteriaNodeBuilder.java | 72 +++--- .../sqm/internal/SqmMappingModelHelper.java | 15 ++ .../StandardFunctionReturnTypeResolvers.java | 4 +- .../sqm/spi/BaseSemanticQueryWalker.java | 24 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 204 +++++++++++++++-- .../sqm/tree/domain/AbstractSqmFrom.java | 211 +++++++++++------- .../SqmIndexedCollectionAccessPath.java | 5 +- .../sqm/tree/domain/SqmMaxElementPath.java | 2 +- .../sqm/tree/domain/SqmMaxIndexPath.java | 2 +- .../sqm/tree/domain/SqmMinElementPath.java | 2 +- .../sqm/tree/domain/SqmMinIndexPath.java | 2 +- .../tree/select/SqmJpaCompoundSelection.java | 2 +- .../sqm/tree/select/SqmSelectStatement.java | 12 +- .../sql/ast/spi/AbstractSqlAstTranslator.java | 24 +- .../tree/expression/CaseSimpleExpression.java | 23 +- .../internal/RowTransformerJpaTupleImpl.java | 10 +- .../sql/results/internal/TupleImpl.java | 30 +-- .../sql/results/internal/TupleMetadata.java | 58 +++++ .../descriptor/jdbc/JdbcTypeDescriptor.java | 2 +- .../orm/test/query/hql/FunctionTests.java | 2 +- 36 files changed, 681 insertions(+), 245 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleMetadata.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java index bb8161b138..1891b0fa02 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java @@ -6,11 +6,25 @@ */ package org.hibernate.dialect.function; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Types; +import java.util.List; import java.util.Arrays; +import java.util.function.Supplier; + +import org.hibernate.metamodel.mapping.BasicValuedMapping; +import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; +import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.BasicType; import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.spi.TypeConfiguration; import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.useArgType; @@ -1476,7 +1490,87 @@ public class CommonFunctionFactory { .setExactArgumentCount(1) .register(); + final TypeConfiguration typeConfiguration = queryEngine.getTypeConfiguration(); + final BasicType longType = typeConfiguration.getBasicTypeForJavaType( Long.class ); + final BasicType doubleType = typeConfiguration.getBasicTypeForJavaType( Double.class ); + final BasicType bigIntegerType = typeConfiguration.getBasicTypeForJavaType( BigInteger.class ); + final BasicType bigDecimalType = typeConfiguration.getBasicTypeForJavaType( BigDecimal.class ); + // Resolve according to JPA spec 4.8.5 + // SUM returns Long when applied to state fields of integral types (other than BigInteger); + // Double when applied to state fields of floating point types; + // BigInteger when applied to state fields of type BigInteger; + // and BigDecimal when applied to state fields of type BigDecimal. queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("sum") + .setReturnTypeResolver( new FunctionReturnTypeResolver() { + @Override + public AllowableFunctionReturnType resolveFunctionReturnType(AllowableFunctionReturnType impliedType, List> arguments, TypeConfiguration typeConfiguration) { + final AllowableFunctionReturnType argType = StandardFunctionReturnTypeResolvers.extractArgumentType( + arguments, + 1 + ); + final BasicType basicType; + if ( argType instanceof BasicType ) { + basicType = (BasicType) argType; + } + else { + basicType = typeConfiguration.getBasicTypeForJavaType( argType.getJavaType() ); + if ( basicType == null ) { + return impliedType; + } + } + switch ( basicType.getJdbcTypeDescriptor().getJdbcTypeCode() ) { + case Types.SMALLINT: + case Types.TINYINT: + case Types.INTEGER: + case Types.BIGINT: + return longType; + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + return doubleType; + case Types.DECIMAL: + case Types.NUMERIC: + if ( BigInteger.class.isAssignableFrom( basicType.getJavaType() ) ) { + return bigIntegerType; + } + else { + return bigDecimalType; + } + } + // Better use the implied type than throwing an exception + return impliedType; + } + + @Override + public BasicValuedMapping resolveFunctionReturnType(Supplier impliedTypeAccess, List arguments) { + // Resolve according to JPA spec 4.8.5 + final BasicValuedMapping specifiedArgType = StandardFunctionReturnTypeResolvers.extractArgumentValuedMapping( + arguments, + 1 + ); + switch ( specifiedArgType.getJdbcMapping().getJdbcTypeDescriptor().getJdbcTypeCode() ) { + case Types.SMALLINT: + case Types.TINYINT: + case Types.INTEGER: + case Types.BIGINT: + return longType; + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + return doubleType; + case Types.DECIMAL: + case Types.NUMERIC: + if ( BigInteger.class.isAssignableFrom( specifiedArgType.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ) ) { + return bigIntegerType; + } + else { + return bigDecimalType; + } + } + return impliedTypeAccess.get(); + } + + } ) .setExactArgumentCount(1) .register(); @@ -1511,6 +1605,8 @@ public class CommonFunctionFactory { .register(); queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("mod") + // According to JPA spec 4.6.17.2.2. + .setInvariantType( StandardBasicTypes.INTEGER ) .setExactArgumentCount(2) .register(); @@ -1524,6 +1620,8 @@ public class CommonFunctionFactory { .register(); queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("sqrt") + // According to JPA spec 4.6.17.2.2. + .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount(1) .register(); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index d7c11adfda..c8b4bb50b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -793,7 +793,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont .getHqlQueryMemento( queryName ); if ( namedHqlDescriptor != null ) { - HqlQueryImplementor query = namedHqlDescriptor.toQuery( this, resultType ); + HqlQueryImplementor query = namedHqlDescriptor.toQuery( this, resultType ); query.setComment( "dynamic HQL query" ); applyQuerySettingsAndHints( query ); return query; @@ -805,10 +805,13 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont .getNativeQueryMemento( queryName ); if ( namedNativeDescriptor != null ) { - if( resultType == null){ - resultType = (Class) namedNativeDescriptor.getResultMappingClass(); + final NativeQueryImplementor query; + if ( resultType == null) { + query = namedNativeDescriptor.toQuery( this ); + } + else { + query = namedNativeDescriptor.toQuery( this, resultType ); } - NativeQueryImplementor query = namedNativeDescriptor.toQuery( this, resultType ); query.setComment( "dynamic native SQL query" ); applyQuerySettingsAndHints( query ); return query; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java index c62943e995..c93c08202e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.hibernate.HibernateException; import org.hibernate.LockOptions; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; @@ -130,14 +131,19 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEn true ); - int size = list.size(); - assert size <= 1; - if ( size == 0 ) { - return null; + switch ( list.size() ) { + case 0: + return null; + case 1: + //noinspection unchecked + return (T) list.get( 0 ); } - - //noinspection unchecked - return (T) list.get( 0 ); + throw new HibernateException( + "More than one row with the given identifier was found: " + + ukValue + + ", for class: " + + entityDescriptor.getEntityName() + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java index 71060b7a37..a1bbff83ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ListAttributeImpl.java @@ -28,7 +28,7 @@ class ListAttributeImpl extends AbstractPluralAttribute, E> imp super( builder, metadataContext ); //noinspection unchecked - this.indexPathSource = (SqmPathSource) SqmMappingModelHelper.resolveSqmPathSource( + this.indexPathSource = (SqmPathSource) SqmMappingModelHelper.resolveSqmKeyPathSource( getName(), builder.getListIndexOrMapKeyType(), BindableType.PLURAL_ATTRIBUTE diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java index 9662aebac7..b339c397b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MapAttributeImpl.java @@ -29,7 +29,7 @@ class MapAttributeImpl extends AbstractPluralAttribute, V> MapAttributeImpl(PluralAttributeBuilder, V, K> xceBuilder, MetadataContext metadataContext) { super( xceBuilder, metadataContext ); - this.keyPathSource = SqmMappingModelHelper.resolveSqmPathSource( + this.keyPathSource = SqmMappingModelHelper.resolveSqmKeyPathSource( CollectionPart.Nature.INDEX.getName(), xceBuilder.getListIndexOrMapKeyType(), BindableType.PLURAL_ATTRIBUTE diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/NamedCallableQueryMementoImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/NamedCallableQueryMementoImpl.java index 63ae610bdf..b72258de35 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/NamedCallableQueryMementoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/NamedCallableQueryMementoImpl.java @@ -24,6 +24,7 @@ import org.hibernate.procedure.spi.ProcedureParameterImplementor; import org.hibernate.query.named.AbstractNamedQueryMemento; import org.hibernate.query.named.NamedQueryMemento; import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.spi.QueryImplementor; /** * Implementation of NamedCallableQueryMemento @@ -121,7 +122,7 @@ public class NamedCallableQueryMementoImpl extends AbstractNamedQueryMemento imp public ProcedureCall makeProcedureCall( SharedSessionContractImplementor session, String... resultSetMappingNames) { - return new ProcedureCallImpl( session, this, resultSetMappingNames ); + return new ProcedureCallImpl<>( session, this, resultSetMappingNames ); } @Override @@ -132,8 +133,8 @@ public class NamedCallableQueryMementoImpl extends AbstractNamedQueryMemento imp } @Override - public ProcedureCallImplementor toQuery(SharedSessionContractImplementor session) { - return makeProcedureCall( session ); + public QueryImplementor toQuery(SharedSessionContractImplementor session) { + return new ProcedureCallImpl<>( session, this ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java index 340f5550fe..19ea311e6a 100755 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java @@ -17,6 +17,7 @@ import org.hibernate.query.hql.spi.HqlQueryImplementor; import org.hibernate.query.hql.spi.NamedHqlQueryMemento; import org.hibernate.query.named.AbstractNamedQueryMemento; import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sqm.internal.QuerySqmImpl; import org.jboss.logging.Logger; @@ -128,7 +129,7 @@ public class NamedHqlQueryMementoImpl extends AbstractNamedQueryMemento implemen } @Override - public HqlQueryImplementor toQuery(SharedSessionContractImplementor session) { + public QueryImplementor toQuery(SharedSessionContractImplementor session) { return toQuery( session, null ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/NamedHqlQueryMemento.java b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/NamedHqlQueryMemento.java index 388006893b..afc867c794 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/NamedHqlQueryMemento.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/NamedHqlQueryMemento.java @@ -16,6 +16,7 @@ import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl; import org.hibernate.query.named.AbstractNamedQueryMemento; import org.hibernate.query.named.NameableQuery; import org.hibernate.query.named.NamedQueryMemento; +import org.hibernate.query.spi.QueryImplementor; /** * NamedQueryMemento for HQL queries @@ -36,7 +37,7 @@ public interface NamedHqlQueryMemento extends NamedQueryMemento { /** * Convert the memento into an untyped executable query */ - HqlQueryImplementor toQuery(SharedSessionContractImplementor session); + QueryImplementor toQuery(SharedSessionContractImplementor session); Integer getFirstResult(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/named/NamedQueryMemento.java b/hibernate-core/src/main/java/org/hibernate/query/named/NamedQueryMemento.java index eeaeda34e7..c3d25b7a9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/named/NamedQueryMemento.java +++ b/hibernate-core/src/main/java/org/hibernate/query/named/NamedQueryMemento.java @@ -54,7 +54,7 @@ public interface NamedQueryMemento { */ NamedQueryMemento makeCopy(String name); - QueryImplementor toQuery(SharedSessionContractImplementor session); + QueryImplementor toQuery(SharedSessionContractImplementor session); QueryImplementor toQuery(SharedSessionContractImplementor session, Class javaType); interface ParameterMemento { diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java index 5256f35f52..e021767e18 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java @@ -33,6 +33,7 @@ import org.hibernate.query.sqm.sql.SqmTranslatorFactory; import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory; import org.hibernate.service.ServiceRegistry; import org.hibernate.stat.spi.StatisticsImplementor; +import org.hibernate.type.spi.TypeConfiguration; import java.util.Map; import java.util.function.Supplier; @@ -75,6 +76,7 @@ public class QueryEngine { sqmTranslatorFactory, sessionFactory.getServiceRegistry().getService( NativeQueryInterpreter.class ), buildInterpretationCache( sessionFactory::getStatistics, sessionFactory.getProperties() ), + metadata.getTypeConfiguration(), dialect, queryEngineOptions.getCustomSqmFunctionRegistry(), sessionFactory.getServiceRegistry() @@ -88,6 +90,7 @@ public class QueryEngine { private final NativeQueryInterpreter nativeQueryInterpreter; private final QueryInterpretationCache interpretationCache; private final SqmFunctionRegistry sqmFunctionRegistry; + private final TypeConfiguration typeConfiguration; private final int preferredSqlTypeCodeForBoolean; public QueryEngine( @@ -99,6 +102,7 @@ public class QueryEngine { SqmTranslatorFactory sqmTranslatorFactory, NativeQueryInterpreter nativeQueryInterpreter, QueryInterpretationCache interpretationCache, + TypeConfiguration typeConfiguration, Dialect dialect, SqmFunctionRegistry userDefinedRegistry, ServiceRegistry serviceRegistry) { @@ -116,6 +120,7 @@ public class QueryEngine { ); this.sqmFunctionRegistry = new SqmFunctionRegistry(); + this.typeConfiguration = typeConfiguration; this.preferredSqlTypeCodeForBoolean = preferredSqlTypeCodeForBoolean; dialect.initializeFunctionRegistry( this ); if ( userDefinedRegistry != null ) { @@ -151,6 +156,7 @@ public class QueryEngine { this.nativeQueryInterpreter = nativeQueryInterpreter; this.sqmFunctionRegistry = new SqmFunctionRegistry(); + this.typeConfiguration = jpaMetamodel.getTypeConfiguration(); this.preferredSqlTypeCodeForBoolean = preferredSqlTypeCodeForBoolean; dialect.initializeFunctionRegistry( this ); @@ -336,6 +342,10 @@ public class QueryEngine { return sqmFunctionRegistry; } + public TypeConfiguration getTypeConfiguration() { + return typeConfiguration; + } + public void close() { if ( namedObjectRepository != null ) { namedObjectRepository.close(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java index 03e3e98c19..b951b512fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java @@ -14,6 +14,7 @@ import org.hibernate.FlushMode; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.named.AbstractNamedQueryMemento; import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sql.spi.NamedNativeQueryMemento; import org.hibernate.query.sql.spi.NativeQueryImplementor; @@ -26,8 +27,8 @@ import org.hibernate.query.sql.spi.NativeQueryImplementor; public class NamedNativeQueryMementoImpl extends AbstractNamedQueryMemento implements NamedNativeQueryMemento { private final String sqlString; - private String resultSetMappingName; - private Class resultSetMappingClass; + private final String resultSetMappingName; + private final Class resultSetMappingClass; private final Set querySpaces; @@ -59,7 +60,9 @@ public class NamedNativeQueryMementoImpl extends AbstractNamedQueryMemento imple hints ); this.sqlString = sqlString; - this.resultSetMappingName = resultSetMappingName; + this.resultSetMappingName = resultSetMappingName == null || resultSetMappingName.isEmpty() + ? null + : resultSetMappingName; this.resultSetMappingClass = resultSetMappingClass; this.querySpaces = querySpaces; } @@ -117,19 +120,17 @@ public class NamedNativeQueryMementoImpl extends AbstractNamedQueryMemento imple } @Override - public NativeQueryImplementor toQuery(SharedSessionContractImplementor session) { - return new NativeQueryImpl( this, session ); + public NativeQueryImplementor toQuery(SharedSessionContractImplementor session) { + return new NativeQueryImpl<>( this, session ); } @Override - @SuppressWarnings("unchecked") public NativeQueryImplementor toQuery(SharedSessionContractImplementor session, Class resultType) { - return new NativeQueryImpl( this, resultType, session ); + return new NativeQueryImpl<>( this, resultType, session ); } @Override - @SuppressWarnings("unchecked") public NativeQueryImplementor toQuery(SharedSessionContractImplementor session, String resultSetMappingName) { - return new NativeQueryImpl( this, resultSetMappingName, session ); + return new NativeQueryImpl<>( this, resultSetMappingName, session ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java index 70b6614015..4e91597e78 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java @@ -16,6 +16,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.named.AbstractNamedQueryMemento; import org.hibernate.query.named.NamedQueryMemento; +import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sql.internal.NamedNativeQueryMementoImpl; /** @@ -50,7 +51,7 @@ public interface NamedNativeQueryMemento extends NamedQueryMemento { * Convert the memento into an untyped executable query */ @Override - NativeQueryImplementor toQuery(SharedSessionContractImplementor session); + NativeQueryImplementor toQuery(SharedSessionContractImplementor session); /** * Convert the memento into a typed executable query diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java index 690048b708..5e0c727464 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java @@ -582,18 +582,18 @@ public interface NodeBuilder extends HibernateCriteriaBuilder { SqmPredicate notLike(Expression x, String pattern, char escapeChar); @Override - SqmInPredicate in(Expression expression); + SqmInPredicate in(Expression expression); @Override - SqmInPredicate in(Expression expression, Expression... values); + SqmInPredicate in(Expression expression, Expression... values); @Override - SqmInPredicate in(Expression expression, T... values); + SqmInPredicate in(Expression expression, T... values); @Override - SqmInPredicate in(Expression expression, List values); + SqmInPredicate in(Expression expression, List values); - SqmInPredicate in(Expression expression, SqmSubQuery subQuery); + SqmInPredicate in(Expression expression, SqmSubQuery subQuery); @Override SqmPredicate exists(Subquery subquery); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java index f13b889e71..351fbfda9e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java @@ -146,13 +146,13 @@ public interface SemanticQueryWalker { T visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath path); - T visitMaxElementPath(SqmMaxElementPath path); + T visitMaxElementPath(SqmMaxElementPath path); - T visitMinElementPath(SqmMinElementPath path); + T visitMinElementPath(SqmMinElementPath path); - T visitMaxIndexPath(SqmMaxIndexPath path); + T visitMaxIndexPath(SqmMaxIndexPath path); - T visitMinIndexPath(SqmMinIndexPath path); + T visitMinIndexPath(SqmMinIndexPath path); T visitTreatedPath(SqmTreatedPath sqmTreatedPath); @@ -168,7 +168,7 @@ public interface SemanticQueryWalker { T visitSelectClause(SqmSelectClause selectClause); - T visitSelection(SqmSelection selection); + T visitSelection(SqmSelection selection); T visitValues(SqmValues values); @@ -178,7 +178,7 @@ public interface SemanticQueryWalker { T visitDynamicInstantiation(SqmDynamicInstantiation sqmDynamicInstantiation); - default T visitJpaCompoundSelection(SqmJpaCompoundSelection selection) { + default T visitJpaCompoundSelection(SqmJpaCompoundSelection selection) { throw new NotYetImplementedFor6Exception( getClass() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java index 8020bb9134..b24504c3e0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.internal; import java.util.ArrayList; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import javax.persistence.Tuple; @@ -45,7 +46,7 @@ import org.hibernate.sql.results.internal.RowTransformerJpaTupleImpl; import org.hibernate.sql.results.internal.RowTransformerPassThruImpl; import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter; -import org.hibernate.sql.results.internal.TupleElementImpl; +import org.hibernate.sql.results.internal.TupleMetadata; import org.hibernate.sql.results.spi.RowTransformer; /** @@ -136,17 +137,12 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { if ( Tuple.class.isAssignableFrom( resultType ) ) { // resultType is Tuple.. if ( queryOptions.getTupleTransformer() == null ) { - final List> tupleElementList = new ArrayList<>( selections.size() ); + final Map, Integer> tupleElementMap = new IdentityHashMap<>( selections.size() ); for ( int i = 0; i < selections.size(); i++ ) { - final SqmSelection selection = selections.get( i ); - tupleElementList.add( - new TupleElementImpl<>( - selection.getSelectableNode().getJavaTypeDescriptor().getJavaTypeClass(), - selection.getAlias() - ) - ); + final SqmSelection selection = selections.get( i ); + tupleElementMap.put( selection.getSelectableNode(), i ); } - return (RowTransformer) new RowTransformerJpaTupleImpl( tupleElementList ); + return (RowTransformer) new RowTransformerJpaTupleImpl( new TupleMetadata( tupleElementMap ) ); } throw new IllegalArgumentException( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index bc280d9931..7c700dd340 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -491,7 +491,7 @@ public class QuerySqmImpl queryOptions.getGraph() != null || hasLimit ); - ExecutionContext executionContextToUse; + final ExecutionContext executionContextToUse; if ( queryOptions.hasLimit() && containsCollectionFetches ) { boolean fail = getSessionFactory().getSessionFactoryOptions().isFailOnPaginationOverCollectionFetchEnabled(); if (fail) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index 07d199b264..f7290334eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -70,6 +70,7 @@ 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.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmSetJoin; import org.hibernate.query.sqm.tree.domain.SqmSingularJoin; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; @@ -94,10 +95,13 @@ 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.SqmEmptinessPredicate; +import org.hibernate.query.sqm.tree.predicate.SqmExistsPredicate; 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.SqmMemberOfPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate; import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; @@ -507,7 +511,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmExpression sum(Expression argument) { return getFunctionDescriptor("sum").generateSqmExpression( (SqmTypedNode) argument, - (AllowableFunctionReturnType) ( (SqmExpression) argument ).getNodeType(), + null, queryEngine, getJpaMetamodel().getTypeConfiguration() ); @@ -545,12 +549,14 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public > SqmExpression greatest(Expression argument) { - throw new NotYetImplementedFor6Exception(); + return queryEngine.getSqmFunctionRegistry().findFunctionDescriptor( "max" ) + .generateSqmExpression( (SqmTypedNode) argument, null, queryEngine, getTypeConfiguration() ); } @Override public > SqmExpression least(Expression argument) { - throw new NotYetImplementedFor6Exception(); + return queryEngine.getSqmFunctionRegistry().findFunctionDescriptor( "min" ) + .generateSqmExpression( (SqmTypedNode) argument, null, queryEngine, getTypeConfiguration() ); } @Override @@ -747,10 +753,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { //noinspection unchecked return getFunctionDescriptor("sqrt").generateSqmExpression( (SqmTypedNode) x, - (AllowableFunctionReturnType) QueryHelper.highestPrecedenceType2( - ((SqmExpression) x).getNodeType(), - StandardBasicTypes.DOUBLE - ), + null, queryEngine, getJpaMetamodel().getTypeConfiguration() ); @@ -1876,32 +1879,32 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public > SqmPredicate isEmpty(Expression collection) { - throw new NotYetImplementedFor6Exception(); + return new SqmEmptinessPredicate( (SqmPluralValuedSimplePath) collection, false, this ); } @Override public > SqmPredicate isNotEmpty(Expression collection) { - throw new NotYetImplementedFor6Exception(); + return new SqmEmptinessPredicate( (SqmPluralValuedSimplePath) collection, true, this ); } @Override public > SqmPredicate isMember(Expression elem, Expression collection) { - throw new NotYetImplementedFor6Exception(); + return new SqmMemberOfPredicate( (SqmExpression) elem, (SqmPath) collection, false, this ); } @Override public > SqmPredicate isMember(E elem, Expression collection) { - throw new NotYetImplementedFor6Exception(); + return new SqmMemberOfPredicate( value( elem ), (SqmPath) collection, false, this ); } @Override public > SqmPredicate isNotMember(Expression elem, Expression collection) { - throw new NotYetImplementedFor6Exception(); + return new SqmMemberOfPredicate( (SqmExpression) elem, (SqmPath) collection, true, this ); } @Override public > SqmPredicate isNotMember(E elem, Expression collection) { - throw new NotYetImplementedFor6Exception(); + return new SqmMemberOfPredicate( value( elem ), (SqmPath) collection, true, this ); } @Override @@ -1993,30 +1996,29 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { } @Override - public SqmInPredicate in(Expression expression) { - //noinspection unchecked - return new SqmInListPredicate( (SqmExpression) expression, this ); + @SuppressWarnings("unchecked") + public SqmInPredicate in(Expression expression) { + return new SqmInListPredicate<>( (SqmExpression) expression, this ); } @Override @SuppressWarnings("unchecked") - public SqmInPredicate in(Expression expression, Expression... values) { - final SqmInListPredicate predicate = new SqmInListPredicate( (SqmExpression) expression, this ); + public SqmInPredicate in(Expression expression, Expression... values) { + final SqmInListPredicate predicate = new SqmInListPredicate<>( (SqmExpression) expression, this ); for ( Expression value : values ) { - predicate.addExpression( (SqmExpression) value ); + predicate.addExpression( (SqmExpression) value ); } return predicate; } @Override @SuppressWarnings("unchecked") - public SqmInPredicate in(Expression expression, T... values) { - final SqmExpression sqmExpression = (SqmExpression) expression; - final SqmInListPredicate predicate = new SqmInListPredicate( sqmExpression, this ); + public SqmInPredicate in(Expression expression, T... values) { + final SqmExpression sqmExpression = (SqmExpression) expression; + final SqmInListPredicate predicate = new SqmInListPredicate<>( sqmExpression, this ); for ( T value : values ) { - //noinspection unchecked predicate.addExpression( - new SqmLiteral( value, sqmExpression.getNodeType(), this ) + new SqmLiteral<>( value, sqmExpression.getNodeType(), this ) ); } return predicate; @@ -2024,35 +2026,35 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override @SuppressWarnings("unchecked") - public SqmInPredicate in(Expression expression, List values) { - final SqmExpression sqmExpression = (SqmExpression) expression; - final SqmInListPredicate predicate = new SqmInListPredicate( sqmExpression, this ); + public SqmInPredicate in(Expression expression, List values) { + final SqmExpression sqmExpression = (SqmExpression) expression; + final SqmInListPredicate predicate = new SqmInListPredicate<>( sqmExpression, this ); for ( T value : values ) { predicate.addExpression( - new SqmLiteral( value, sqmExpression.getNodeType(), this ) + new SqmLiteral<>( value, sqmExpression.getNodeType(), this ) ); } return predicate; } @Override - public SqmInPredicate in(Expression expression, SqmSubQuery subQuery) { - //noinspection unchecked - return new SqmInSubQueryPredicate( (SqmExpression) expression, subQuery, this ); + @SuppressWarnings("unchecked") + public SqmInPredicate in(Expression expression, SqmSubQuery subQuery) { + return new SqmInSubQueryPredicate<>( (SqmExpression) expression, subQuery, this ); } @Override - public SqmPredicate exists(Subquery subquery) { - throw new NotYetImplementedFor6Exception(); + public SqmPredicate exists(Subquery subQuery) { + return new SqmExistsPredicate( (SqmExpression) subQuery, this ); } @Override public > SqmPredicate isMapEmpty(JpaExpression mapExpression) { - throw new NotYetImplementedFor6Exception(); + return new SqmEmptinessPredicate( (SqmPluralValuedSimplePath) mapExpression, false, this ); } @Override public > SqmPredicate isMapNotEmpty(JpaExpression mapExpression) { - throw new NotYetImplementedFor6Exception(); + return new SqmEmptinessPredicate( (SqmPluralValuedSimplePath) mapExpression, true, this ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java index b0192bad80..988ed8581f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java @@ -31,6 +31,7 @@ import org.hibernate.query.sqm.SqmExpressable; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.query.sqm.tree.domain.AbstractSqmSpecificPluralPartPath; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -59,6 +60,14 @@ public class SqmMappingModelHelper { return sessionFactory.getMetamodel().entityPersister( hibernateEntityName ); } + public static SqmPathSource resolveSqmKeyPathSource( + String name, + DomainType valueDomainType, + Bindable.BindableType jpaBindableType) { + // todo (6.0): the key path source must create a special path for the key + return resolveSqmPathSource( name, valueDomainType, jpaBindableType ); + } + public static SqmPathSource resolveSqmPathSource( String name, DomainType valueDomainType, @@ -137,6 +146,12 @@ public class SqmMappingModelHelper { return container.findSubPart( sqmPath.getNavigablePath().getLocalName(), container ); } + // Plural path parts are not joined and thus also have no table group + if ( sqmPath instanceof AbstractSqmSpecificPluralPartPath ) { + final TableGroup lhsTableGroup = tableGroupLocator.apply( sqmPath.getLhs().getLhs().getNavigablePath() ); + return lhsTableGroup.getModelPart().findSubPart( sqmPath.getReferencedPathSource().getPathName(), null ); + } + final TableGroup lhsTableGroup = tableGroupLocator.apply( sqmPath.getLhs().getNavigablePath() ); return lhsTableGroup.getModelPart().findSubPart( sqmPath.getReferencedPathSource().getPathName(), null ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java index 63ca55badb..0eff0f770b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java @@ -200,7 +200,7 @@ public class StandardFunctionReturnTypeResolvers { return false; } - private static AllowableFunctionReturnType extractArgumentType(List> arguments, int position) { + public static AllowableFunctionReturnType extractArgumentType(List> arguments, int position) { final SqmTypedNode specifiedArgument = arguments.get( position-1 ); final SqmExpressable specifiedArgType = specifiedArgument.getNodeType(); if ( !(specifiedArgType instanceof AllowableFunctionReturnType) ) { @@ -218,7 +218,7 @@ public class StandardFunctionReturnTypeResolvers { return (AllowableFunctionReturnType) specifiedArgType; } - private static BasicValuedMapping extractArgumentValuedMapping(List arguments, int position) { + public static BasicValuedMapping extractArgumentValuedMapping(List arguments, int position) { final SqlAstNode specifiedArgument = arguments.get( position-1 ); final MappingModelExpressable specifiedArgType = specifiedArgument instanceof Expression ? ( (Expression) specifiedArgument ).getExpressionType() diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java index 13ac16c376..8c30508ffc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java @@ -85,12 +85,14 @@ import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; +import org.hibernate.query.sqm.tree.select.SqmJpaCompoundSelection; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQueryGroup; import org.hibernate.query.sqm.tree.select.SqmQueryPart; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectClause; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; +import org.hibernate.query.sqm.tree.select.SqmSelectableNode; import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.query.sqm.tree.select.SqmSubQuery; @@ -287,22 +289,22 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker path) { return path; } @Override - public Object visitMinElementPath(SqmMinElementPath path) { + public Object visitMinElementPath(SqmMinElementPath path) { return path; } @Override - public Object visitMaxIndexPath(SqmMaxIndexPath path) { + public Object visitMaxIndexPath(SqmMaxIndexPath path) { return path; } @Override - public Object visitMinIndexPath(SqmMinIndexPath path) { + public Object visitMinIndexPath(SqmMinIndexPath path) { return path; } @@ -325,11 +327,19 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker selection) { selection.getSelectableNode().accept( this ); return selection; } + @Override + public Object visitJpaCompoundSelection(SqmJpaCompoundSelection selection) { + for ( SqmSelectableNode selectionItem : selection.getSelectionItems() ) { + selectionItem.accept( this ); + } + return selection; + } + @Override public Object visitValues(SqmValues values) { for ( SqmExpression expression : values.getExpressions() ) { @@ -399,7 +409,9 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker extends Base } @Override - public Object visitSelection(SqmSelection sqmSelection) { + public Void visitSelection(SqmSelection sqmSelection) { currentSqlSelectionCollector().next(); final Map> resultProducers; if ( sqmSelection.getSelectableNode() instanceof SqmJpaCompoundSelection ) { @@ -3452,26 +3462,23 @@ public abstract class BaseSqmToSqlAstConverter extends Base @Override public CaseSimpleExpression visitSimpleCaseExpression(SqmCaseSimple expression) { - 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 ) + visitWithInferredType( whenFragment.getResult(), expression ) ) ); } Expression otherwise = null; if ( expression.getOtherwise() != null ) { - resultType = QueryHelper.highestPrecedenceType2( resultType, expression.getOtherwise().getNodeType() ); - otherwise = (Expression) expression.getOtherwise().accept( this ); + otherwise = visitWithInferredType( expression.getOtherwise(), expression ); } final CaseSimpleExpression result = new CaseSimpleExpression( - resolveMappingExpressable( resultType ), + resolveMappingExpressable( expression.getNodeType() ), (Expression) expression.getFixture().accept( this ), whenFragments, otherwise @@ -3482,26 +3489,23 @@ public abstract class BaseSqmToSqlAstConverter extends Base @Override public CaseSearchedExpression visitSearchedCaseExpression(SqmCaseSearched expression) { - 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 ) + visitWithInferredType( whenFragment.getResult(), expression ) ) ); } Expression otherwise = null; if ( expression.getOtherwise() != null ) { - resultType = QueryHelper.highestPrecedenceType2( resultType, expression.getOtherwise().getNodeType() ); - otherwise = (Expression) expression.getOtherwise().accept( this ); + otherwise = visitWithInferredType( expression.getOtherwise(), expression ); } final CaseSearchedExpression result = new CaseSearchedExpression( - resolveMappingExpressable( resultType ), + resolveMappingExpressable( expression.getNodeType() ), whenFragments, otherwise ); @@ -3509,6 +3513,16 @@ public abstract class BaseSqmToSqlAstConverter extends Base return result; } + private T visitWithInferredType(SqmExpression expression, SqmExpression inferred) { + inferrableTypeAccessStack.push( () -> determineValueMapping( inferred ) ); + try { + return (T) expression.accept( this ); + } + finally { + inferrableTypeAccessStack.pop(); + } + } + @Override public Object visitAny(SqmAny sqmAny) { return new Any( @@ -3591,6 +3605,170 @@ public abstract class BaseSqmToSqlAstConverter extends Base ); } + @Override + public Expression visitMaxElementPath(SqmMaxElementPath path) { + return createCorrelatedAggregateSubQuery( path, false, true ); + } + + @Override + public Expression visitMinElementPath(SqmMinElementPath path) { + return createCorrelatedAggregateSubQuery( path, false, false ); + } + + @Override + public Expression visitMaxIndexPath(SqmMaxIndexPath path) { + return createCorrelatedAggregateSubQuery( path, true, true ); + } + + @Override + public Expression visitMinIndexPath(SqmMinIndexPath path) { + return createCorrelatedAggregateSubQuery( path, true, false ); + } + + @Override + public Expression visitPluralAttributeSizeFunction(SqmCollectionSize function) { + final SqmPath pluralPath = function.getPluralPath(); + final PluralAttributeMapping mappingModelExpressable = (PluralAttributeMapping) determineValueMapping( + pluralPath ); + final FromClauseAccess parentFromClauseAccess = getFromClauseAccess(); + final QuerySpec subQuerySpec = new QuerySpec( false ); + pushProcessingState( + new SqlAstQueryPartProcessingStateImpl( + subQuerySpec, + getCurrentProcessingState(), + this, + currentClauseStack::getCurrent + ) + ); + try { + final TableGroup tableGroup = mappingModelExpressable.createRootTableGroup( + pluralPath.getNavigablePath(), + null, + true, + LockOptions.NONE.getLockMode(), + () -> subQuerySpec::applyPredicate, + this, + creationContext + ); + + getFromClauseAccess().registerTableGroup( pluralPath.getNavigablePath(), tableGroup ); + subQuerySpec.getFromClause().addRoot( tableGroup ); + + final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor = (AbstractSqmSelfRenderingFunctionDescriptor) creationContext + .getSessionFactory() + .getQueryEngine() + .getSqmFunctionRegistry() + .findFunctionDescriptor( "count" ); + final BasicType integerType = creationContext.getDomainModel() + .getTypeConfiguration() + .getBasicTypeForJavaType( Integer.class ); + final Expression expression = new SelfRenderingFunctionSqlAstExpression( + functionDescriptor.getName(), + functionDescriptor::render, + Collections.singletonList( new QueryLiteral<>( 1, integerType ) ), + integerType, + integerType + ); + subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) ); + + subQuerySpec.applyPredicate( + mappingModelExpressable.getKeyDescriptor().generateJoinPredicate( + parentFromClauseAccess.findTableGroup( pluralPath.getNavigablePath().getParent() ), + tableGroup, + SqlAstJoinType.INNER, + getSqlExpressionResolver(), + creationContext + ) + ); + } + finally { + popProcessingStateStack(); + } + return subQuerySpec; + } + + @Override + public Object visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath path) { + // SemanticQueryBuilder applies the index expression to the generated join + return path.getLhs().accept( this ); + } + + protected Expression createCorrelatedAggregateSubQuery( + AbstractSqmSpecificPluralPartPath pluralPartPath, + boolean index, + boolean max) { + final PluralAttributeMapping mappingModelExpressable = (PluralAttributeMapping) determineValueMapping( + pluralPartPath.getPluralDomainPath() ); + final FromClauseAccess parentFromClauseAccess = getFromClauseAccess(); + final QuerySpec subQuerySpec = new QuerySpec( false ); + pushProcessingState( + new SqlAstQueryPartProcessingStateImpl( + subQuerySpec, + getCurrentProcessingState(), + this, + currentClauseStack::getCurrent + ) + ); + try { + final TableGroup tableGroup = mappingModelExpressable.createRootTableGroup( + pluralPartPath.getNavigablePath(), + null, + true, + LockOptions.NONE.getLockMode(), + () -> subQuerySpec::applyPredicate, + this, + creationContext + ); + + getFromClauseAccess().registerTableGroup( pluralPartPath.getNavigablePath(), tableGroup ); + subQuerySpec.getFromClause().addRoot( tableGroup ); + + final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor = (AbstractSqmSelfRenderingFunctionDescriptor) creationContext + .getSessionFactory() + .getQueryEngine() + .getSqmFunctionRegistry() + .findFunctionDescriptor( max ? "max" : "min" ); + final CollectionPart collectionPart = index + ? mappingModelExpressable.getIndexDescriptor() + : mappingModelExpressable.getElementDescriptor(); + final List arguments = new ArrayList<>( 1 ); + + collectionPart.forEachSelection( + (selectionIndex, selectionMapping) -> { + arguments.add( + new ColumnReference( + tableGroup.getTableReference( selectionMapping.getContainingTableExpression() ), + selectionMapping, + creationContext.getSessionFactory() + ) + ); + } + ); + final Expression expression = new SelfRenderingFunctionSqlAstExpression( + functionDescriptor.getName(), + functionDescriptor::render, + arguments, + (AllowableFunctionReturnType) collectionPart.getJdbcMappings().get( 0 ), + collectionPart + ); + subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) ); + + subQuerySpec.applyPredicate( + mappingModelExpressable.getKeyDescriptor().generateJoinPredicate( + parentFromClauseAccess.findTableGroup( pluralPartPath.getPluralDomainPath().getNavigablePath().getParent() ), + tableGroup, + SqlAstJoinType.INNER, + getSqlExpressionResolver(), + creationContext + ) + ); + } + finally { + popProcessingStateStack(); + } + return subQuerySpec; + } + // @Override // public Object visitPluralAttributeElementBinding(PluralAttributeElementBinding binding) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java index f0119141ce..b4a6ee613a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java @@ -46,7 +46,6 @@ import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmJoin; import org.hibernate.query.sqm.tree.from.SqmRoot; - /** * Convenience base class for SqmFrom implementations * @@ -55,7 +54,7 @@ import org.hibernate.query.sqm.tree.from.SqmRoot; public abstract class AbstractSqmFrom extends AbstractSqmPath implements SqmFrom { private String alias; - private List> joins; + private List> joins; protected AbstractSqmFrom( NavigablePath navigablePath, @@ -133,12 +132,12 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @Override - public List> getSqmJoins() { + public List> getSqmJoins() { return joins == null ? Collections.emptyList() : Collections.unmodifiableList( joins ); } @Override - public void addSqmJoin(SqmJoin join) { + public void addSqmJoin(SqmJoin join) { if ( joins == null ) { joins = new ArrayList<>(); } @@ -146,7 +145,7 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @Override - public void visitSqmJoins(Consumer> consumer) { + public void visitSqmJoins(Consumer> consumer) { if ( joins != null ) { joins.forEach( consumer ); } @@ -190,84 +189,105 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements @Override @SuppressWarnings("unchecked") public SqmSingularJoin join(SingularAttribute attribute, JoinType jt) { - return buildSingularJoin( (SingularPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); + final SqmSingularJoin join = buildSingularJoin( (SingularPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); + addSqmJoin( join ); + return join; } @Override - @SuppressWarnings("unchecked") - public SqmBagJoin join(CollectionAttribute attribute) { + public SqmBagJoin join(CollectionAttribute attribute) { return join( attribute, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmBagJoin join(CollectionAttribute attribute, JoinType jt) { - return buildBagJoin( (BagPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); + public SqmBagJoin join(CollectionAttribute attribute, JoinType jt) { + final SqmBagJoin join = buildBagJoin( (BagPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); + addSqmJoin( join ); + return join; } @Override - @SuppressWarnings("unchecked") - public SqmSetJoin join(SetAttribute attribute) { + public SqmSetJoin join(SetAttribute attribute) { return join( attribute, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmSetJoin join(SetAttribute attribute, JoinType jt) { - return buildSetJoin( (SetPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); + public SqmSetJoin join(SetAttribute attribute, JoinType jt) { + final SqmSetJoin join = buildSetJoin( + (SetPersistentAttribute) attribute, + SqmJoinType.from( jt ), + false + ); + addSqmJoin( join ); + return join; } @Override - @SuppressWarnings("unchecked") - public SqmListJoin join(ListAttribute attribute) { + public SqmListJoin join(ListAttribute attribute) { return join( attribute, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmListJoin join(ListAttribute attribute, JoinType jt) { - return buildListJoin( (ListPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); + public SqmListJoin join(ListAttribute attribute, JoinType jt) { + final SqmListJoin join = buildListJoin( + (ListPersistentAttribute) attribute, + SqmJoinType.from( jt ), + false + ); + addSqmJoin( join ); + return join; } @Override - @SuppressWarnings("unchecked") - public SqmMapJoin join(MapAttribute attribute) { + public SqmMapJoin join(MapAttribute attribute) { return join( attribute, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmMapJoin join(MapAttribute attribute, JoinType jt) { - return buildMapJoin( (MapPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); + public SqmMapJoin join(MapAttribute attribute, JoinType jt) { + final SqmMapJoin join = buildMapJoin( + (MapPersistentAttribute) attribute, + SqmJoinType.from( jt ), + false + ); + addSqmJoin( join ); + return join; } @Override - @SuppressWarnings("unchecked") - public SqmAttributeJoin join(String attributeName) { + public SqmAttributeJoin join(String attributeName) { return join( attributeName, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmAttributeJoin join(String attributeName, JoinType jt) { + public SqmAttributeJoin join(String attributeName, JoinType jt) { final SqmPathSource subPathSource = getReferencedPathSource().findSubPathSource( attributeName ); - - return buildJoin( subPathSource, SqmJoinType.from( jt ), false ); + return (SqmAttributeJoin) buildJoin( subPathSource, SqmJoinType.from( jt ), false ); } @Override - @SuppressWarnings("unchecked") - public SqmBagJoin joinCollection(String attributeName) { + public SqmBagJoin joinCollection(String attributeName) { return joinCollection( attributeName, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmBagJoin joinCollection(String attributeName, JoinType jt) { + public SqmBagJoin joinCollection(String attributeName, JoinType jt) { final SqmPathSource joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName ); if ( joinedPathSource instanceof BagPersistentAttribute ) { - return buildBagJoin( (BagPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false ); + final SqmBagJoin join = buildBagJoin( + (BagPersistentAttribute) joinedPathSource, + SqmJoinType.from( jt ), + false + ); + addSqmJoin( join ); + return (SqmBagJoin) join; } throw new IllegalArgumentException( @@ -282,18 +302,23 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @Override - @SuppressWarnings("unchecked") - public SqmSetJoin joinSet(String attributeName) { + public SqmSetJoin joinSet(String attributeName) { return joinSet( attributeName, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmSetJoin joinSet(String attributeName, JoinType jt) { + public SqmSetJoin joinSet(String attributeName, JoinType jt) { final SqmPathSource joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName ); if ( joinedPathSource instanceof SetPersistentAttribute ) { - return buildSetJoin( (SetPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false ); + final SqmSetJoin join = buildSetJoin( + (SetPersistentAttribute) joinedPathSource, + SqmJoinType.from( jt ), + false + ); + addSqmJoin( join ); + return (SqmSetJoin) join; } throw new IllegalArgumentException( @@ -308,18 +333,23 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @Override - @SuppressWarnings("unchecked") - public SqmListJoin joinList(String attributeName) { + public SqmListJoin joinList(String attributeName) { return joinList( attributeName, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmListJoin joinList(String attributeName, JoinType jt) { + public SqmListJoin joinList(String attributeName, JoinType jt) { final SqmPathSource joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName ); if ( joinedPathSource instanceof ListPersistentAttribute ) { - return buildListJoin( (ListPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false ); + final SqmListJoin join = buildListJoin( + (ListPersistentAttribute) joinedPathSource, + SqmJoinType.from( jt ), + false + ); + addSqmJoin( join ); + return (SqmListJoin) join; } throw new IllegalArgumentException( @@ -334,18 +364,23 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @Override - @SuppressWarnings("unchecked") - public SqmMapJoin joinMap(String attributeName) { + public SqmMapJoin joinMap(String attributeName) { return joinMap( attributeName, JoinType.INNER ); } @Override @SuppressWarnings("unchecked") - public SqmMapJoin joinMap(String attributeName, JoinType jt) { + public SqmMapJoin joinMap(String attributeName, JoinType jt) { final SqmPathSource joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName ); - if ( joinedPathSource instanceof MapPersistentAttribute ) { - return buildMapJoin( (MapPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false ); + if ( joinedPathSource instanceof MapPersistentAttribute ) { + final SqmMapJoin join = buildMapJoin( + (MapPersistentAttribute) joinedPathSource, + SqmJoinType.from( jt ), + false + ); + addSqmJoin( join ); + return (SqmMapJoin) join; } throw new IllegalArgumentException( @@ -374,12 +409,14 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements @Override @SuppressWarnings("unchecked") - public SqmSingularJoin fetch(SingularAttribute attribute, JoinType jt) { - return buildSingularJoin( - (SingularPersistentAttribute) attribute, + public SqmSingularJoin fetch(SingularAttribute attribute, JoinType jt) { + final SqmSingularJoin join = buildSingularJoin( + (SingularPersistentAttribute) attribute, SqmJoinType.from( jt ), true ); + addSqmJoin( join ); + return join; } @Override @@ -391,7 +428,7 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements @SuppressWarnings("unchecked") public SqmAttributeJoin fetch(PluralAttribute attribute, JoinType jt) { return buildJoin( - (PluralPersistentAttribute) attribute, + (PluralPersistentAttribute) attribute, SqmJoinType.from( jt ), true ); @@ -404,47 +441,52 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements @Override @SuppressWarnings("unchecked") - public SqmAttributeJoin fetch(String attributeName, JoinType jt) { - final SqmPathSource fetchedPathSource = getReferencedPathSource().findSubPathSource( attributeName ); - return buildJoin( fetchedPathSource, SqmJoinType.from( jt ), true ); + public SqmAttributeJoin fetch(String attributeName, JoinType jt) { + final SqmPathSource fetchedPathSource = (SqmPathSource) getReferencedPathSource() + .findSubPathSource( attributeName ); + return (SqmAttributeJoin) buildJoin( + fetchedPathSource, + SqmJoinType.from( jt ), + true + ); } - private SqmAttributeJoin buildJoin( + private SqmAttributeJoin buildJoin( SqmPathSource joinedPathSource, SqmJoinType joinType, boolean fetched) { - final SqmAttributeJoin sqmJoin; - if ( joinedPathSource instanceof SingularPersistentAttribute ) { + final SqmAttributeJoin sqmJoin; + if ( joinedPathSource instanceof SingularPersistentAttribute ) { sqmJoin = buildSingularJoin( - (SingularPersistentAttribute) joinedPathSource, + (SingularPersistentAttribute) joinedPathSource, joinType, fetched ); } - else if ( joinedPathSource instanceof BagPersistentAttribute ) { + else if ( joinedPathSource instanceof BagPersistentAttribute ) { sqmJoin = buildBagJoin( - (BagPersistentAttribute) joinedPathSource, + (BagPersistentAttribute) joinedPathSource, joinType, fetched ); } - else if ( joinedPathSource instanceof ListPersistentAttribute ) { + else if ( joinedPathSource instanceof ListPersistentAttribute ) { sqmJoin = buildListJoin( - (ListPersistentAttribute) joinedPathSource, + (ListPersistentAttribute) joinedPathSource, joinType, fetched ); } - else if ( joinedPathSource instanceof MapPersistentAttribute ) { + else if ( joinedPathSource instanceof MapPersistentAttribute ) { sqmJoin = buildMapJoin( - (MapPersistentAttribute) joinedPathSource, + (MapPersistentAttribute) joinedPathSource, joinType, fetched ); } - else if ( joinedPathSource instanceof SetPersistentAttribute ) { + else if ( joinedPathSource instanceof SetPersistentAttribute ) { sqmJoin = buildSetJoin( - (SetPersistentAttribute) joinedPathSource, + (SetPersistentAttribute) joinedPathSource, joinType, fetched ); @@ -464,15 +506,15 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements return sqmJoin; } - private SqmSingularJoin buildSingularJoin( - SingularPersistentAttribute attribute, + @SuppressWarnings("unchecked") + private SqmSingularJoin buildSingularJoin( + SingularPersistentAttribute attribute, SqmJoinType joinType, boolean fetched) { if ( attribute.getSqmPathType() instanceof ManagedDomainType ) { - //noinspection unchecked - return new SqmSingularJoin( + return new SqmSingularJoin<>( this, - attribute, + (SingularPersistentAttribute) attribute, null, joinType, fetched, @@ -481,17 +523,16 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } throw new SemanticException( "Attribute [" + attribute + "] is not joinable" ); - } @SuppressWarnings("unchecked") - private SqmBagJoin buildBagJoin( - BagPersistentAttribute attribute, + private SqmBagJoin buildBagJoin( + BagPersistentAttribute attribute, SqmJoinType joinType, boolean fetched) { - return new SqmBagJoin( + return new SqmBagJoin<>( this, - attribute, + (BagPersistentAttribute)attribute, null, joinType, fetched, @@ -500,13 +541,13 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @SuppressWarnings("unchecked") - private SqmListJoin buildListJoin( - ListPersistentAttribute attribute, + private SqmListJoin buildListJoin( + ListPersistentAttribute attribute, SqmJoinType joinType, boolean fetched) { - return new SqmListJoin( + return new SqmListJoin<>( this, - attribute, + (ListPersistentAttribute) attribute, null, joinType, fetched, @@ -515,13 +556,13 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @SuppressWarnings("unchecked") - private SqmMapJoin buildMapJoin( - MapPersistentAttribute attribute, + private SqmMapJoin buildMapJoin( + MapPersistentAttribute attribute, SqmJoinType joinType, boolean fetched) { - return new SqmMapJoin( + return new SqmMapJoin<>( this, - attribute, + (MapPersistentAttribute) attribute, null, joinType, fetched, @@ -530,13 +571,13 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } @SuppressWarnings("unchecked") - private SqmSetJoin buildSetJoin( - SetPersistentAttribute attribute, + private SqmSetJoin buildSetJoin( + SetPersistentAttribute attribute, SqmJoinType joinType, boolean fetched) { - return new SqmSetJoin( + return new SqmSetJoin<>( this, - attribute, + (SetPersistentAttribute) attribute, null, joinType, fetched, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java index 06e9608dbc..6f0008b92f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java @@ -28,7 +28,7 @@ public class SqmIndexedCollectionAccessPath extends AbstractSqmPath implem SqmExpression selectorExpression) { //noinspection unchecked super( - pluralDomainPath.getNavigablePath().append( "[]" ), + pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), "[]" ), (PluralPersistentAttribute) pluralDomainPath.getReferencedPathSource(), pluralDomainPath, pluralDomainPath.nodeBuilder() @@ -50,7 +50,8 @@ public class SqmIndexedCollectionAccessPath extends AbstractSqmPath implem @Override public NavigablePath getNavigablePath() { // todo (6.0) : this would require some String-ified form of the selector - return null; +// return null; + return super.getNavigablePath(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxElementPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxElementPath.java index 5ade2f33a2..aff93a4a70 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxElementPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxElementPath.java @@ -23,7 +23,7 @@ public class SqmMaxElementPath extends AbstractSqmSpecificPluralPartPath { public SqmMaxElementPath(SqmPath pluralDomainPath) { //noinspection unchecked super( - pluralDomainPath.getNavigablePath().append( NAVIGABLE_NAME ), + pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ), pluralDomainPath, (PluralPersistentAttribute) pluralDomainPath.getReferencedPathSource() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxIndexPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxIndexPath.java index 3f41954ee2..09acc737e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxIndexPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMaxIndexPath.java @@ -25,7 +25,7 @@ public class SqmMaxIndexPath extends AbstractSqmSpecificPluralPartPath { public SqmMaxIndexPath(SqmPath pluralDomainPath) { //noinspection unchecked super( - pluralDomainPath.getNavigablePath().append( NAVIGABLE_NAME ), + pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ), pluralDomainPath, (PluralPersistentAttribute) pluralDomainPath.getReferencedPathSource() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinElementPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinElementPath.java index 6b4356ae46..bf53a63c66 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinElementPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinElementPath.java @@ -23,7 +23,7 @@ public class SqmMinElementPath extends AbstractSqmSpecificPluralPartPath { public SqmMinElementPath(SqmPath pluralDomainPath) { //noinspection unchecked super( - pluralDomainPath.getNavigablePath().append( NAVIGABLE_NAME ), + pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ), pluralDomainPath, (PluralPersistentAttribute) pluralDomainPath.getReferencedPathSource() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinIndexPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinIndexPath.java index 3d1857aa8a..4007b4e547 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinIndexPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMinIndexPath.java @@ -25,7 +25,7 @@ public class SqmMinIndexPath extends AbstractSqmSpecificPluralPartPath { public SqmMinIndexPath(SqmPath pluralDomainPath) { //noinspection unchecked super( - pluralDomainPath.getNavigablePath().append( NAVIGABLE_NAME ), + pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ), pluralDomainPath, (PluralPersistentAttribute) pluralDomainPath.getReferencedPathSource() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java index c1dbbaada1..c25733211d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java @@ -85,7 +85,7 @@ public class SqmJpaCompoundSelection } @Override - public List> getSelectionItems() { + public List> getSelectionItems() { return selectableNodes; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java index 0097cc27bc..3f0fa03d01 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java @@ -315,7 +315,9 @@ public class SqmSelectStatement extends AbstractSqmSelectQuery implements @SuppressWarnings("unchecked") public SqmSelectStatement select(Selection selection) { getQuerySpec().setSelection( (JpaSelection) selection ); - setResultType( (Class) selection.getJavaType() ); + if ( getResultType() == null ) { + setResultType( (Class) selection.getJavaType() ); + } return this; } @@ -324,7 +326,9 @@ public class SqmSelectStatement extends AbstractSqmSelectQuery implements for ( Selection selection : selections ) { getQuerySpec().getSelectClause().add( (SqmExpression) selection, selection.getAlias() ); } - setResultType( (Class) Object[].class ); + if ( getResultType() == null ) { + setResultType( (Class) Object[].class ); + } return this; } @@ -333,7 +337,9 @@ public class SqmSelectStatement extends AbstractSqmSelectQuery implements for ( Selection selection : selectionList ) { getQuerySpec().getSelectClause().add( (SqmExpression) selection, selection.getAlias() ); } - setResultType( (Class) Object[].class ); + if ( getResultType() == null ) { + setResultType( (Class) Object[].class ); + } return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index e9a5e8c907..425fa636b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -38,10 +38,14 @@ import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.StandardStack; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.CollectionPart; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart; +import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; +import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.Loadable; import org.hibernate.query.ComparisonOperator; @@ -2650,11 +2654,27 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( ( (Loadable) tableGroup.getModelPart() ).getIdentifierColumnNames()[0] ); } else if ( modelPart instanceof PluralAttributeMapping ) { - CollectionPart elementDescriptor = ( (PluralAttributeMapping) modelPart ).getElementDescriptor(); + final CollectionPart elementDescriptor = ( (PluralAttributeMapping) modelPart ).getElementDescriptor(); if ( elementDescriptor instanceof BasicValuedCollectionPart ) { String mappedColumnExpression = ( (BasicValuedCollectionPart) elementDescriptor ).getSelectionExpression(); appendSql( mappedColumnExpression ); } + else if ( elementDescriptor instanceof EntityCollectionPart ) { + final ForeignKeyDescriptor foreignKeyDescriptor = ( (EntityCollectionPart) elementDescriptor ).getForeignKeyDescriptor(); + if ( foreignKeyDescriptor instanceof SimpleForeignKeyDescriptor ) { + foreignKeyDescriptor.visitTargetColumns( + (selectionIndex, selectionMapping) -> appendSql( selectionMapping.getSelectionExpression() ) + ); + } + } + } + else if ( modelPart instanceof ToOneAttributeMapping ) { + final ForeignKeyDescriptor foreignKeyDescriptor = ( (ToOneAttributeMapping) modelPart ).getForeignKeyDescriptor(); + if ( foreignKeyDescriptor instanceof SimpleForeignKeyDescriptor ) { + foreignKeyDescriptor.visitTargetColumns( + (selectionIndex, selectionMapping) -> appendSql( selectionMapping.getSelectionExpression() ) + ); + } } else { throw new NotYetImplementedFor6Exception( getClass() ); @@ -3190,7 +3210,7 @@ public abstract class AbstractSqlAstTranslator implemen @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/CaseSimpleExpression.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/CaseSimpleExpression.java index 2192fd51cd..a59c865fb0 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 @@ -16,6 +16,8 @@ import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.sql.results.graph.basic.BasicResult; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** * @author Steve Ebersole @@ -57,17 +59,16 @@ public class CaseSimpleExpression implements Expression, DomainResultProducer { public DomainResult createDomainResult( String resultVariable, DomainResultCreationState creationState) { - throw new NotYetImplementedFor6Exception( getClass() ); - -// return new BasicResultImpl( -// resultVariable, -// creationState.getSqlExpressionResolver().resolveSqlSelection( -// this, -// getType().getJavaTypeDescriptor(), -// creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration() -// ), -// getType() -// ); + final JavaTypeDescriptor javaTypeDescriptor = type.getJdbcMappings().get( 0 ).getJavaTypeDescriptor(); + return new BasicResult( + creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection( + this, + javaTypeDescriptor, + creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration() + ).getValuesArrayPosition(), + resultVariable, + javaTypeDescriptor + ); } public List getWhenFragments() { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerJpaTupleImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerJpaTupleImpl.java index 99dba17297..3036858790 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerJpaTupleImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerJpaTupleImpl.java @@ -7,9 +7,7 @@ package org.hibernate.sql.results.internal; -import java.util.List; import javax.persistence.Tuple; -import javax.persistence.TupleElement; import org.hibernate.sql.results.spi.RowTransformer; @@ -19,15 +17,15 @@ import org.hibernate.sql.results.spi.RowTransformer; * @author Steve Ebersole */ public class RowTransformerJpaTupleImpl implements RowTransformer { - private final List> tupleElements; + private final TupleMetadata tupleMetadata; - public RowTransformerJpaTupleImpl(List> tupleElements) { - this.tupleElements = tupleElements; + public RowTransformerJpaTupleImpl(TupleMetadata tupleMetadata) { + this.tupleMetadata = tupleMetadata; } @Override public Tuple transformRow(Object[] row) { - return new TupleImpl( tupleElements, row ); + return new TupleImpl( tupleMetadata, row ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java index 3b56125709..09307f41fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java @@ -17,19 +17,19 @@ import org.hibernate.query.JpaTuple; * @author Steve Ebersole */ public class TupleImpl implements JpaTuple { - private final List> tupleElements; + private final TupleMetadata tupleMetadata; private final Object[] row; - public TupleImpl(List> tupleElements, Object[] row) { - this.tupleElements = tupleElements; + public TupleImpl(TupleMetadata tupleMetadata, Object[] row) { + this.tupleMetadata = tupleMetadata; this.row = row; } @Override @SuppressWarnings("unchecked") public X get(TupleElement tupleElement) { - int index = tupleElements.indexOf( tupleElement ); - if ( index < 0 ) { + final Integer index = tupleMetadata.get( tupleElement ); + if ( index == null ) { throw new IllegalArgumentException( "Requested tuple element did not correspond to element in the result tuple" ); @@ -59,21 +59,8 @@ public class TupleImpl implements JpaTuple { @Override public Object get(String alias) { - int index = -1; - if ( alias != null ) { - alias = alias.trim(); - if ( alias.length() > 0 ) { - int i = 0; - for ( TupleElement selection : tupleElements ) { - if ( alias.equals( selection.getAlias() ) ) { - index = i; - break; - } - i++; - } - } - } - if ( index < 0 ) { + Integer index = tupleMetadata.get( alias ); + if ( index == null ) { throw new IllegalArgumentException( "Given alias [" + alias + "] did not correspond to an element in the result tuple" ); @@ -115,8 +102,7 @@ public class TupleImpl implements JpaTuple { } @Override - @SuppressWarnings("unchecked") public List> getElements() { - return tupleElements; + return tupleMetadata.getList(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleMetadata.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleMetadata.java new file mode 100644 index 0000000000..ec11784ff6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleMetadata.java @@ -0,0 +1,58 @@ +/* + * 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.results.internal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.persistence.TupleElement; + +/** + * Metadata about the tuple structure. + * + * @author Christian Beikov + */ +public final class TupleMetadata { + private final Map, Integer> index; + private Map nameIndex; + private List> list; + + public TupleMetadata(Map, Integer> index) { + this.index = index; + } + + public Integer get(TupleElement tupleElement) { + return index.get( tupleElement ); + } + + public Integer get(String name) { + Map nameIndex = this.nameIndex; + if ( nameIndex == null ) { + nameIndex = new HashMap<>( index.size() ); + for ( Map.Entry, Integer> entry : index.entrySet() ) { + nameIndex.put( entry.getKey().getAlias(), entry.getValue() ); + } + this.nameIndex = nameIndex = Collections.unmodifiableMap( nameIndex ); + } + return nameIndex.get( name ); + } + + public List> getList() { + List> list = this.list; + if ( list == null ) { + final TupleElement[] array = new TupleElement[index.size()]; + for ( Map.Entry, Integer> entry : index.entrySet() ) { + array[entry.getValue()] = entry.getKey(); + } + this.list = list = Collections.unmodifiableList( Arrays.asList( array ) ); + } + return list; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java index 7cf6a421ef..4157dadb8e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java @@ -138,7 +138,7 @@ public interface JdbcTypeDescriptor extends Serializable { } default boolean isTemporal() { - switch ( getSqlType() ) { + switch ( getJdbcType() ) { case Types.DATE: case Types.TIME: case Types.TIMESTAMP: diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 5f1dd84e7e..6f3fdfa9ba 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -168,7 +168,7 @@ public class FunctionTests { assertThat( session.createQuery("select round(32.12345,2)").getSingleResult(), is(32.12f) ); assertThat( session.createQuery("select mod(3,2)").getSingleResult(), is(1) ); assertThat( session.createQuery("select 3%2").getSingleResult(), is(1) ); - assertThat( session.createQuery("select sqrt(9.0)").getSingleResult(), is(3.0f) ); + assertThat( session.createQuery("select sqrt(9.0)").getSingleResult(), is(3.0d) ); } ); }