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
This commit is contained in:
Christian Beikov 2024-05-03 14:41:37 +02:00
parent 75e1f17139
commit 940c898ecf
41 changed files with 208 additions and 145 deletions

View File

@ -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<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
final SqmExpressible<?> expressible = arguments.get( 0 ).getExpressible();

View File

@ -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<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return type == null ? null : typeConfiguration.getBasicTypeRegistry().resolve( type );

View File

@ -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<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
if ( impliedType != null ) {

View File

@ -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<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
final BasicType<?> invariantType;

View File

@ -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<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> 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;

View File

@ -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<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> 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;

View File

@ -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<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> 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;

View File

@ -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,

View File

@ -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,

View File

@ -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<T> extends TupleElement<T>, JpaCriteriaNode {
JavaType<T> getJavaTypeDescriptor();
@Nullable JavaType<T> getJavaTypeDescriptor();
@Override
default Class<? extends T> getJavaType() {
default @Nullable Class<? extends T> getJavaType() {
// todo (6.0) : can this signature just return `Class<T>`?
return getJavaTypeDescriptor() == null ? null : getJavaTypeDescriptor().getJavaTypeClass();
}

View File

@ -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<? extends SqlAstNode> 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<? extends SqlAstNode> 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<? extends SqlAstNode> 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;
}

View File

@ -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> T getCopy(T original) {
public <T> @Nullable T getCopy(T original) {
if ( original instanceof SqmParameter<?> ) {
return original;
}

View File

@ -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> T getCopy(T original) {
public <T> @Nullable T getCopy(T original) {
return (T) map.get( original );
}

View File

@ -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" );
}

View File

@ -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<? extends SqmTypedNode<?>> 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<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> 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.
* <p>
* The <em>context-implied</em> 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<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType(
impliedType,
converter == null ? () -> null : converter::resolveFunctionImpliedReturnType,
arguments,
typeConfiguration
);
}
/**

View File

@ -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<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return isAssignableTo( invariantType, impliedType ) ? impliedType : invariantType;
@ -84,15 +79,7 @@ public class StandardFunctionReturnTypeResolvers {
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
ReturnableType<?> argType = extractArgumentType( arguments, argPosition );
@ -133,15 +120,7 @@ public class StandardFunctionReturnTypeResolvers {
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
for ( int i = 0; i < arguments.size(); i++ ) {

View File

@ -5364,6 +5364,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return inferredType;
}
@Override
public boolean isInTypeInference() {
return inImpliedResultTypeInference || inTypeInference;
}
@Override
public MappingModelExpressible<?> resolveFunctionImpliedReturnType() {
if ( inImpliedResultTypeInference || functionImpliedResultTypeAccess == null ) {

View File

@ -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;

View File

@ -35,6 +35,12 @@ public interface SqmToSqlAstConverter extends SemanticQueryWalker<Object>, 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 <code>null</code>.

View File

@ -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> T getCopy(T original);
<T> @Nullable T getCopy(T original);
<T> T registerCopy(T original, T copy);

View File

@ -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<T> extends SqmNode, SqmExpressibleAccessor<T>, Sqm
/**
* The Java type descriptor for this node.
*/
default JavaType<T> getNodeJavaType() {
default @Nullable JavaType<T> getNodeJavaType() {
final SqmExpressible<T> nodeType = getNodeType();
return nodeType != null ? nodeType.getExpressibleJavaType() : null;
}
@Override
default SqmExpressible<T> getExpressible() {
default @Nullable SqmExpressible<T> getExpressible() {
return getNodeType();
}
SqmExpressible<T> getNodeType();
@Nullable SqmExpressible<T> getNodeType();
@Override
SqmTypedNode<T> copy(SqmCopyContext context);

View File

@ -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<T> extends AbstractSqmExpression<T> implem
@Override
public SqmPathSource<T> getNodeType() {
return (SqmPathSource<T>) super.getNodeType();
return (SqmPathSource<T>) NullnessUtil.castNonNull( super.getNodeType() );
}
@Override
public SqmPathSource<T> getReferencedPathSource() {
return (SqmPathSource<T>) super.getNodeType();
return (SqmPathSource<T>) NullnessUtil.castNonNull( super.getNodeType() );
}
@Override

View File

@ -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<T> extends SqmExpression<T>, SemanticPathPart, JpaPath<
SqmPathSource<T> getNodeType();
@Override
default void applyInferableType(SqmExpressible<?> type) {
default void applyInferableType(@Nullable SqmExpressible<?> type) {
// do nothing
}

View File

@ -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<T> extends AbstractJpaSelection<T> implements SqmExpression<T> {
public AbstractSqmExpression(SqmExpressible<? super T> type, NodeBuilder criteriaBuilder) {
public AbstractSqmExpression(@Nullable SqmExpressible<? super T> type, NodeBuilder criteriaBuilder) {
super( type, criteriaBuilder );
}
@ -38,10 +39,10 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> 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<T> extends AbstractJpaSelection<T> i
}
@Override
public JpaSelection<T> alias(String name) {
setAlias( name );
return this;
}
@Override
public JavaType<T> getJavaTypeDescriptor() {
public @Nullable JavaType<T> getJavaTypeDescriptor() {
return getNodeType() == null ? null : getNodeType().getExpressibleJavaType();
}
}

View File

@ -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<T> extends AbstractSqmExpression<T> i
}
@Override
public void applyInferableType(SqmExpressible<?> type) {
public void applyInferableType(@Nullable SqmExpressible<?> type) {
if ( type != null ) {
if ( type instanceof PluralPersistentAttribute ) {
final PluralPersistentAttribute<?, ?, ?> pluralPersistentAttribute =

View File

@ -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<T> extends AbstractSqmExpression<T> {
}
@Override
public SqmExpressible<T> getNodeType() {
public @Nullable SqmExpressible<T> getNodeType() {
return subquery.getNodeType();
}

View File

@ -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<T> extends AbstractSqmExpression<T> {
}
@Override
public SqmExpressible<T> getNodeType() {
public @Nullable SqmExpressible<T> getNodeType() {
return subquery.getNodeType();
}

View File

@ -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<T> extends SqmSelectableNode<T>, JpaExpression<T>
* Can change as a result of calls to {@link #applyInferableType}
*/
@Override
SqmExpressible<T> getNodeType();
@Nullable SqmExpressible<T> getNodeType();
/**
* Used to apply type information based on the expression's usage
@ -51,7 +52,7 @@ public interface SqmExpression<T> extends SqmSelectableNode<T>, JpaExpression<T>
* an implicit cast)
*/
@Internal
void applyInferableType(SqmExpressible<?> type);
void applyInferableType(@Nullable SqmExpressible<?> type);
@Override
default void visitSubSelectableNodes(Consumer<SqmSelectableNode<?>> jpaSelectionConsumer) {

View File

@ -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<T> implements SqmExpression<T>, SqmExpressible<T>,
}
@Override
public void applyInferableType(SqmExpressible<?> type) {
public void applyInferableType(@Nullable SqmExpressible<?> type) {
}
@Override

View File

@ -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<N extends Number> extends SqmLiteral<N> {
@Override
public BasicDomainType<N> getNodeType() {
return (BasicDomainType<N>) super.getNodeType();
return (BasicDomainType<N>) NullnessUtil.castNonNull( super.getNodeType() );
}
@Override

View File

@ -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<T> extends AbstractSqmExpression<T> {
}
@Override
public SqmExpressible<T> getNodeType() {
public @Nullable SqmExpressible<T> getNodeType() {
return expression.getNodeType();
}

View File

@ -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<T> extends SqmExpression<T>, JpaParameterExpressio
*/
BindableType<T> getAnticipatedType();
@Override
SqmExpressible<T> getNodeType();
/**
* Make a copy
*/

View File

@ -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<T>
extends AbstractJpaTupleElement<T>
implements SqmSelectableNode<T>, JpaSelection<T> {
protected AbstractJpaSelection(SqmExpressible<? super T> sqmExpressible, NodeBuilder criteriaBuilder) {
protected AbstractJpaSelection(@Nullable SqmExpressible<? super T> sqmExpressible, NodeBuilder criteriaBuilder) {
super( sqmExpressible, criteriaBuilder );
}

View File

@ -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<T>
extends AbstractSqmNode
implements SqmVisitableNode, JpaTupleElement<T> {
private SqmExpressible<T> expressibleType;
private String alias;
private @Nullable SqmExpressible<T> expressibleType;
private @Nullable String alias;
protected AbstractJpaTupleElement(SqmExpressible<? super T> expressibleType, NodeBuilder criteriaBuilder) {
protected AbstractJpaTupleElement(@Nullable SqmExpressible<? super T> expressibleType, NodeBuilder criteriaBuilder) {
super( criteriaBuilder );
setExpressibleType( expressibleType );
}
@ -35,22 +37,22 @@ public abstract class AbstractJpaTupleElement<T>
}
@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<T> getNodeType() {
public @Nullable SqmExpressible<T> getNodeType() {
return expressibleType;
}
protected final void setExpressibleType(SqmExpressible<?> expressibleType) {
protected final void setExpressibleType(@Nullable SqmExpressible<?> expressibleType) {
//noinspection unchecked
this.expressibleType = (SqmExpressible<T>) expressibleType;
}

View File

@ -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<T> extends AbstractSqmSelectQuery<T> implements SqmSele
}
@Override
public SqmExpressible<T> getNodeType() {
public @Nullable SqmExpressible<T> getNodeType() {
return expressibleType;
}
@Override
public void applyInferableType(SqmExpressible<?> type) {
public void applyInferableType(@Nullable SqmExpressible<?> type) {
//noinspection unchecked
expressibleType = (SqmExpressible<T>) type;
}

View File

@ -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<C> extends AbstractClassJavaType<C> {
TypeConfiguration typeConfiguration) {
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
final JavaType<Object> valueDescriptor = javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] );
switch ( semantics.getCollectionClassification() ) {
case ARRAY:
//noinspection unchecked
return (JavaType<C>) new ArrayJavaType<>(
javaTypeRegistry.resolveDescriptor(
( (Class<?>) parameterizedType.getRawType() ).getComponentType()
)
);
case BAG:
case ID_BAG:
case LIST:
@ -70,7 +76,7 @@ public class CollectionJavaType<C> extends AbstractClassJavaType<C> {
//noinspection unchecked,rawtypes
return new BasicCollectionJavaType(
parameterizedType,
valueDescriptor,
javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] ),
semantics
);
@ -82,7 +88,7 @@ public class CollectionJavaType<C> extends AbstractClassJavaType<C> {
new MapMutabilityPlan<>(
(MapSemantics<Map<Object, Object>, Object, Object>) semantics,
javaTypeRegistry.resolveDescriptor( actualTypeArguments[0] ),
valueDescriptor
javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] )
)
);
}

View File

@ -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 );

View File

@ -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<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
@Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) {
SqmTypedNode<?> sqmTypedNode = arguments.get(0);

View File

@ -138,7 +138,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
QueryEngine queryEngine) {
super(
orderByFragmentFunction,
null,
(sqlAppender, sqlAstArguments, returnType, walker) -> {},
arguments,
impliedResultType,
orderByFragmentFunction.getArgumentsValidator(),

View File

@ -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();
if ( javaResultType == null ) {
returnTypeCorrect = true;
}
else {
final TypeElement typeElement = context.getTypeElementForFullyQualifiedName( javaResultType.getName() );
final Types types = context.getTypeUtils();
returnTypeCorrect = types.isAssignable( returnType, types.erasure( typeElement.asType() ) );
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) {

View File

@ -104,8 +104,9 @@ class NamedQueryMethod implements MetaAttribute {
else {
final List<SqmSelectableNode<?>> 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();