From 940c898ecf69ea59d2bee6ac5ee20c256604da78 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 3 May 2024 14:41:37 +0200 Subject: [PATCH] Various improvements: * Sprinkle some @Nullable annotations to better understand nullness guarantees * Fix some potential NPEs * Improve Oracle array_agg emulation * Prepare for aggregate component array support --- .../dialect/function/AvgFunction.java | 5 ++- .../dialect/function/StandardSQLFunction.java | 14 ++---- .../function/SumReturnTypeResolver.java | 13 ++---- .../function/TimestampdiffFunction.java | 12 ++---- .../ArrayViaArgumentReturnTypeResolver.java | 9 +++- ...yViaElementArgumentReturnTypeResolver.java | 9 +++- ...entViaArrayArgumentReturnTypeResolver.java | 9 +++- .../internal/AbstractEmbeddableMapping.java | 7 +-- .../entity/AbstractEntityPersister.java | 5 ++- .../query/criteria/JpaTupleElement.java | 5 ++- ...SelfRenderingFunctionSqlAstExpression.java | 16 ++++--- .../sqm/internal/NoParamSqmCopyContext.java | 4 +- .../sqm/internal/SimpleSqmCopyContext.java | 4 +- .../query/sqm/internal/TypecheckUtil.java | 10 +++-- .../function/FunctionReturnTypeResolver.java | 43 +++++++++++++++++-- .../StandardFunctionReturnTypeResolvers.java | 33 +++----------- .../sqm/sql/BaseSqmToSqlAstConverter.java | 5 +++ .../sqm/sql/FakeSqmToSqlAstConverter.java | 5 +++ .../query/sqm/sql/SqmToSqlAstConverter.java | 6 +++ .../query/sqm/tree/SqmCopyContext.java | 4 +- .../query/sqm/tree/SqmTypedNode.java | 8 ++-- .../sqm/tree/domain/AbstractSqmPath.java | 5 ++- .../query/sqm/tree/domain/SqmPath.java | 3 +- .../expression/AbstractSqmExpression.java | 15 +++---- .../tree/expression/AbstractSqmParameter.java | 4 +- .../query/sqm/tree/expression/SqmAny.java | 4 +- .../query/sqm/tree/expression/SqmEvery.java | 4 +- .../sqm/tree/expression/SqmExpression.java | 5 ++- .../sqm/tree/expression/SqmFieldLiteral.java | 3 +- .../tree/expression/SqmHqlNumericLiteral.java | 3 +- .../query/sqm/tree/expression/SqmOver.java | 4 +- .../sqm/tree/expression/SqmParameter.java | 4 -- .../sqm/tree/jpa/AbstractJpaSelection.java | 4 +- .../sqm/tree/jpa/AbstractJpaTupleElement.java | 16 ++++--- .../query/sqm/tree/select/SqmSubQuery.java | 5 ++- .../java/spi/CollectionJavaType.java | 12 ++++-- .../jdbc/JdbcTypeJavaClassMappings.java | 2 +- .../DynamicParameterizedTypeTest.java | 13 ++---- .../function/OrderByFragmentFunction.java | 2 +- .../annotation/AnnotationMetaEntity.java | 14 ++++-- .../annotation/NamedQueryMethod.java | 5 ++- 41 files changed, 208 insertions(+), 145 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/AvgFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/AvgFunction.java index 4b7a2a52f2..41362b15ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/AvgFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/AvgFunction.java @@ -24,6 +24,7 @@ import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionArgumentException; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; @@ -44,6 +45,8 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.ObjectJdbcType; import org.hibernate.type.spi.TypeConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC; /** @@ -240,7 +243,7 @@ public class AvgFunction extends AbstractSqmSelfRenderingFunctionDescriptor { @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { final SqmExpressible expressible = arguments.get( 0 ).getExpressible(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java index fee1102450..6b80b8d3ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java @@ -10,15 +10,17 @@ import java.util.List; import java.util.function.Supplier; import org.hibernate.metamodel.mapping.BasicValuedMapping; -import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.function.NamedSqmFunctionDescriptor; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.BasicTypeReference; import org.hibernate.type.spi.TypeConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Simplified API allowing users to contribute * {@link org.hibernate.query.sqm.function.SqmFunctionDescriptor}s @@ -42,15 +44,7 @@ public class StandardSQLFunction extends NamedSqmFunctionDescriptor { @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - List> arguments, - TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration ); - } - - @Override - public ReturnableType resolveFunctionReturnType( - ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { return type == null ? null : typeConfiguration.getBasicTypeRegistry().resolve( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/SumReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/SumReturnTypeResolver.java index 596e5d29a6..5994e8a818 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/SumReturnTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/SumReturnTypeResolver.java @@ -11,6 +11,7 @@ import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.BasicType; @@ -21,6 +22,8 @@ import java.math.BigInteger; import java.util.List; import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.extractArgumentType; import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.extractArgumentValuedMapping; import static org.hibernate.type.SqlTypes.*; @@ -59,15 +62,7 @@ class SumReturnTypeResolver implements FunctionReturnTypeResolver { @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - List> arguments, - TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration ); - } - - @Override - public ReturnableType resolveFunctionReturnType( - ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { if ( impliedType != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampdiffFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampdiffFunction.java index 77a5cacca0..339529a36b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampdiffFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/TimestampdiffFunction.java @@ -23,6 +23,7 @@ import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.produce.function.internal.PatternRenderer; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; import org.hibernate.sql.ast.SqlAstTranslator; @@ -35,6 +36,7 @@ import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.spi.TypeConfiguration; import jakarta.persistence.TemporalType; +import org.checkerframework.checker.nullness.qual.Nullable; import static java.util.Arrays.asList; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL; @@ -129,15 +131,7 @@ public class TimestampdiffFunction @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - List> arguments, - TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration ); - } - - @Override - public ReturnableType resolveFunctionReturnType( - ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { final BasicType invariantType; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaArgumentReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaArgumentReturnTypeResolver.java index 91361e58e5..2eec562297 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaArgumentReturnTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaArgumentReturnTypeResolver.java @@ -15,11 +15,14 @@ import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.BasicPluralType; import org.hibernate.type.spi.TypeConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A {@link FunctionReturnTypeResolver} that resolves the array type based on an argument. * The inferred type and implied type have precedence though. @@ -37,10 +40,12 @@ public class ArrayViaArgumentReturnTypeResolver implements FunctionReturnTypeRes @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { - final MappingModelExpressible inferredType = inferredTypeSupplier.get(); + final MappingModelExpressible inferredType = converter == null + ? null + : converter.resolveFunctionImpliedReturnType(); if ( inferredType != null ) { if ( inferredType instanceof ReturnableType ) { return (ReturnableType) inferredType; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaElementArgumentReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaElementArgumentReturnTypeResolver.java index 07b27d326d..d55a8d2a31 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaElementArgumentReturnTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayViaElementArgumentReturnTypeResolver.java @@ -14,10 +14,13 @@ import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.spi.TypeConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A {@link FunctionReturnTypeResolver} that resolves an array type based on the arguments, * which are supposed to be of the element type. The inferred type and implied type have precedence though. @@ -41,10 +44,12 @@ public class ArrayViaElementArgumentReturnTypeResolver implements FunctionReturn @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { - final MappingModelExpressible inferredType = inferredTypeSupplier.get(); + final MappingModelExpressible inferredType = converter == null + ? null + : converter.resolveFunctionImpliedReturnType(); if ( inferredType != null ) { if ( inferredType instanceof ReturnableType ) { return (ReturnableType) inferredType; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ElementViaArrayArgumentReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ElementViaArrayArgumentReturnTypeResolver.java index 03e64ece0d..5e2f993703 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ElementViaArrayArgumentReturnTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ElementViaArrayArgumentReturnTypeResolver.java @@ -15,11 +15,14 @@ import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.BasicPluralType; import org.hibernate.type.spi.TypeConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A {@link FunctionReturnTypeResolver} that resolves the array element type based on an argument. * The inferred type and implied type have precedence though. @@ -37,10 +40,12 @@ public class ElementViaArrayArgumentReturnTypeResolver implements FunctionReturn @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { - final MappingModelExpressible inferredType = inferredTypeSupplier.get(); + final MappingModelExpressible inferredType = converter == null + ? null + : converter.resolveFunctionImpliedReturnType(); if ( inferredType != null ) { if ( inferredType instanceof ReturnableType ) { return (ReturnableType) inferredType; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java index e1d0b3b8be..fe415c72d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java @@ -278,6 +278,7 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType containingTableExpression = rootTableExpression; columnExpression = rootTableKeyColumnNames[ columnPosition ]; } + final NavigableRole role = navigableRole.append( bootPropertyDescriptor.getName() ); final SelectablePath selectablePath; final String columnDefinition; final Long length; @@ -310,18 +311,18 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping( bootPropertyDescriptor.getName(), - navigableRole.append( bootPropertyDescriptor.getName() ), + role, attributeIndex, attributeIndex, bootPropertyDescriptor, declarer, - (BasicType) subtype, + basicValue.getResolution().getLegacyResolvedBasicType(), containingTableExpression, columnExpression, selectablePath, selectable.isFormula(), selectable.getCustomReadExpression(), - selectable.getWriteExpr( ( (BasicType) subtype ).getJdbcMapping(), dialect ), + selectable.getWriteExpr( basicValue.getResolution().getJdbcMapping(), dialect ), columnDefinition, length, precision, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 094ea2ed6f..d29000be55 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -5702,6 +5702,7 @@ public abstract class AbstractEntityPersister } if ( attrType instanceof BasicType ) { + final NavigableRole role = getNavigableRole().append( bootProperty.getName() ); final String attrColumnExpression; final boolean isAttrColumnExpressionFormula; final String customReadExpr; @@ -5774,12 +5775,12 @@ public abstract class AbstractEntityPersister return MappingModelCreationHelper.buildBasicAttributeMapping( attrName, - getNavigableRole().append( bootProperty.getName() ), + role, stateArrayPosition, fetchableIndex, bootProperty, this, - (BasicType) attrType, + (BasicType) value.getType(), tableExpression, attrColumnExpression, null, diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaTupleElement.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaTupleElement.java index fbf5f779a4..dac18206a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaTupleElement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaTupleElement.java @@ -7,6 +7,7 @@ package org.hibernate.query.criteria; import jakarta.persistence.TupleElement; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.type.descriptor.java.EnumJavaType; import org.hibernate.type.descriptor.java.JavaType; @@ -17,10 +18,10 @@ import org.hibernate.type.descriptor.java.JavaType; * @author Steve Ebersole */ public interface JpaTupleElement extends TupleElement, JpaCriteriaNode { - JavaType getJavaTypeDescriptor(); + @Nullable JavaType getJavaTypeDescriptor(); @Override - default Class getJavaType() { + default @Nullable Class getJavaType() { // todo (6.0) : can this signature just return `Class`? return getJavaTypeDescriptor() == null ? null : getJavaTypeDescriptor().getJavaTypeClass(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java index 6b316eb7d1..60e3f89380 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java @@ -33,6 +33,8 @@ import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Representation of a function call in the SQL AST for impls that know how to * render themselves. @@ -44,8 +46,8 @@ public class SelfRenderingFunctionSqlAstExpression private final String functionName; private final FunctionRenderer renderer; private final List sqlAstArguments; - private final ReturnableType type; - private final JdbcMappingContainer expressible; + private final @Nullable ReturnableType type; + private final @Nullable JdbcMappingContainer expressible; /** * @deprecated Use {@link #SelfRenderingFunctionSqlAstExpression(String, FunctionRenderer, List, ReturnableType, JdbcMappingContainer)} instead @@ -55,8 +57,8 @@ public class SelfRenderingFunctionSqlAstExpression String functionName, FunctionRenderingSupport renderer, List sqlAstArguments, - ReturnableType type, - JdbcMappingContainer expressible) { + @Nullable ReturnableType type, + @Nullable JdbcMappingContainer expressible) { this.functionName = functionName; this.renderer = renderer::render; this.sqlAstArguments = sqlAstArguments; @@ -69,8 +71,8 @@ public class SelfRenderingFunctionSqlAstExpression String functionName, FunctionRenderer renderer, List sqlAstArguments, - ReturnableType type, - JdbcMappingContainer expressible) { + @Nullable ReturnableType type, + @Nullable JdbcMappingContainer expressible) { this.functionName = functionName; this.renderer = renderer; this.sqlAstArguments = sqlAstArguments; @@ -108,7 +110,7 @@ public class SelfRenderingFunctionSqlAstExpression return renderer; } - protected ReturnableType getType() { + protected @Nullable ReturnableType getType() { return type; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/NoParamSqmCopyContext.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/NoParamSqmCopyContext.java index dc7c6d7d1c..8a3d960a2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/NoParamSqmCopyContext.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/NoParamSqmCopyContext.java @@ -8,12 +8,14 @@ package org.hibernate.query.sqm.internal; import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * @author Marco Belladelli */ public class NoParamSqmCopyContext extends SimpleSqmCopyContext { @Override - public T getCopy(T original) { + public @Nullable T getCopy(T original) { if ( original instanceof SqmParameter ) { return original; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleSqmCopyContext.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleSqmCopyContext.java index 4071683cff..37e61b8d41 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleSqmCopyContext.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleSqmCopyContext.java @@ -10,6 +10,8 @@ import java.util.IdentityHashMap; import org.hibernate.query.sqm.tree.SqmCopyContext; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * @author Marco Belladelli */ @@ -18,7 +20,7 @@ public class SimpleSqmCopyContext implements SqmCopyContext { @Override @SuppressWarnings( "unchecked" ) - public T getCopy(T original) { + public @Nullable T getCopy(T original) { return (T) map.get( original ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java index f92919f007..5432f1a42b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java @@ -336,10 +336,12 @@ public class TypecheckUtil { * @see TypecheckUtil#assertAssignable(String, SqmPath, SqmTypedNode, SessionFactoryImplementor) */ public static void assertComparable(Expression x, Expression y, SessionFactoryImplementor factory) { - SqmExpression left = (SqmExpression) x; - SqmExpression right = (SqmExpression) y; - if ( left.getTupleLength() != null && right.getTupleLength() != null - && left.getTupleLength().intValue() != right.getTupleLength().intValue() ) { + final SqmExpression left = (SqmExpression) x; + final SqmExpression right = (SqmExpression) y; + final Integer leftTupleLength = left.getTupleLength(); + final Integer rightTupleLength; + if ( leftTupleLength != null && ( rightTupleLength = right.getTupleLength() ) != null + && leftTupleLength.intValue() != rightTupleLength.intValue() ) { throw new SemanticException( "Cannot compare tuples of different lengths" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionReturnTypeResolver.java index f419724124..8fb1fa934e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionReturnTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionReturnTypeResolver.java @@ -9,6 +9,8 @@ package org.hibernate.query.sqm.produce.function; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.ReturnableType; +import org.hibernate.query.sqm.sql.FakeSqmToSqlAstConverter; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.spi.TypeConfiguration; @@ -16,6 +18,8 @@ import org.hibernate.type.spi.TypeConfiguration; import java.util.List; import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Pluggable strategy for resolving a function return type for a specific call. * @@ -32,10 +36,10 @@ public interface FunctionReturnTypeResolver { * of `some_function`. * * @return The resolved type. - * @deprecated Use {@link #resolveFunctionReturnType(ReturnableType, Supplier, List, TypeConfiguration)} instead + * @deprecated Use {@link #resolveFunctionReturnType(ReturnableType, SqmToSqlAstConverter, List, TypeConfiguration)} instead */ @Deprecated(forRemoval = true) - default ReturnableType resolveFunctionReturnType( + default @Nullable ReturnableType resolveFunctionReturnType( ReturnableType impliedType, List> arguments, TypeConfiguration typeConfiguration) { @@ -52,13 +56,44 @@ public interface FunctionReturnTypeResolver { * of `some_function`. * * @return The resolved type. + * @deprecated Use {@link #resolveFunctionReturnType(ReturnableType, SqmToSqlAstConverter, List, TypeConfiguration)} instead */ - default ReturnableType resolveFunctionReturnType( + @Deprecated(forRemoval = true) + default @Nullable ReturnableType resolveFunctionReturnType( ReturnableType impliedType, Supplier> inferredTypeSupplier, List> arguments, TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, arguments, typeConfiguration ); + return resolveFunctionReturnType( impliedType, new FakeSqmToSqlAstConverter( null ) { + @Override + public MappingModelExpressible resolveFunctionImpliedReturnType() { + return inferredTypeSupplier.get(); + } + }, arguments, typeConfiguration ); + } + + /** + * Resolve the return type for a function given its context-implied type and + * the arguments to this call. + *

+ * The context-implied type is the type implied by where the function + * occurs in the query. E.g., for an equality predicate (`something = some_function`) + * the implied type of the return from `some_function` would be defined by the type + * of `some_function`. + * + * @return The resolved type. + */ + default @Nullable ReturnableType resolveFunctionReturnType( + ReturnableType impliedType, + @Nullable SqmToSqlAstConverter converter, + List> arguments, + TypeConfiguration typeConfiguration) { + return resolveFunctionReturnType( + impliedType, + converter == null ? () -> null : converter::resolveFunctionImpliedReturnType, + arguments, + typeConfiguration + ); } /** 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 59fc361a7b..fc206e14a0 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 @@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.SqmExpressible; 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.expression.NullSqmExpressible; import org.hibernate.sql.ast.tree.SqlAstNode; @@ -26,6 +27,8 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.type.BasicType; import org.hibernate.type.spi.TypeConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * @author Steve Ebersole */ @@ -51,15 +54,7 @@ public class StandardFunctionReturnTypeResolvers { @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - List> arguments, - TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration ); - } - - @Override - public ReturnableType resolveFunctionReturnType( - ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { return isAssignableTo( invariantType, impliedType ) ? impliedType : invariantType; @@ -84,15 +79,7 @@ public class StandardFunctionReturnTypeResolvers { @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - List> arguments, - TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration ); - } - - @Override - public ReturnableType resolveFunctionReturnType( - ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { ReturnableType argType = extractArgumentType( arguments, argPosition ); @@ -133,15 +120,7 @@ public class StandardFunctionReturnTypeResolvers { @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - List> arguments, - TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration ); - } - - @Override - public ReturnableType resolveFunctionReturnType( - ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { for ( int i = 0; i < arguments.size(); i++ ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 7688634e99..508b5aa5a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5364,6 +5364,11 @@ public abstract class BaseSqmToSqlAstConverter extends Base return inferredType; } + @Override + public boolean isInTypeInference() { + return inImpliedResultTypeInference || inTypeInference; + } + @Override public MappingModelExpressible resolveFunctionImpliedReturnType() { if ( inImpliedResultTypeInference || functionImpliedResultTypeAccess == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FakeSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FakeSqmToSqlAstConverter.java index 7e8f09985a..ada844cddc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FakeSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FakeSqmToSqlAstConverter.java @@ -95,6 +95,11 @@ public class FakeSqmToSqlAstConverter extends BaseSemanticQueryWalker implements public void registerQueryTransformer(QueryTransformer transformer) { } + @Override + public boolean isInTypeInference() { + return false; + } + @Override public MappingModelExpressible resolveFunctionImpliedReturnType() { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmToSqlAstConverter.java index 378fef24be..72f6da54b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmToSqlAstConverter.java @@ -35,6 +35,12 @@ public interface SqmToSqlAstConverter extends SemanticQueryWalker, SqlAs void registerQueryTransformer(QueryTransformer transformer); + /** + * Returns whether the state of the translation is currently in type inference mode. + * This is useful to avoid type inference based on other incomplete inference information. + */ + boolean isInTypeInference(); + /** * Returns the function return type implied from the context within which it is used. * If there is no current function being processed or no context implied type, the return is null. diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCopyContext.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCopyContext.java index 2d55ed4bcd..b57278264f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCopyContext.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCopyContext.java @@ -10,12 +10,14 @@ import org.hibernate.Incubating; import org.hibernate.query.sqm.internal.NoParamSqmCopyContext; import org.hibernate.query.sqm.internal.SimpleSqmCopyContext; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * */ public interface SqmCopyContext { - T getCopy(T original); + @Nullable T getCopy(T original); T registerCopy(T original, T copy); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmTypedNode.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmTypedNode.java index 4c48e51db8..8b6ac9c9dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmTypedNode.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmTypedNode.java @@ -9,6 +9,8 @@ package org.hibernate.query.sqm.tree; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.type.descriptor.java.JavaType; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Optional contract for SqmNode implementations which are * typed @@ -19,17 +21,17 @@ public interface SqmTypedNode extends SqmNode, SqmExpressibleAccessor, Sqm /** * The Java type descriptor for this node. */ - default JavaType getNodeJavaType() { + default @Nullable JavaType getNodeJavaType() { final SqmExpressible nodeType = getNodeType(); return nodeType != null ? nodeType.getExpressibleJavaType() : null; } @Override - default SqmExpressible getExpressible() { + default @Nullable SqmExpressible getExpressible() { return getNodeType(); } - SqmExpressible getNodeType(); + @Nullable SqmExpressible getNodeType(); @Override SqmTypedNode copy(SqmCopyContext context); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java index 7ccdd46fad..11b3966a42 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.hibernate.internal.util.NullnessUtil; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.model.domain.DomainType; @@ -79,12 +80,12 @@ public abstract class AbstractSqmPath extends AbstractSqmExpression implem @Override public SqmPathSource getNodeType() { - return (SqmPathSource) super.getNodeType(); + return (SqmPathSource) NullnessUtil.castNonNull( super.getNodeType() ); } @Override public SqmPathSource getReferencedPathSource() { - return (SqmPathSource) super.getNodeType(); + return (SqmPathSource) NullnessUtil.castNonNull( super.getNodeType() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java index 77966e14c6..7daa4e0866 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java @@ -14,6 +14,7 @@ import java.util.function.Consumer; import jakarta.persistence.metamodel.MapAttribute; import jakarta.persistence.metamodel.PluralAttribute; import jakarta.persistence.metamodel.SingularAttribute; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.SemanticException; @@ -104,7 +105,7 @@ public interface SqmPath extends SqmExpression, SemanticPathPart, JpaPath< SqmPathSource getNodeType(); @Override - default void applyInferableType(SqmExpressible type) { + default void applyInferableType(@Nullable SqmExpressible type) { // do nothing } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java index 88f02c5f1e..b9eadf62c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java @@ -20,6 +20,7 @@ import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.type.descriptor.java.JavaType; import jakarta.persistence.criteria.Expression; +import org.checkerframework.checker.nullness.qual.Nullable; import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType2; @@ -28,7 +29,7 @@ import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType2; */ public abstract class AbstractSqmExpression extends AbstractJpaSelection implements SqmExpression { - public AbstractSqmExpression(SqmExpressible type, NodeBuilder criteriaBuilder) { + public AbstractSqmExpression(@Nullable SqmExpressible type, NodeBuilder criteriaBuilder) { super( type, criteriaBuilder ); } @@ -38,10 +39,10 @@ public abstract class AbstractSqmExpression extends AbstractJpaSelection i } @Override - public void applyInferableType(SqmExpressible type) { + public void applyInferableType(@Nullable SqmExpressible type) { } - protected void internalApplyInferableType(SqmExpressible newType) { + protected void internalApplyInferableType(@Nullable SqmExpressible newType) { SqmTreeCreationLogger.LOGGER.debugf( "Applying inferable type to SqmExpression [%s] : %s -> %s", this, @@ -138,13 +139,7 @@ public abstract class AbstractSqmExpression extends AbstractJpaSelection i } @Override - public JpaSelection alias(String name) { - setAlias( name ); - return this; - } - - @Override - public JavaType getJavaTypeDescriptor() { + public @Nullable JavaType getJavaTypeDescriptor() { return getNodeType() == null ? null : getNodeType().getExpressibleJavaType(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java index d18472f14b..0167a0b610 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java @@ -11,6 +11,8 @@ import org.hibernate.query.BindableType; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmExpressible; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Common support for SqmParameter impls * @@ -28,7 +30,7 @@ public abstract class AbstractSqmParameter extends AbstractSqmExpression i } @Override - public void applyInferableType(SqmExpressible type) { + public void applyInferableType(@Nullable SqmExpressible type) { if ( type != null ) { if ( type instanceof PluralPersistentAttribute ) { final PluralPersistentAttribute pluralPersistentAttribute = diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java index e9d6ed4b39..87d6343ce6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java @@ -12,6 +12,8 @@ import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.select.SqmSubQuery; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * @author Gavin King */ @@ -25,7 +27,7 @@ public class SqmAny extends AbstractSqmExpression { } @Override - public SqmExpressible getNodeType() { + public @Nullable SqmExpressible getNodeType() { return subquery.getNodeType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java index 22e9e35a2a..d9e1e1c365 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java @@ -12,6 +12,8 @@ import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.select.SqmSubQuery; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * @author Gavin King */ @@ -25,7 +27,7 @@ public class SqmEvery extends AbstractSqmExpression { } @Override - public SqmExpressible getNodeType() { + public @Nullable SqmExpressible getNodeType() { return subquery.getNodeType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExpression.java index 0d1e85f4de..8ed07fae21 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExpression.java @@ -11,6 +11,7 @@ import java.math.BigInteger; import java.util.Collection; import java.util.function.Consumer; import jakarta.persistence.criteria.Expression; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Internal; import org.hibernate.query.ReturnableType; @@ -40,7 +41,7 @@ public interface SqmExpression extends SqmSelectableNode, JpaExpression * Can change as a result of calls to {@link #applyInferableType} */ @Override - SqmExpressible getNodeType(); + @Nullable SqmExpressible getNodeType(); /** * Used to apply type information based on the expression's usage @@ -51,7 +52,7 @@ public interface SqmExpression extends SqmSelectableNode, JpaExpression * an implicit cast) */ @Internal - void applyInferableType(SqmExpressible type); + void applyInferableType(@Nullable SqmExpressible type); @Override default void visitSubSelectableNodes(Consumer> jpaSelectionConsumer) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java index 5d607d7524..1fb04fc918 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java @@ -29,6 +29,7 @@ import org.hibernate.query.sqm.tree.select.SqmSelectableNode; import org.hibernate.type.descriptor.java.JavaType; import jakarta.persistence.criteria.Expression; +import org.checkerframework.checker.nullness.qual.Nullable; /** * @author Steve Ebersole @@ -111,7 +112,7 @@ public class SqmFieldLiteral implements SqmExpression, SqmExpressible, } @Override - public void applyInferableType(SqmExpressible type) { + public void applyInferableType(@Nullable SqmExpressible type) { } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java index 557f52eea6..f27bae151b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java @@ -11,6 +11,7 @@ import java.math.BigInteger; import java.util.Locale; import org.hibernate.HibernateException; +import org.hibernate.internal.util.NullnessUtil; import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -67,7 +68,7 @@ public class SqmHqlNumericLiteral extends SqmLiteral { @Override public BasicDomainType getNodeType() { - return (BasicDomainType) super.getNodeType(); + return (BasicDomainType) NullnessUtil.castNonNull( super.getNodeType() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java index d2e49c063f..0decadee7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java @@ -16,6 +16,8 @@ import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.select.SqmSortSpecification; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * @author Christian Beikov * @author Marco Belladelli @@ -86,7 +88,7 @@ public class SqmOver extends AbstractSqmExpression { } @Override - public SqmExpressible getNodeType() { + public @Nullable SqmExpressible getNodeType() { return expression.getNodeType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java index b169c700b6..d91e03ba58 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java @@ -9,7 +9,6 @@ package org.hibernate.query.sqm.tree.expression; import org.hibernate.HibernateException; import org.hibernate.query.BindableType; import org.hibernate.query.criteria.JpaParameterExpression; -import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.SqmCopyContext; /** @@ -65,9 +64,6 @@ public interface SqmParameter extends SqmExpression, JpaParameterExpressio */ BindableType getAnticipatedType(); - @Override - SqmExpressible getNodeType(); - /** * Make a copy */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaSelection.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaSelection.java index 2ae461f6bc..2b27869ec8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaSelection.java @@ -13,6 +13,8 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.select.SqmSelectableNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Base support for {@link JpaSelection} impls. * @@ -21,7 +23,7 @@ import org.hibernate.query.sqm.tree.select.SqmSelectableNode; public abstract class AbstractJpaSelection extends AbstractJpaTupleElement implements SqmSelectableNode, JpaSelection { - protected AbstractJpaSelection(SqmExpressible sqmExpressible, NodeBuilder criteriaBuilder) { + protected AbstractJpaSelection(@Nullable SqmExpressible sqmExpressible, NodeBuilder criteriaBuilder) { super( sqmExpressible, criteriaBuilder ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaTupleElement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaTupleElement.java index 62e6ac78d0..3d9a2607c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaTupleElement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/AbstractJpaTupleElement.java @@ -13,6 +13,8 @@ import org.hibernate.query.sqm.tree.AbstractSqmNode; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmVisitableNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Base support for {@link JpaTupleElement} impls * @@ -22,10 +24,10 @@ public abstract class AbstractJpaTupleElement extends AbstractSqmNode implements SqmVisitableNode, JpaTupleElement { - private SqmExpressible expressibleType; - private String alias; + private @Nullable SqmExpressible expressibleType; + private @Nullable String alias; - protected AbstractJpaTupleElement(SqmExpressible expressibleType, NodeBuilder criteriaBuilder) { + protected AbstractJpaTupleElement(@Nullable SqmExpressible expressibleType, NodeBuilder criteriaBuilder) { super( criteriaBuilder ); setExpressibleType( expressibleType ); } @@ -35,22 +37,22 @@ public abstract class AbstractJpaTupleElement } @Override - public String getAlias() { + public @Nullable String getAlias() { return alias; } /** * Protected access to set the alias. */ - protected void setAlias(String alias) { + protected void setAlias(@Nullable String alias) { this.alias = alias; } - public SqmExpressible getNodeType() { + public @Nullable SqmExpressible getNodeType() { return expressibleType; } - protected final void setExpressibleType(SqmExpressible expressibleType) { + protected final void setExpressibleType(@Nullable SqmExpressible expressibleType) { //noinspection unchecked this.expressibleType = (SqmExpressible) expressibleType; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java index dd5f9e4518..87766ca7fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java @@ -69,6 +69,7 @@ import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Selection; import jakarta.persistence.criteria.SetJoin; +import org.checkerframework.checker.nullness.qual.Nullable; /** * @author Steve Ebersole @@ -600,12 +601,12 @@ public class SqmSubQuery extends AbstractSqmSelectQuery implements SqmSele } @Override - public SqmExpressible getNodeType() { + public @Nullable SqmExpressible getNodeType() { return expressibleType; } @Override - public void applyInferableType(SqmExpressible type) { + public void applyInferableType(@Nullable SqmExpressible type) { //noinspection unchecked expressibleType = (SqmExpressible) type; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java index 35fdea6314..0b3886879c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java @@ -18,6 +18,7 @@ import org.hibernate.collection.spi.MapSemantics; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.java.ArrayJavaType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.jdbc.JdbcType; @@ -58,9 +59,14 @@ public class CollectionJavaType extends AbstractClassJavaType { TypeConfiguration typeConfiguration) { final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); - final JavaType valueDescriptor = javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] ); switch ( semantics.getCollectionClassification() ) { case ARRAY: + //noinspection unchecked + return (JavaType) new ArrayJavaType<>( + javaTypeRegistry.resolveDescriptor( + ( (Class) parameterizedType.getRawType() ).getComponentType() + ) + ); case BAG: case ID_BAG: case LIST: @@ -70,7 +76,7 @@ public class CollectionJavaType extends AbstractClassJavaType { //noinspection unchecked,rawtypes return new BasicCollectionJavaType( parameterizedType, - valueDescriptor, + javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] ), semantics ); @@ -82,7 +88,7 @@ public class CollectionJavaType extends AbstractClassJavaType { new MapMutabilityPlan<>( (MapSemantics, Object, Object>) semantics, javaTypeRegistry.resolveDescriptor( actualTypeArguments[0] ), - valueDescriptor + javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] ) ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeJavaClassMappings.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeJavaClassMappings.java index c20d40221d..5801185f7c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeJavaClassMappings.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeJavaClassMappings.java @@ -192,7 +192,7 @@ public class JdbcTypeJavaClassMappings { workMap.put( SqlTypes.ARRAY, Array.class ); workMap.put( SqlTypes.STRUCT, Struct.class ); workMap.put( SqlTypes.REF, Ref.class ); - workMap.put( SqlTypes.JAVA_OBJECT, Class.class ); + workMap.put( SqlTypes.JAVA_OBJECT, Object.class ); workMap.put( SqlTypes.ROWID, RowId.class ); workMap.put( SqlTypes.SQLXML, SQLXML.class ); workMap.put( SqlTypes.UUID, UUID.class ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/DynamicParameterizedTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/DynamicParameterizedTypeTest.java index 847bb6db90..b24b031afb 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/DynamicParameterizedTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/DynamicParameterizedTypeTest.java @@ -32,6 +32,7 @@ import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; @@ -48,6 +49,8 @@ import java.util.Date; import java.util.List; import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.junit.jupiter.api.Assertions.*; /** @@ -137,15 +140,7 @@ public class DynamicParameterizedTypeTest { @Override public ReturnableType resolveFunctionReturnType( ReturnableType impliedType, - List> arguments, - TypeConfiguration typeConfiguration) { - return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration ); - } - - @Override - public ReturnableType resolveFunctionReturnType( - ReturnableType impliedType, - Supplier> inferredTypeSupplier, + @Nullable SqmToSqlAstConverter converter, List> arguments, TypeConfiguration typeConfiguration) { SqmTypedNode sqmTypedNode = arguments.get(0); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java index e105f4fc5e..cc29570591 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java @@ -138,7 +138,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { QueryEngine queryEngine) { super( orderByFragmentFunction, - null, + (sqlAppender, sqlAstArguments, returnType, walker) -> {}, arguments, impliedResultType, orderByFragmentFunction.getArgumentsValidator(), diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java index 0978e957a0..f96dad1374 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java @@ -2558,9 +2558,14 @@ public class AnnotationMetaEntity extends AnnotationMeta { // TODO: anything more we can do here? e.g. check constructor try { final Class javaResultType = selection.getJavaType(); - final TypeElement typeElement = context.getTypeElementForFullyQualifiedName( javaResultType.getName() ); - final Types types = context.getTypeUtils(); - returnTypeCorrect = types.isAssignable( returnType, types.erasure( typeElement.asType() ) ); + if ( javaResultType == null ) { + returnTypeCorrect = true; + } + else { + final TypeElement typeElement = context.getTypeElementForFullyQualifiedName( javaResultType.getName() ); + final Types types = context.getTypeUtils(); + returnTypeCorrect = context.getTypeUtils().isAssignable( returnType, types.erasure( typeElement.asType() ) ); + } } catch (Exception e) { //ignore @@ -2670,7 +2675,8 @@ public class AnnotationMetaEntity extends AnnotationMeta { } private static boolean parameterMatches(VariableElement parameter, JpaSelection item) { - return parameterMatches( parameter.asType(), item.getJavaType() ); + final Class javaType = item.getJavaType(); + return javaType != null && parameterMatches( parameter.asType(), javaType ); } private static boolean parameterMatches(TypeMirror parameterType, Class itemType) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java index 042b2af39a..69222111b4 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java @@ -104,8 +104,9 @@ class NamedQueryMethod implements MetaAttribute { else { final List> items = select.getQuerySpec().getSelectClause().getSelectionItems(); - if ( items.size() == 1 ) { - final String typeName = items.get(0).getExpressible().getTypeName(); + final SqmExpressible expressible; + if ( items.size() == 1 && ( expressible = items.get( 0 ).getExpressible() ) != null ) { + final String typeName = expressible.getTypeName(); final TypeElement entityType = entityType( typeName ); return entityType == null ? typeName : entityType.getQualifiedName().toString();