Fix multiparameter binding issue

This commit is contained in:
Andrea Boriero 2021-07-26 12:32:46 +02:00
parent e4571cbf30
commit 967553add4
7 changed files with 175 additions and 60 deletions

View File

@ -7,6 +7,7 @@
package org.hibernate.dialect;
import org.hibernate.LockOptions;
import org.hibernate.dialect.function.FieldFunction;
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
@ -380,6 +381,8 @@ public void initializeFunctionRegistry(QueryEngine queryEngine) {
// we want the standard default precision of 6 (microseconds)
CommonFunctionFactory.sysdateExplicitMicros( queryEngine );
}
queryEngine.getSqmFunctionRegistry().register( "field", new FieldFunction() );
}
@Override

View File

@ -0,0 +1,55 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.List;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.type.StandardBasicTypes;
public class FieldFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
public FieldFunction() {
super(
"field",
StandardArgumentsValidators.min( 2 ),
StandardFunctionReturnTypeResolvers.invariant( StandardBasicTypes.INTEGER )
);
}
@Override
public void render(
SqlAppender sqlAppender, List<SqlAstNode> sqlAstArguments, SqlAstTranslator<?> walker) {
sqlAppender.appendSql( "field(" );
sqlAstArguments.get( 0 ).accept( walker );
for ( int i = 1; i < sqlAstArguments.size(); i++ ) {
sqlAppender.appendSql( ", " );
final SqlAstNode argument = sqlAstArguments.get( i );
if ( argument instanceof SqlTupleContainer ) {
final List<? extends Expression> expressions = ( (SqlTupleContainer) argument ).getSqlTuple()
.getExpressions();
for ( int j = 0; j < expressions.size(); j++ ) {
if ( j != 0 ) {
sqlAppender.appendSql( ", " );
}
expressions.get( j ).accept( walker );
}
}
else {
argument.accept( walker );
}
}
sqlAppender.appendSql( ")" );
}
}

View File

@ -62,22 +62,8 @@ protected static List<SqlAstNode> resolveSqlAstArguments(List<SqmTypedNode<?>> s
final ArrayList<SqlAstNode> sqlAstArguments = new ArrayList<>( sqmArguments.size() );
for ( SqmTypedNode<?> sqmArgument : sqmArguments ) {
if ( sqmArgument instanceof SqmParameter ) {
final SqmParameter sqmParameter = (SqmParameter) sqmArgument;
if ( sqmParameter.allowMultiValuedBinding() ) {
final List<Expression> expressions = walker.expandSelfRenderingFunctionMultiValueParameter( sqmParameter );
for ( int i = 0; i < expressions.size(); i++ ) {
sqlAstArguments.add( expressions.get( i ) );
}
}
else {
sqlAstArguments.add( (SqlAstNode) ( (SqmVisitableNode) sqmArgument ).accept( walker ) );
}
}
else {
sqlAstArguments.add( (SqlAstNode) ( (SqmVisitableNode) sqmArgument ).accept( walker ) );
}
}
return sqlAstArguments;
}

View File

@ -216,10 +216,10 @@ public Predicate visitWhereClause(SqmWhereClause whereClause) {
}
@Override
protected Expression consumeSqmParameter(SqmParameter sqmParameter) {
protected Expression consumeSingleSqmParameter(SqmParameter sqmParameter) {
assert parameterResolutionConsumer != null;
final Expression expression = super.consumeSqmParameter( sqmParameter );
final Expression expression = super.consumeSingleSqmParameter( sqmParameter );
final List<List<JdbcParameter>> jdbcParameters = getJdbcParamsBySqmParam().get( sqmParameter );
final MappingModelExpressable<?> mappingType = getSqmParameterMappingModelExpressableResolutions().get( sqmParameter );

View File

@ -2463,7 +2463,6 @@ public Expression visitNamedParameterExpression(SqmNamedParameter expression) {
return consumeSqmParameter( expression );
}
protected Expression consumeSqmParameter(
SqmParameter sqmParameter,
BiConsumer<Integer,JdbcParameter> jdbcParameterConsumer) {
@ -2498,6 +2497,39 @@ protected Expression consumeSqmParameter(
}
protected Expression consumeSqmParameter(SqmParameter sqmParameter) {
if ( sqmParameter.allowMultiValuedBinding() ) {
final QueryParameterImplementor<?> domainParam = domainParameterXref.getQueryParameter( sqmParameter );
final QueryParameterBinding domainParamBinding = domainParameterBindings.getBinding( domainParam );
if ( !domainParamBinding.isMultiValued() ) {
return consumeSingleSqmParameter( sqmParameter );
}
final Collection bindValues = domainParamBinding.getBindValues();
final List<Expression> expressions = new ArrayList<>( bindValues.size());
boolean first = true;
for ( Object bindValue : bindValues ) {
final SqmParameter sqmParamToConsume;
// for each bind value create an "expansion"
if ( first ) {
sqmParamToConsume = sqmParameter;
first = false;
}
else {
sqmParamToConsume = sqmParameter.copy();
domainParameterXref.addExpansion( domainParam, sqmParameter, sqmParamToConsume );
}
expressions.add( consumeSingleSqmParameter( sqmParamToConsume ) );
}
return new SqlTuple( expressions, null );
}
else {
return consumeSingleSqmParameter( sqmParameter );
}
}
protected Expression consumeSingleSqmParameter(SqmParameter sqmParameter) {
final MappingModelExpressable valueMapping = determineValueMapping( sqmParameter );
final List<JdbcParameter> jdbcParametersForSqm = new ArrayList<>();
@ -4488,7 +4520,7 @@ private InListPredicate processInSingleHqlParameter(SqmInListPredicate<?> sqmPre
domainParameterXref.addExpansion( domainParam, sqmParameter, sqmParamToConsume );
}
inListPredicate.addExpression( consumeSqmParameter( sqmParamToConsume ) );
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParamToConsume ) );
}
}
finally {
@ -4533,7 +4565,7 @@ private InListPredicate processInSingleCriteriaParameter(
domainParameterXref.addExpansion( jpaCriteriaParameter, sqmWrapper, sqmParamToConsume );
}
inListPredicate.addExpression( consumeSqmParameter( sqmParamToConsume ) );
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParamToConsume ) );
}
}
finally {
@ -4968,7 +5000,7 @@ public List<Expression> expandSelfRenderingFunctionMultiValueParameter(SqmParame
sqmParamToConsume = sqmParameter.copy();
domainParameterXref.addExpansion( domainParam, sqmParameter, sqmParamToConsume );
}
final Expression expression = consumeSqmParameter( sqmParamToConsume );
final Expression expression = consumeSingleSqmParameter( sqmParamToConsume );
result.add( expression );
}
return result;

View File

@ -19,6 +19,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Function;
import org.hibernate.LockMode;
import org.hibernate.QueryException;
@ -94,6 +95,7 @@
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.ExtractUnit;
import org.hibernate.sql.ast.tree.expression.Format;
import org.hibernate.sql.ast.tree.expression.FunctionExpression;
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.Literal;
@ -830,6 +832,23 @@ protected void visitUpdateStatementOnly(UpdateStatement statement) {
final List<ColumnReference> columnReferences = assignment.getAssignable().getColumnReferences();
if ( columnReferences.size() == 1 ) {
columnReferences.get( 0 ).accept( this );
appendSql( " = " );
final Expression assignedValue = assignment.getAssignedValue();
if ( assignedValue instanceof SqlTupleContainer ) {
final SqlTuple sqlTuple = ( (SqlTupleContainer) assignedValue ).getSqlTuple();
if ( sqlTuple != null ) {
final Expression expression = sqlTuple
.getExpressions()
.get( 0 );
expression.accept( this );
}
else {
assignedValue.accept( this );
}
}
else {
assignment.getAssignedValue().accept( this );
}
}
else {
appendSql( " (" );
@ -837,11 +856,11 @@ protected void visitUpdateStatementOnly(UpdateStatement statement) {
columnReference.accept( this );
}
appendSql( ") " );
}
appendSql( " = " );
assignment.getAssignedValue().accept( this );
}
}
}
finally {
clauseStack.pop();
}
@ -1512,41 +1531,36 @@ protected final void visitPartitionByClause(List<Expression> partitionExpression
}
}
protected final void visitPartitionExpressions(List<Expression> partitionExpressions, boolean supportsSelectAliases) {
String separator = "";
protected final void visitPartitionExpressions(
List<Expression> partitionExpressions,
boolean supportsSelectAliases) {
if ( supportsSelectAliases ) {
for ( Expression partitionExpression : partitionExpressions ) {
if ( partitionExpression instanceof SqlTuple ) {
for ( Expression expression : ( (SqlTuple) partitionExpression ).getExpressions() ) {
appendSql( separator );
renderPartitionItem( expression );
separator = COMA_SEPARATOR;
}
visitPartitionExpressions( partitionExpressions, expression -> expression );
}
else {
appendSql( separator );
renderPartitionItem( partitionExpression );
}
separator = COMA_SEPARATOR;
visitPartitionExpressions( partitionExpressions, expression -> resolveAliasedExpression( expression ) );
}
}
else {
protected final void visitPartitionExpressions(
List<Expression> partitionExpressions,
Function<Expression, Expression> resolveAliasExpression) {
String separator = "";
for ( Expression partitionExpression : partitionExpressions ) {
if ( partitionExpression instanceof SqlTupleContainer ) {
for ( Expression expression : ( (SqlTupleContainer) partitionExpression ).getSqlTuple().getExpressions() ) {
for ( Expression e : ( (SqlTupleContainer) partitionExpression ).getSqlTuple().getExpressions() ) {
appendSql( separator );
renderPartitionItem( resolveAliasedExpression( expression ) );
renderPartitionItem( resolveAliasExpression.apply( e ) );
separator = COMA_SEPARATOR;
}
}
else {
appendSql( separator );
renderPartitionItem( resolveAliasedExpression( partitionExpression ) );
renderPartitionItem( resolveAliasExpression.apply( partitionExpression ) );
}
separator = COMA_SEPARATOR;
}
}
}
protected void renderPartitionItem(Expression expression) {
if ( expression instanceof Literal ) {
@ -2905,7 +2919,22 @@ protected List<SortSpecification> getSortSpecificationsRowNumbering(
@Override
public void visitSqlSelection(SqlSelection sqlSelection) {
renderSelectExpression( sqlSelection.getExpression() );
final Expression expression = sqlSelection.getExpression();
if ( expression instanceof SqlTupleContainer ) {
boolean isFirst = true;
for ( Expression e : ( (SqlTupleContainer) expression ).getSqlTuple().getExpressions() ) {
if ( isFirst ) {
isFirst = false;
}
else {
appendSql( ", " );
}
renderSelectExpression( e );
}
}
else {
renderSelectExpression( expression );
}
}
protected void renderSelectExpression(Expression expression) {
@ -3376,17 +3405,12 @@ else if ( jdbcMapping.getJdbcTypeDescriptor().isString() ) {
@Override
public void visitTuple(SqlTuple tuple) {
boolean isCurrentWhereClause = clauseStack.getCurrent() == Clause.WHERE;
if ( isCurrentWhereClause ) {
appendSql( OPEN_PARENTHESIS );
}
renderCommaSeparated( tuple.getExpressions() );
if ( isCurrentWhereClause ) {
appendSql( CLOSE_PARENTHESIS );
}
}
protected final void renderCommaSeparated(Iterable<? extends SqlAstNode> expressions) {
String separator = NO_SEPARATOR;

View File

@ -38,6 +38,21 @@ public void tearDown(EntityManagerFactoryScope scope) {
);
}
@Test
@TestForIssue(jiraKey = "HHH-10502")
@RequiresDialect(value = MySQLDialect.class, version = 500)
public void testIt(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
List<Person> persons = entityManager.createQuery(
"SELECT p " +
"FROM Person p " +
"ORDER BY FIELD(id, :ids) ", Person.class )
.setParameter( "ids", Arrays.asList( 3L, 1L, 2L ) )
.getResultList();
} );
}
@Test
@TestForIssue(jiraKey = "HHH-10502")
@RequiresDialect(value = MySQLDialect.class, version = 500)