Fix multiparameter binding issue
This commit is contained in:
parent
e4571cbf30
commit
967553add4
|
@ -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
|
||||
|
|
|
@ -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( ")" );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue