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 class MySQLDialect extends Dialect {
// 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,21 +62,7 @@ public class SelfRenderingSqmFunction<T> extends SqmFunction<T> {
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 ) );
}
sqlAstArguments.add( (SqlAstNode) ( (SqmVisitableNode) sqmArgument ).accept( walker ) );
}
return sqlAstArguments;
}

View File

@ -216,10 +216,10 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
}
@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

@ -447,7 +447,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
fromClauseIndexStack.push( fromClauseIndex );
processingStateStack.push( processingState );
}
protected void popProcessingStateStack() {
lastPoppedFromClauseIndex = fromClauseIndexStack.pop();
lastPoppedProcessingState = processingStateStack.pop();
@ -2463,7 +2463,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return consumeSqmParameter( expression );
}
protected Expression consumeSqmParameter(
SqmParameter sqmParameter,
BiConsumer<Integer,JdbcParameter> jdbcParameterConsumer) {
@ -2498,6 +2497,39 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
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 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
domainParameterXref.addExpansion( domainParam, sqmParameter, sqmParamToConsume );
}
inListPredicate.addExpression( consumeSqmParameter( sqmParamToConsume ) );
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParamToConsume ) );
}
}
finally {
@ -4533,7 +4565,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
domainParameterXref.addExpansion( jpaCriteriaParameter, sqmWrapper, sqmParamToConsume );
}
inListPredicate.addExpression( consumeSqmParameter( sqmParamToConsume ) );
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParamToConsume ) );
}
}
finally {
@ -4968,7 +5000,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
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.Locale;
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.Every;
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 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
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,9 +856,9 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
columnReference.accept( this );
}
appendSql( ") " );
appendSql( " = " );
assignment.getAssignedValue().accept( this );
}
appendSql( " = " );
assignment.getAssignedValue().accept( this );
}
}
finally {
@ -1512,39 +1531,34 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
}
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;
}
}
else {
appendSql( separator );
renderPartitionItem( partitionExpression );
}
separator = COMA_SEPARATOR;
}
visitPartitionExpressions( partitionExpressions, expression -> expression );
}
else {
for ( Expression partitionExpression : partitionExpressions ) {
if ( partitionExpression instanceof SqlTupleContainer ) {
for ( Expression expression : ( (SqlTupleContainer) partitionExpression ).getSqlTuple().getExpressions() ) {
appendSql( separator );
renderPartitionItem( resolveAliasedExpression( expression ) );
separator = COMA_SEPARATOR;
}
}
else {
visitPartitionExpressions( partitionExpressions, expression -> resolveAliasedExpression( expression ) );
}
}
protected final void visitPartitionExpressions(
List<Expression> partitionExpressions,
Function<Expression, Expression> resolveAliasExpression) {
String separator = "";
for ( Expression partitionExpression : partitionExpressions ) {
if ( partitionExpression instanceof SqlTupleContainer ) {
for ( Expression e : ( (SqlTupleContainer) partitionExpression ).getSqlTuple().getExpressions() ) {
appendSql( separator );
renderPartitionItem( resolveAliasedExpression( partitionExpression ) );
renderPartitionItem( resolveAliasExpression.apply( e ) );
separator = COMA_SEPARATOR;
}
separator = COMA_SEPARATOR;
}
else {
appendSql( separator );
renderPartitionItem( resolveAliasExpression.apply( partitionExpression ) );
}
separator = COMA_SEPARATOR;
}
}
@ -2905,7 +2919,22 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
@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,16 +3405,11 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
@Override
public void visitTuple(SqlTuple tuple) {
boolean isCurrentWhereClause = clauseStack.getCurrent() == Clause.WHERE;
if ( isCurrentWhereClause ) {
appendSql( OPEN_PARENTHESIS );
}
appendSql( OPEN_PARENTHESIS );
renderCommaSeparated( tuple.getExpressions() );
if ( isCurrentWhereClause ) {
appendSql( CLOSE_PARENTHESIS );
}
appendSql( CLOSE_PARENTHESIS );
}
protected final void renderCommaSeparated(Iterable<? extends SqlAstNode> expressions) {

View File

@ -38,6 +38,21 @@ public class HqlOrderByIdsTest {
);
}
@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)
@ -70,7 +85,7 @@ public class HqlOrderByIdsTest {
"SELECT p " +
"FROM Person p " +
"WHERE p.id IN (:ids) " +
"ORDER BY FIELD(id, :ids) ", Person.class )
"ORDER BY FIELD(id, :ids) ", Person.class )
.setParameter( "ids", Arrays.asList( 3L, 1L, 2L ) )
.getResultList();