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.FunctionArgumentException;
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; 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.descriptor.jdbc.ObjectJdbcType;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC;
/** /**
@ -240,7 +243,7 @@ public class AvgFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier, @Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
final SqmExpressible<?> expressible = arguments.get( 0 ).getExpressible(); final SqmExpressible<?> expressible = arguments.get( 0 ).getExpressible();

View File

@ -10,15 +10,17 @@ import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.function.NamedSqmFunctionDescriptor; import org.hibernate.query.sqm.function.NamedSqmFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.BasicTypeReference; import org.hibernate.type.BasicTypeReference;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Simplified API allowing users to contribute * Simplified API allowing users to contribute
* {@link org.hibernate.query.sqm.function.SqmFunctionDescriptor}s * {@link org.hibernate.query.sqm.function.SqmFunctionDescriptor}s
@ -42,15 +44,7 @@ public class StandardSQLFunction extends NamedSqmFunctionDescriptor {
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, @Nullable SqmToSqlAstConverter converter,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
return type == null ? null : typeConfiguration.getBasicTypeRegistry().resolve( type ); 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.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
@ -21,6 +22,8 @@ import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.function.Supplier; 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.extractArgumentType;
import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.extractArgumentValuedMapping; import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.extractArgumentValuedMapping;
import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.SqlTypes.*;
@ -59,15 +62,7 @@ class SumReturnTypeResolver implements FunctionReturnTypeResolver {
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, @Nullable SqmToSqlAstConverter converter,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
if ( impliedType != null ) { 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.StandardFunctionArgumentTypeResolvers;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer; 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.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
@ -35,6 +36,7 @@ import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL;
@ -129,15 +131,7 @@ public class TimestampdiffFunction
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, @Nullable SqmToSqlAstConverter converter,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
final BasicType<?> invariantType; 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.ReturnableType;
import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicPluralType;
import org.hibernate.type.spi.TypeConfiguration; 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. * A {@link FunctionReturnTypeResolver} that resolves the array type based on an argument.
* The inferred type and implied type have precedence though. * The inferred type and implied type have precedence though.
@ -37,10 +40,12 @@ public class ArrayViaArgumentReturnTypeResolver implements FunctionReturnTypeRes
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier, @Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
final MappingModelExpressible<?> inferredType = inferredTypeSupplier.get(); final MappingModelExpressible<?> inferredType = converter == null
? null
: converter.resolveFunctionImpliedReturnType();
if ( inferredType != null ) { if ( inferredType != null ) {
if ( inferredType instanceof ReturnableType<?> ) { if ( inferredType instanceof ReturnableType<?> ) {
return (ReturnableType<?>) inferredType; 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.metamodel.model.domain.DomainType;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.spi.TypeConfiguration; 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, * 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. * 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 @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier, @Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
final MappingModelExpressible<?> inferredType = inferredTypeSupplier.get(); final MappingModelExpressible<?> inferredType = converter == null
? null
: converter.resolveFunctionImpliedReturnType();
if ( inferredType != null ) { if ( inferredType != null ) {
if ( inferredType instanceof ReturnableType<?> ) { if ( inferredType instanceof ReturnableType<?> ) {
return (ReturnableType<?>) inferredType; 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.ReturnableType;
import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicPluralType;
import org.hibernate.type.spi.TypeConfiguration; 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. * A {@link FunctionReturnTypeResolver} that resolves the array element type based on an argument.
* The inferred type and implied type have precedence though. * The inferred type and implied type have precedence though.
@ -37,10 +40,12 @@ public class ElementViaArrayArgumentReturnTypeResolver implements FunctionReturn
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier, @Nullable SqmToSqlAstConverter converter,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
final MappingModelExpressible<?> inferredType = inferredTypeSupplier.get(); final MappingModelExpressible<?> inferredType = converter == null
? null
: converter.resolveFunctionImpliedReturnType();
if ( inferredType != null ) { if ( inferredType != null ) {
if ( inferredType instanceof ReturnableType<?> ) { if ( inferredType instanceof ReturnableType<?> ) {
return (ReturnableType<?>) inferredType; return (ReturnableType<?>) inferredType;

View File

@ -278,6 +278,7 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
containingTableExpression = rootTableExpression; containingTableExpression = rootTableExpression;
columnExpression = rootTableKeyColumnNames[ columnPosition ]; columnExpression = rootTableKeyColumnNames[ columnPosition ];
} }
final NavigableRole role = navigableRole.append( bootPropertyDescriptor.getName() );
final SelectablePath selectablePath; final SelectablePath selectablePath;
final String columnDefinition; final String columnDefinition;
final Long length; final Long length;
@ -310,18 +311,18 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping( attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping(
bootPropertyDescriptor.getName(), bootPropertyDescriptor.getName(),
navigableRole.append( bootPropertyDescriptor.getName() ), role,
attributeIndex, attributeIndex,
attributeIndex, attributeIndex,
bootPropertyDescriptor, bootPropertyDescriptor,
declarer, declarer,
(BasicType<?>) subtype, basicValue.getResolution().getLegacyResolvedBasicType(),
containingTableExpression, containingTableExpression,
columnExpression, columnExpression,
selectablePath, selectablePath,
selectable.isFormula(), selectable.isFormula(),
selectable.getCustomReadExpression(), selectable.getCustomReadExpression(),
selectable.getWriteExpr( ( (BasicType<?>) subtype ).getJdbcMapping(), dialect ), selectable.getWriteExpr( basicValue.getResolution().getJdbcMapping(), dialect ),
columnDefinition, columnDefinition,
length, length,
precision, precision,

View File

@ -5702,6 +5702,7 @@ public abstract class AbstractEntityPersister
} }
if ( attrType instanceof BasicType ) { if ( attrType instanceof BasicType ) {
final NavigableRole role = getNavigableRole().append( bootProperty.getName() );
final String attrColumnExpression; final String attrColumnExpression;
final boolean isAttrColumnExpressionFormula; final boolean isAttrColumnExpressionFormula;
final String customReadExpr; final String customReadExpr;
@ -5774,12 +5775,12 @@ public abstract class AbstractEntityPersister
return MappingModelCreationHelper.buildBasicAttributeMapping( return MappingModelCreationHelper.buildBasicAttributeMapping(
attrName, attrName,
getNavigableRole().append( bootProperty.getName() ), role,
stateArrayPosition, stateArrayPosition,
fetchableIndex, fetchableIndex,
bootProperty, bootProperty,
this, this,
(BasicType<?>) attrType, (BasicType<?>) value.getType(),
tableExpression, tableExpression,
attrColumnExpression, attrColumnExpression,
null, null,

View File

@ -7,6 +7,7 @@
package org.hibernate.query.criteria; package org.hibernate.query.criteria;
import jakarta.persistence.TupleElement; import jakarta.persistence.TupleElement;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.type.descriptor.java.EnumJavaType; import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
@ -17,10 +18,10 @@ import org.hibernate.type.descriptor.java.JavaType;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface JpaTupleElement<T> extends TupleElement<T>, JpaCriteriaNode { public interface JpaTupleElement<T> extends TupleElement<T>, JpaCriteriaNode {
JavaType<T> getJavaTypeDescriptor(); @Nullable JavaType<T> getJavaTypeDescriptor();
@Override @Override
default Class<? extends T> getJavaType() { default @Nullable Class<? extends T> getJavaType() {
// todo (6.0) : can this signature just return `Class<T>`? // todo (6.0) : can this signature just return `Class<T>`?
return getJavaTypeDescriptor() == null ? null : getJavaTypeDescriptor().getJavaTypeClass(); 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.descriptor.java.JavaType;
import org.hibernate.type.spi.TypeConfiguration; 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 * Representation of a function call in the SQL AST for impls that know how to
* render themselves. * render themselves.
@ -44,8 +46,8 @@ public class SelfRenderingFunctionSqlAstExpression
private final String functionName; private final String functionName;
private final FunctionRenderer renderer; private final FunctionRenderer renderer;
private final List<? extends SqlAstNode> sqlAstArguments; private final List<? extends SqlAstNode> sqlAstArguments;
private final ReturnableType<?> type; private final @Nullable ReturnableType<?> type;
private final JdbcMappingContainer expressible; private final @Nullable JdbcMappingContainer expressible;
/** /**
* @deprecated Use {@link #SelfRenderingFunctionSqlAstExpression(String, FunctionRenderer, List, ReturnableType, JdbcMappingContainer)} instead * @deprecated Use {@link #SelfRenderingFunctionSqlAstExpression(String, FunctionRenderer, List, ReturnableType, JdbcMappingContainer)} instead
@ -55,8 +57,8 @@ public class SelfRenderingFunctionSqlAstExpression
String functionName, String functionName,
FunctionRenderingSupport renderer, FunctionRenderingSupport renderer,
List<? extends SqlAstNode> sqlAstArguments, List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> type, @Nullable ReturnableType<?> type,
JdbcMappingContainer expressible) { @Nullable JdbcMappingContainer expressible) {
this.functionName = functionName; this.functionName = functionName;
this.renderer = renderer::render; this.renderer = renderer::render;
this.sqlAstArguments = sqlAstArguments; this.sqlAstArguments = sqlAstArguments;
@ -69,8 +71,8 @@ public class SelfRenderingFunctionSqlAstExpression
String functionName, String functionName,
FunctionRenderer renderer, FunctionRenderer renderer,
List<? extends SqlAstNode> sqlAstArguments, List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> type, @Nullable ReturnableType<?> type,
JdbcMappingContainer expressible) { @Nullable JdbcMappingContainer expressible) {
this.functionName = functionName; this.functionName = functionName;
this.renderer = renderer; this.renderer = renderer;
this.sqlAstArguments = sqlAstArguments; this.sqlAstArguments = sqlAstArguments;
@ -108,7 +110,7 @@ public class SelfRenderingFunctionSqlAstExpression
return renderer; return renderer;
} }
protected ReturnableType<?> getType() { protected @Nullable ReturnableType<?> getType() {
return type; return type;
} }

View File

@ -8,12 +8,14 @@ package org.hibernate.query.sqm.internal;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Marco Belladelli * @author Marco Belladelli
*/ */
public class NoParamSqmCopyContext extends SimpleSqmCopyContext { public class NoParamSqmCopyContext extends SimpleSqmCopyContext {
@Override @Override
public <T> T getCopy(T original) { public <T> @Nullable T getCopy(T original) {
if ( original instanceof SqmParameter<?> ) { if ( original instanceof SqmParameter<?> ) {
return original; return original;
} }

View File

@ -10,6 +10,8 @@ import java.util.IdentityHashMap;
import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Marco Belladelli * @author Marco Belladelli
*/ */
@ -18,7 +20,7 @@ public class SimpleSqmCopyContext implements SqmCopyContext {
@Override @Override
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public <T> T getCopy(T original) { public <T> @Nullable T getCopy(T original) {
return (T) map.get( original ); return (T) map.get( original );
} }

View File

@ -336,10 +336,12 @@ public class TypecheckUtil {
* @see TypecheckUtil#assertAssignable(String, SqmPath, SqmTypedNode, SessionFactoryImplementor) * @see TypecheckUtil#assertAssignable(String, SqmPath, SqmTypedNode, SessionFactoryImplementor)
*/ */
public static void assertComparable(Expression<?> x, Expression<?> y, SessionFactoryImplementor factory) { public static void assertComparable(Expression<?> x, Expression<?> y, SessionFactoryImplementor factory) {
SqmExpression<?> left = (SqmExpression<?>) x; final SqmExpression<?> left = (SqmExpression<?>) x;
SqmExpression<?> right = (SqmExpression<?>) y; final SqmExpression<?> right = (SqmExpression<?>) y;
if ( left.getTupleLength() != null && right.getTupleLength() != null final Integer leftTupleLength = left.getTupleLength();
&& left.getTupleLength().intValue() != right.getTupleLength().intValue() ) { final Integer rightTupleLength;
if ( leftTupleLength != null && ( rightTupleLength = right.getTupleLength() ) != null
&& leftTupleLength.intValue() != rightTupleLength.intValue() ) {
throw new SemanticException( "Cannot compare tuples of different lengths" ); 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.BasicValuedMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.ReturnableType; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -16,6 +18,8 @@ import org.hibernate.type.spi.TypeConfiguration;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Pluggable strategy for resolving a function return type for a specific call. * Pluggable strategy for resolving a function return type for a specific call.
* *
@ -32,10 +36,10 @@ public interface FunctionReturnTypeResolver {
* of `some_function`. * of `some_function`.
* *
* @return The resolved type. * @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) @Deprecated(forRemoval = true)
default ReturnableType<?> resolveFunctionReturnType( default @Nullable ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
@ -52,13 +56,44 @@ public interface FunctionReturnTypeResolver {
* of `some_function`. * of `some_function`.
* *
* @return The resolved type. * @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, ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier, Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { 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.ReturnableType;
import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.SqmPathSource; 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.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.NullSqmExpressible; import org.hibernate.query.sqm.tree.expression.NullSqmExpressible;
import org.hibernate.sql.ast.tree.SqlAstNode; 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.BasicType;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -51,15 +54,7 @@ public class StandardFunctionReturnTypeResolvers {
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, @Nullable SqmToSqlAstConverter converter,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
return isAssignableTo( invariantType, impliedType ) ? impliedType : invariantType; return isAssignableTo( invariantType, impliedType ) ? impliedType : invariantType;
@ -84,15 +79,7 @@ public class StandardFunctionReturnTypeResolvers {
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, @Nullable SqmToSqlAstConverter converter,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
ReturnableType<?> argType = extractArgumentType( arguments, argPosition ); ReturnableType<?> argType = extractArgumentType( arguments, argPosition );
@ -133,15 +120,7 @@ public class StandardFunctionReturnTypeResolvers {
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, @Nullable SqmToSqlAstConverter converter,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
for ( int i = 0; i < arguments.size(); i++ ) { 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; return inferredType;
} }
@Override
public boolean isInTypeInference() {
return inImpliedResultTypeInference || inTypeInference;
}
@Override @Override
public MappingModelExpressible<?> resolveFunctionImpliedReturnType() { public MappingModelExpressible<?> resolveFunctionImpliedReturnType() {
if ( inImpliedResultTypeInference || functionImpliedResultTypeAccess == null ) { if ( inImpliedResultTypeInference || functionImpliedResultTypeAccess == null ) {

View File

@ -95,6 +95,11 @@ public class FakeSqmToSqlAstConverter extends BaseSemanticQueryWalker implements
public void registerQueryTransformer(QueryTransformer transformer) { public void registerQueryTransformer(QueryTransformer transformer) {
} }
@Override
public boolean isInTypeInference() {
return false;
}
@Override @Override
public MappingModelExpressible<?> resolveFunctionImpliedReturnType() { public MappingModelExpressible<?> resolveFunctionImpliedReturnType() {
return null; return null;

View File

@ -35,6 +35,12 @@ public interface SqmToSqlAstConverter extends SemanticQueryWalker<Object>, SqlAs
void registerQueryTransformer(QueryTransformer transformer); 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. * 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>. * 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.NoParamSqmCopyContext;
import org.hibernate.query.sqm.internal.SimpleSqmCopyContext; import org.hibernate.query.sqm.internal.SimpleSqmCopyContext;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* *
*/ */
public interface SqmCopyContext { public interface SqmCopyContext {
<T> T getCopy(T original); <T> @Nullable T getCopy(T original);
<T> T registerCopy(T original, T copy); <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.query.sqm.SqmExpressible;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Optional contract for SqmNode implementations which are * Optional contract for SqmNode implementations which are
* typed * typed
@ -19,17 +21,17 @@ public interface SqmTypedNode<T> extends SqmNode, SqmExpressibleAccessor<T>, Sqm
/** /**
* The Java type descriptor for this node. * The Java type descriptor for this node.
*/ */
default JavaType<T> getNodeJavaType() { default @Nullable JavaType<T> getNodeJavaType() {
final SqmExpressible<T> nodeType = getNodeType(); final SqmExpressible<T> nodeType = getNodeType();
return nodeType != null ? nodeType.getExpressibleJavaType() : null; return nodeType != null ? nodeType.getExpressibleJavaType() : null;
} }
@Override @Override
default SqmExpressible<T> getExpressible() { default @Nullable SqmExpressible<T> getExpressible() {
return getNodeType(); return getNodeType();
} }
SqmExpressible<T> getNodeType(); @Nullable SqmExpressible<T> getNodeType();
@Override @Override
SqmTypedNode<T> copy(SqmCopyContext context); SqmTypedNode<T> copy(SqmCopyContext context);

View File

@ -13,6 +13,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.internal.util.NullnessUtil;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
@ -79,12 +80,12 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
@Override @Override
public SqmPathSource<T> getNodeType() { public SqmPathSource<T> getNodeType() {
return (SqmPathSource<T>) super.getNodeType(); return (SqmPathSource<T>) NullnessUtil.castNonNull( super.getNodeType() );
} }
@Override @Override
public SqmPathSource<T> getReferencedPathSource() { public SqmPathSource<T> getReferencedPathSource() {
return (SqmPathSource<T>) super.getNodeType(); return (SqmPathSource<T>) NullnessUtil.castNonNull( super.getNodeType() );
} }
@Override @Override

View File

@ -14,6 +14,7 @@ import java.util.function.Consumer;
import jakarta.persistence.metamodel.MapAttribute; import jakarta.persistence.metamodel.MapAttribute;
import jakarta.persistence.metamodel.PluralAttribute; import jakarta.persistence.metamodel.PluralAttribute;
import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.SingularAttribute;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.SemanticException; import org.hibernate.query.SemanticException;
@ -104,7 +105,7 @@ public interface SqmPath<T> extends SqmExpression<T>, SemanticPathPart, JpaPath<
SqmPathSource<T> getNodeType(); SqmPathSource<T> getNodeType();
@Override @Override
default void applyInferableType(SqmExpressible<?> type) { default void applyInferableType(@Nullable SqmExpressible<?> type) {
// do nothing // do nothing
} }

View File

@ -20,6 +20,7 @@ import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType2; 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 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 ); super( type, criteriaBuilder );
} }
@ -38,10 +39,10 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
} }
@Override @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( SqmTreeCreationLogger.LOGGER.debugf(
"Applying inferable type to SqmExpression [%s] : %s -> %s", "Applying inferable type to SqmExpression [%s] : %s -> %s",
this, this,
@ -138,13 +139,7 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
} }
@Override @Override
public JpaSelection<T> alias(String name) { public @Nullable JavaType<T> getJavaTypeDescriptor() {
setAlias( name );
return this;
}
@Override
public JavaType<T> getJavaTypeDescriptor() {
return getNodeType() == null ? null : getNodeType().getExpressibleJavaType(); 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.NodeBuilder;
import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmExpressible;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Common support for SqmParameter impls * Common support for SqmParameter impls
* *
@ -28,7 +30,7 @@ public abstract class AbstractSqmParameter<T> extends AbstractSqmExpression<T> i
} }
@Override @Override
public void applyInferableType(SqmExpressible<?> type) { public void applyInferableType(@Nullable SqmExpressible<?> type) {
if ( type != null ) { if ( type != null ) {
if ( type instanceof PluralPersistentAttribute ) { if ( type instanceof PluralPersistentAttribute ) {
final PluralPersistentAttribute<?, ?, ?> 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.SqmCopyContext;
import org.hibernate.query.sqm.tree.select.SqmSubQuery; import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Gavin King * @author Gavin King
*/ */
@ -25,7 +27,7 @@ public class SqmAny<T> extends AbstractSqmExpression<T> {
} }
@Override @Override
public SqmExpressible<T> getNodeType() { public @Nullable SqmExpressible<T> getNodeType() {
return subquery.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.SqmCopyContext;
import org.hibernate.query.sqm.tree.select.SqmSubQuery; import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Gavin King * @author Gavin King
*/ */
@ -25,7 +27,7 @@ public class SqmEvery<T> extends AbstractSqmExpression<T> {
} }
@Override @Override
public SqmExpressible<T> getNodeType() { public @Nullable SqmExpressible<T> getNodeType() {
return subquery.getNodeType(); return subquery.getNodeType();
} }

View File

@ -11,6 +11,7 @@ import java.math.BigInteger;
import java.util.Collection; import java.util.Collection;
import java.util.function.Consumer; import java.util.function.Consumer;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.query.ReturnableType; 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} * Can change as a result of calls to {@link #applyInferableType}
*/ */
@Override @Override
SqmExpressible<T> getNodeType(); @Nullable SqmExpressible<T> getNodeType();
/** /**
* Used to apply type information based on the expression's usage * 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) * an implicit cast)
*/ */
@Internal @Internal
void applyInferableType(SqmExpressible<?> type); void applyInferableType(@Nullable SqmExpressible<?> type);
@Override @Override
default void visitSubSelectableNodes(Consumer<SqmSelectableNode<?>> jpaSelectionConsumer) { 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 org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -111,7 +112,7 @@ public class SqmFieldLiteral<T> implements SqmExpression<T>, SqmExpressible<T>,
} }
@Override @Override
public void applyInferableType(SqmExpressible<?> type) { public void applyInferableType(@Nullable SqmExpressible<?> type) {
} }
@Override @Override

View File

@ -11,6 +11,7 @@ import java.math.BigInteger;
import java.util.Locale; import java.util.Locale;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.internal.util.NullnessUtil;
import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SemanticQueryWalker;
@ -67,7 +68,7 @@ public class SqmHqlNumericLiteral<N extends Number> extends SqmLiteral<N> {
@Override @Override
public BasicDomainType<N> getNodeType() { public BasicDomainType<N> getNodeType() {
return (BasicDomainType<N>) super.getNodeType(); return (BasicDomainType<N>) NullnessUtil.castNonNull( super.getNodeType() );
} }
@Override @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.SqmCopyContext;
import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Christian Beikov * @author Christian Beikov
* @author Marco Belladelli * @author Marco Belladelli
@ -86,7 +88,7 @@ public class SqmOver<T> extends AbstractSqmExpression<T> {
} }
@Override @Override
public SqmExpressible<T> getNodeType() { public @Nullable SqmExpressible<T> getNodeType() {
return expression.getNodeType(); return expression.getNodeType();
} }

View File

@ -9,7 +9,6 @@ package org.hibernate.query.sqm.tree.expression;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.query.BindableType; import org.hibernate.query.BindableType;
import org.hibernate.query.criteria.JpaParameterExpression; import org.hibernate.query.criteria.JpaParameterExpression;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmCopyContext;
/** /**
@ -65,9 +64,6 @@ public interface SqmParameter<T> extends SqmExpression<T>, JpaParameterExpressio
*/ */
BindableType<T> getAnticipatedType(); BindableType<T> getAnticipatedType();
@Override
SqmExpressible<T> getNodeType();
/** /**
* Make a copy * 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.SqmExpressible;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode; import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Base support for {@link JpaSelection} impls. * Base support for {@link JpaSelection} impls.
* *
@ -21,7 +23,7 @@ import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
public abstract class AbstractJpaSelection<T> public abstract class AbstractJpaSelection<T>
extends AbstractJpaTupleElement<T> extends AbstractJpaTupleElement<T>
implements SqmSelectableNode<T>, JpaSelection<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 ); 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.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmVisitableNode; import org.hibernate.query.sqm.tree.SqmVisitableNode;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Base support for {@link JpaTupleElement} impls * Base support for {@link JpaTupleElement} impls
* *
@ -22,10 +24,10 @@ public abstract class AbstractJpaTupleElement<T>
extends AbstractSqmNode extends AbstractSqmNode
implements SqmVisitableNode, JpaTupleElement<T> { implements SqmVisitableNode, JpaTupleElement<T> {
private SqmExpressible<T> expressibleType; private @Nullable SqmExpressible<T> expressibleType;
private String alias; private @Nullable String alias;
protected AbstractJpaTupleElement(SqmExpressible<? super T> expressibleType, NodeBuilder criteriaBuilder) { protected AbstractJpaTupleElement(@Nullable SqmExpressible<? super T> expressibleType, NodeBuilder criteriaBuilder) {
super( criteriaBuilder ); super( criteriaBuilder );
setExpressibleType( expressibleType ); setExpressibleType( expressibleType );
} }
@ -35,22 +37,22 @@ public abstract class AbstractJpaTupleElement<T>
} }
@Override @Override
public String getAlias() { public @Nullable String getAlias() {
return alias; return alias;
} }
/** /**
* Protected access to set the alias. * Protected access to set the alias.
*/ */
protected void setAlias(String alias) { protected void setAlias(@Nullable String alias) {
this.alias = alias; this.alias = alias;
} }
public SqmExpressible<T> getNodeType() { public @Nullable SqmExpressible<T> getNodeType() {
return expressibleType; return expressibleType;
} }
protected final void setExpressibleType(SqmExpressible<?> expressibleType) { protected final void setExpressibleType(@Nullable SqmExpressible<?> expressibleType) {
//noinspection unchecked //noinspection unchecked
this.expressibleType = (SqmExpressible<T>) expressibleType; 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.Root;
import jakarta.persistence.criteria.Selection; import jakarta.persistence.criteria.Selection;
import jakarta.persistence.criteria.SetJoin; import jakarta.persistence.criteria.SetJoin;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -600,12 +601,12 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
} }
@Override @Override
public SqmExpressible<T> getNodeType() { public @Nullable SqmExpressible<T> getNodeType() {
return expressibleType; return expressibleType;
} }
@Override @Override
public void applyInferableType(SqmExpressible<?> type) { public void applyInferableType(@Nullable SqmExpressible<?> type) {
//noinspection unchecked //noinspection unchecked
expressibleType = (SqmExpressible<T>) type; 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.collection.spi.PersistentCollection;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.AbstractClassJavaType; 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.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
@ -58,9 +59,14 @@ public class CollectionJavaType<C> extends AbstractClassJavaType<C> {
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
final JavaType<Object> valueDescriptor = javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] );
switch ( semantics.getCollectionClassification() ) { switch ( semantics.getCollectionClassification() ) {
case ARRAY: case ARRAY:
//noinspection unchecked
return (JavaType<C>) new ArrayJavaType<>(
javaTypeRegistry.resolveDescriptor(
( (Class<?>) parameterizedType.getRawType() ).getComponentType()
)
);
case BAG: case BAG:
case ID_BAG: case ID_BAG:
case LIST: case LIST:
@ -70,7 +76,7 @@ public class CollectionJavaType<C> extends AbstractClassJavaType<C> {
//noinspection unchecked,rawtypes //noinspection unchecked,rawtypes
return new BasicCollectionJavaType( return new BasicCollectionJavaType(
parameterizedType, parameterizedType,
valueDescriptor, javaTypeRegistry.resolveDescriptor( actualTypeArguments[actualTypeArguments.length - 1] ),
semantics semantics
); );
@ -82,7 +88,7 @@ public class CollectionJavaType<C> extends AbstractClassJavaType<C> {
new MapMutabilityPlan<>( new MapMutabilityPlan<>(
(MapSemantics<Map<Object, Object>, Object, Object>) semantics, (MapSemantics<Map<Object, Object>, Object, Object>) semantics,
javaTypeRegistry.resolveDescriptor( actualTypeArguments[0] ), 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.ARRAY, Array.class );
workMap.put( SqlTypes.STRUCT, Struct.class ); workMap.put( SqlTypes.STRUCT, Struct.class );
workMap.put( SqlTypes.REF, Ref.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.ROWID, RowId.class );
workMap.put( SqlTypes.SQLXML, SQLXML.class ); workMap.put( SqlTypes.SQLXML, SQLXML.class );
workMap.put( SqlTypes.UUID, UUID.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.persister.entity.EntityPersister;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; 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.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
@ -48,6 +49,8 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
/** /**
@ -137,15 +140,7 @@ public class DynamicParameterizedTypeTest {
@Override @Override
public ReturnableType<?> resolveFunctionReturnType( public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType, ReturnableType<?> impliedType,
List<? extends SqmTypedNode<?>> arguments, @Nullable SqmToSqlAstConverter converter,
TypeConfiguration typeConfiguration) {
return resolveFunctionReturnType( impliedType, null, arguments, typeConfiguration );
}
@Override
public ReturnableType<?> resolveFunctionReturnType(
ReturnableType<?> impliedType,
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
List<? extends SqmTypedNode<?>> arguments, List<? extends SqmTypedNode<?>> arguments,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
SqmTypedNode<?> sqmTypedNode = arguments.get(0); SqmTypedNode<?> sqmTypedNode = arguments.get(0);

View File

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

View File

@ -2558,9 +2558,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
// TODO: anything more we can do here? e.g. check constructor // TODO: anything more we can do here? e.g. check constructor
try { try {
final Class<?> javaResultType = selection.getJavaType(); final Class<?> javaResultType = selection.getJavaType();
final TypeElement typeElement = context.getTypeElementForFullyQualifiedName( javaResultType.getName() ); if ( javaResultType == null ) {
final Types types = context.getTypeUtils(); returnTypeCorrect = true;
returnTypeCorrect = types.isAssignable( returnType, types.erasure( typeElement.asType() ) ); }
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) { catch (Exception e) {
//ignore //ignore
@ -2670,7 +2675,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
private static boolean parameterMatches(VariableElement parameter, JpaSelection<?> item) { 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) { private static boolean parameterMatches(TypeMirror parameterType, Class<?> itemType) {

View File

@ -104,8 +104,9 @@ class NamedQueryMethod implements MetaAttribute {
else { else {
final List<SqmSelectableNode<?>> items = final List<SqmSelectableNode<?>> items =
select.getQuerySpec().getSelectClause().getSelectionItems(); select.getQuerySpec().getSelectClause().getSelectionItems();
if ( items.size() == 1 ) { final SqmExpressible<?> expressible;
final String typeName = items.get(0).getExpressible().getTypeName(); if ( items.size() == 1 && ( expressible = items.get( 0 ).getExpressible() ) != null ) {
final String typeName = expressible.getTypeName();
final TypeElement entityType = entityType( typeName ); final TypeElement entityType = entityType( typeName );
return entityType == null ? typeName : entityType.getQualifiedName().toString(); return entityType == null ? typeName : entityType.getQualifiedName().toString();