HHH-17677 handle literal null arguments more elegantly in StandardFunctionReturnTypeResolvers

resolves a very confusing error message
This commit is contained in:
Gavin King 2024-01-25 14:19:58 +01:00 committed by Christian Beikov
parent e90dba2c98
commit 78990a7910
3 changed files with 34 additions and 10 deletions

View File

@ -12,16 +12,15 @@ import java.util.Locale;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.QueryException;
import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.model.domain.EntityDomainType;
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.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.NullSqmExpressible;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
@ -252,7 +251,10 @@ public class StandardFunctionReturnTypeResolvers {
int position) { int position) {
final SqmTypedNode<?> specifiedArgument = arguments.get( position - 1 ); final SqmTypedNode<?> specifiedArgument = arguments.get( position - 1 );
final SqmExpressible<?> specifiedArgType = getArgumentExpressible( specifiedArgument ); final SqmExpressible<?> specifiedArgType = getArgumentExpressible( specifiedArgument );
if ( specifiedArgType != null && !(specifiedArgType instanceof ReturnableType ) ) { if ( specifiedArgType == null || specifiedArgType instanceof NullSqmExpressible ) {
return null;
}
else if ( !(specifiedArgType instanceof ReturnableType) ) {
throw new FunctionArgumentException( throw new FunctionArgumentException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
@ -263,18 +265,19 @@ public class StandardFunctionReturnTypeResolvers {
) )
); );
} }
else {
return (ReturnableType<?>) specifiedArgType; return (ReturnableType<?>) specifiedArgType;
} }
}
private static SqmExpressible<?> getArgumentExpressible(SqmTypedNode<?> specifiedArgument) { private static SqmExpressible<?> getArgumentExpressible(SqmTypedNode<?> specifiedArgument) {
final SqmExpressible<?> expressible = specifiedArgument.getNodeType(); final SqmExpressible<?> expressible = specifiedArgument.getNodeType();
final SqmExpressible<?> specifiedArgType = expressible instanceof SqmTypedNode<?> final SqmExpressible<?> specifiedArgType = expressible instanceof SqmTypedNode<?>
? ( (SqmTypedNode<?>) expressible ).getNodeType() ? ( (SqmTypedNode<?>) expressible ).getNodeType()
: expressible; : expressible;
return specifiedArgType instanceof SqmPathSource ? return specifiedArgType instanceof SqmPathSource
( (SqmPathSource<?>) specifiedArgType ).getSqmPathType() : ? ( (SqmPathSource<?>) specifiedArgType ).getSqmPathType()
specifiedArgType; : specifiedArgType;
} }
public static JdbcMapping extractArgumentJdbcMapping( public static JdbcMapping extractArgumentJdbcMapping(

View File

@ -11,7 +11,6 @@ import java.util.Objects;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.SqlExpressible;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlExpressionAccess; import org.hibernate.sql.ast.spi.SqlExpressionAccess;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
@ -93,7 +92,10 @@ public class SqlSelectionImpl implements SqlSelection, SqlExpressionAccess {
} }
private static ValueExtractor determineValueExtractor(Expression sqlExpression, JavaType<?> jdbcJavaType) { private static ValueExtractor determineValueExtractor(Expression sqlExpression, JavaType<?> jdbcJavaType) {
final JdbcMapping jdbcMapping = sqlExpression.getExpressionType().getSingleJdbcMapping(); final JdbcMappingContainer expressionType = sqlExpression.getExpressionType();
final JdbcMapping jdbcMapping = expressionType == null
? JavaObjectType.INSTANCE
: expressionType.getSingleJdbcMapping();
if ( jdbcJavaType == null || jdbcMapping.getMappedJavaType() == jdbcJavaType ) { if ( jdbcJavaType == null || jdbcMapping.getMappedJavaType() == jdbcJavaType ) {
return jdbcMapping.getJdbcValueExtractor(); return jdbcMapping.getJdbcValueExtractor();
} }
@ -102,6 +104,7 @@ public class SqlSelectionImpl implements SqlSelection, SqlExpressionAccess {
} }
} }
@Override @Override
public Expression getExpression() { public Expression getExpression() {
return sqlExpression; return sqlExpression;

View File

@ -2248,4 +2248,22 @@ public class FunctionTests {
} }
); );
} }
@Test
@SkipForDialect(dialectClass = H2Dialect.class)
@SkipForDialect(dialectClass = DerbyDialect.class)
@SkipForDialect(dialectClass = HSQLDialect.class)
@SkipForDialect(dialectClass = DB2Dialect.class)
public void testNullInCoalesce(SessionFactoryScope scope) {
scope.inTransaction(s -> {
assertEquals("hello",
s.createQuery("select coalesce(null, :word)", String.class)
.setParameter("word", "hello")
.getSingleResultOrNull());
assertEquals("hello",
s.createQuery("select coalesce(:word, null)", String.class)
.setParameter("word", "hello")
.getSingleResultOrNull());
});
}
} }