HHH-16786 Fix NPE in SqmParameterInterpretation when binding null for select item in insert-select statement
This commit is contained in:
parent
2ad5cdd9b9
commit
1a9732a5c2
|
@ -5499,11 +5499,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return new SqmParameterInterpretation(
|
return new SqmParameterInterpretation(
|
||||||
sqmParameter,
|
|
||||||
queryParameter,
|
|
||||||
jdbcParametersForSqm,
|
jdbcParametersForSqm,
|
||||||
valueMapping,
|
valueMapping
|
||||||
qp -> binding
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
package org.hibernate.query.sqm.sql.internal;
|
package org.hibernate.query.sqm.sql.internal;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
|
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
|
@ -17,12 +15,7 @@ import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||||
import org.hibernate.query.BindableType;
|
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
import org.hibernate.query.spi.QueryParameterBinding;
|
|
||||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
|
||||||
import org.hibernate.query.sqm.SqmExpressible;
|
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
|
||||||
import org.hibernate.sql.ast.SqlAstWalker;
|
import org.hibernate.sql.ast.SqlAstWalker;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
@ -38,22 +31,13 @@ import org.hibernate.type.descriptor.java.JavaType;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SqmParameterInterpretation implements Expression, DomainResultProducer, SqlTupleContainer {
|
public class SqmParameterInterpretation implements Expression, DomainResultProducer, SqlTupleContainer {
|
||||||
private final SqmParameter<?> sqmParameter;
|
|
||||||
private final QueryParameterImplementor<?> queryParameter;
|
|
||||||
private final MappingModelExpressible<?> valueMapping;
|
private final MappingModelExpressible<?> valueMapping;
|
||||||
private final Function<QueryParameterImplementor<?>, QueryParameterBinding<?>> queryParameterBindingResolver;
|
|
||||||
private final List<JdbcParameter> jdbcParameters;
|
private final List<JdbcParameter> jdbcParameters;
|
||||||
private Expression resolvedExpression;
|
private Expression resolvedExpression;
|
||||||
|
|
||||||
public SqmParameterInterpretation(
|
public SqmParameterInterpretation(
|
||||||
SqmParameter<?> sqmParameter,
|
|
||||||
QueryParameterImplementor<?> queryParameter,
|
|
||||||
List<JdbcParameter> jdbcParameters,
|
List<JdbcParameter> jdbcParameters,
|
||||||
MappingModelExpressible<?> valueMapping,
|
MappingModelExpressible<?> valueMapping) {
|
||||||
Function<QueryParameterImplementor<?>, QueryParameterBinding<?>> queryParameterBindingResolver) {
|
|
||||||
this.sqmParameter = sqmParameter;
|
|
||||||
this.queryParameter = queryParameter;
|
|
||||||
this.queryParameterBindingResolver = queryParameterBindingResolver;
|
|
||||||
|
|
||||||
if ( valueMapping instanceof EntityAssociationMapping ) {
|
if ( valueMapping instanceof EntityAssociationMapping ) {
|
||||||
final EntityAssociationMapping mapping = (EntityAssociationMapping) valueMapping;
|
final EntityAssociationMapping mapping = (EntityAssociationMapping) valueMapping;
|
||||||
|
@ -109,39 +93,21 @@ public class SqmParameterInterpretation implements Expression, DomainResultProdu
|
||||||
throw new SemanticException( "Composite query parameter cannot be used in select" );
|
throw new SemanticException( "Composite query parameter cannot be used in select" );
|
||||||
}
|
}
|
||||||
|
|
||||||
BindableType<?> nodeType = sqmParameter.getNodeType();
|
final JdbcMapping jdbcMapping = resolvedExpression.getExpressionType().getSingleJdbcMapping();
|
||||||
if ( nodeType == null ) {
|
final JavaType<?> jdbcJavaType = jdbcMapping.getJdbcJavaType();
|
||||||
final QueryParameterBinding<?> binding = queryParameterBindingResolver.apply( queryParameter );
|
final BasicValueConverter<?, ?> converter = jdbcMapping.getValueConverter();
|
||||||
nodeType = binding.getBindType();
|
|
||||||
}
|
|
||||||
final SessionFactoryImplementor sessionFactory = creationState.getSqlAstCreationState()
|
|
||||||
.getCreationContext()
|
|
||||||
.getSessionFactory();
|
|
||||||
|
|
||||||
final SqmExpressible<?> sqmExpressible = nodeType.resolveExpressible( sessionFactory );
|
|
||||||
final JavaType<?> jdbcJavaType;
|
|
||||||
final BasicValueConverter<?, ?> converter;
|
|
||||||
if ( sqmExpressible instanceof JdbcMapping ) {
|
|
||||||
final JdbcMapping jdbcMapping = (JdbcMapping) sqmExpressible;
|
|
||||||
jdbcJavaType = jdbcMapping.getJdbcJavaType();
|
|
||||||
converter = jdbcMapping.getValueConverter();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
jdbcJavaType = sqmExpressible.getExpressibleJavaType();
|
|
||||||
converter = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SqlSelection sqlSelection = creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
|
final SqlSelection sqlSelection = creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
|
||||||
resolvedExpression,
|
resolvedExpression,
|
||||||
jdbcJavaType,
|
jdbcJavaType,
|
||||||
null,
|
null,
|
||||||
sessionFactory.getTypeConfiguration()
|
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory().getTypeConfiguration()
|
||||||
);
|
);
|
||||||
|
|
||||||
return new BasicResult(
|
return new BasicResult(
|
||||||
sqlSelection.getValuesArrayPosition(),
|
sqlSelection.getValuesArrayPosition(),
|
||||||
resultVariable,
|
resultVariable,
|
||||||
sqmExpressible.getExpressibleJavaType(),
|
jdbcMapping.getMappedJavaType(),
|
||||||
converter
|
converter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -165,31 +131,11 @@ public class SqmParameterInterpretation implements Expression, DomainResultProdu
|
||||||
throw new SemanticException( "Composite query parameter cannot be used in select" );
|
throw new SemanticException( "Composite query parameter cannot be used in select" );
|
||||||
}
|
}
|
||||||
|
|
||||||
BindableType<?> nodeType = sqmParameter.getNodeType();
|
|
||||||
if ( nodeType == null ) {
|
|
||||||
final QueryParameterBinding<?> binding = queryParameterBindingResolver.apply( queryParameter );
|
|
||||||
nodeType = binding.getBindType();
|
|
||||||
}
|
|
||||||
|
|
||||||
final SessionFactoryImplementor sessionFactory = creationState.getSqlAstCreationState()
|
|
||||||
.getCreationContext()
|
|
||||||
.getSessionFactory();
|
|
||||||
|
|
||||||
final SqmExpressible<?> sqmExpressible = nodeType.resolveExpressible( sessionFactory );
|
|
||||||
final JavaType<?> jdbcJavaType;
|
|
||||||
if ( sqmExpressible instanceof JdbcMapping ) {
|
|
||||||
final JdbcMapping jdbcMapping = (JdbcMapping) sqmExpressible;
|
|
||||||
jdbcJavaType = jdbcMapping.getJdbcJavaType();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
jdbcJavaType = sqmExpressible.getExpressibleJavaType();
|
|
||||||
}
|
|
||||||
|
|
||||||
return creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
|
return creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
|
||||||
resolvedExpression,
|
resolvedExpression,
|
||||||
jdbcJavaType,
|
resolvedExpression.getExpressionType().getSingleJdbcMapping().getMappedJavaType(),
|
||||||
null,
|
null,
|
||||||
sessionFactory.getTypeConfiguration()
|
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory().getTypeConfiguration()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
@ -37,8 +38,8 @@ public class InsertSelectTests {
|
||||||
public void prepareTestData(SessionFactoryScope scope) {
|
public void prepareTestData(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
session.persist( new EntitySource( "A" ) );
|
session.persist( new EntitySource( 1, "A" ) );
|
||||||
session.persist( new EntitySource( "A" ) );
|
session.persist( new EntitySource( 2, "A" ) );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -100,25 +101,47 @@ public class InsertSelectTests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-16786")
|
||||||
|
public void testInsertSelectParameterInference(SessionFactoryScope scope) {
|
||||||
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
statementInspector.clear();
|
||||||
|
session.createMutationQuery(
|
||||||
|
"insert into EntityEntry (id, name, source) " +
|
||||||
|
"select 1, 'abc', :source from EntityEntry e"
|
||||||
|
).setParameter( "source", null ).executeUpdate();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name = "EntityEntry")
|
@Entity(name = "EntityEntry")
|
||||||
public static class EntityEntry {
|
public static class EntityEntry {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
Integer id;
|
Integer id;
|
||||||
String name;
|
String name;
|
||||||
|
@ManyToOne
|
||||||
|
EntitySource source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity(name = "EntitySource")
|
@Entity(name = "EntitySource")
|
||||||
public static class EntitySource {
|
public static class EntitySource {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
|
||||||
Integer id;
|
Integer id;
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
public EntitySource() {
|
public EntitySource() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySource(String name) {
|
public EntitySource(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitySource(Integer id, String name) {
|
||||||
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue