From d1119d320a96695341d102480648752a7208525b Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 23 Aug 2020 10:33:56 +0200 Subject: [PATCH] HHH-14153 optimize away temp table for single-table HQL update When a HQL bulk update query only touches a single table, use BasicExecutor instead of MultiTableUpdateExecutor. This is an alternate implementation to the one proposed by @NathanQingyangXu in #3508 and loosely based on that work. --- .../org/hibernate/hql/internal/ast/HqlSqlWalker.java | 4 ++-- .../hql/internal/ast/QueryTranslatorImpl.java | 10 ++++++---- .../hql/internal/ast/exec/BasicExecutor.java | 12 +++++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java index c6db01e8ad..bdb8ced180 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java @@ -141,7 +141,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par private ArrayList parameterSpecs = new ArrayList<>(); private int numberOfParametersInSetClause; - private ArrayList assignmentSpecifications = new ArrayList(); + private ArrayList assignmentSpecifications = new ArrayList<>(); private JoinType impliedJoinType = JoinType.INNER_JOIN; @@ -1393,7 +1393,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par } } - public ArrayList getAssignmentSpecifications() { + public ArrayList getAssignmentSpecifications() { return assignmentSpecifications; } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java index 21d94d1e64..e2efb5ad4d 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java @@ -63,6 +63,8 @@ import antlr.RecognitionException; import antlr.TokenStreamException; import antlr.collections.AST; +import static java.util.Collections.singleton; + /** * A QueryTranslator that uses an Antlr-based parser. * @@ -609,10 +611,10 @@ public class QueryTranslatorImpl implements FilterTranslator { else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) { final FromElement fromElement = walker.getFinalFromClause().getFromElement(); final Queryable persister = fromElement.getQueryable(); - if ( persister.isMultiTable() ) { - // even here, if only properties mapped to the "base table" are referenced - // in the set and where clauses, this could be handled by the BasicDelegate. - // TODO : decide if it is better performance-wise to doAfterTransactionCompletion that check, or to simply use the MultiTableUpdateDelegate + + boolean affectsExtraTables = persister.isMultiTable() + && !singleton( persister.getTableName() ).containsAll( walker.getQuerySpaces() ); + if ( affectsExtraTables ) { return new MultiTableUpdateExecutor( walker ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java index e000e39ec7..f178b9e61c 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java @@ -17,6 +17,7 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; +import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; import org.hibernate.hql.internal.ast.HqlSqlWalker; import org.hibernate.hql.internal.ast.QuerySyntaxException; import org.hibernate.hql.internal.ast.SqlGenerator; @@ -40,7 +41,16 @@ public class BasicExecutor implements StatementExecutor { try { SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() ); gen.statement( walker.getAST() ); - sql = gen.getSQL(); + if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) { + // workaround for a problem where HqlSqlWalker actually generates + // broken SQL with undefined aliases in the where clause, because + // that is what MultiTableUpdateExecutor is expecting to get + String alias = walker.getFinalFromClause().getFromElement().getTableAlias(); + sql = gen.getSQL().replace( alias + ".", "" ); + } + else { + sql = gen.getSQL(); + } gen.getParseErrorHandler().throwQueryException(); parameterSpecifications = gen.getCollectedParameters(); }