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.
This commit is contained in:
Gavin King 2020-08-23 10:33:56 +02:00
parent 59d40c3bf5
commit d1119d320a
3 changed files with 19 additions and 7 deletions

View File

@ -141,7 +141,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
private ArrayList<ParameterSpecification> parameterSpecs = new ArrayList<>(); private ArrayList<ParameterSpecification> parameterSpecs = new ArrayList<>();
private int numberOfParametersInSetClause; private int numberOfParametersInSetClause;
private ArrayList assignmentSpecifications = new ArrayList(); private ArrayList<AssignmentSpecification> assignmentSpecifications = new ArrayList<>();
private JoinType impliedJoinType = JoinType.INNER_JOIN; private JoinType impliedJoinType = JoinType.INNER_JOIN;
@ -1393,7 +1393,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
} }
} }
public ArrayList getAssignmentSpecifications() { public ArrayList<AssignmentSpecification> getAssignmentSpecifications() {
return assignmentSpecifications; return assignmentSpecifications;
} }

View File

@ -63,6 +63,8 @@ import antlr.RecognitionException;
import antlr.TokenStreamException; import antlr.TokenStreamException;
import antlr.collections.AST; import antlr.collections.AST;
import static java.util.Collections.singleton;
/** /**
* A QueryTranslator that uses an Antlr-based parser. * A QueryTranslator that uses an Antlr-based parser.
* *
@ -609,10 +611,10 @@ public class QueryTranslatorImpl implements FilterTranslator {
else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) { else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) {
final FromElement fromElement = walker.getFinalFromClause().getFromElement(); final FromElement fromElement = walker.getFinalFromClause().getFromElement();
final Queryable persister = fromElement.getQueryable(); final Queryable persister = fromElement.getQueryable();
if ( persister.isMultiTable() ) {
// even here, if only properties mapped to the "base table" are referenced boolean affectsExtraTables = persister.isMultiTable()
// in the set and where clauses, this could be handled by the BasicDelegate. && !singleton( persister.getTableName() ).containsAll( walker.getQuerySpaces() );
// TODO : decide if it is better performance-wise to doAfterTransactionCompletion that check, or to simply use the MultiTableUpdateDelegate if ( affectsExtraTables ) {
return new MultiTableUpdateExecutor( walker ); return new MultiTableUpdateExecutor( walker );
} }
else { else {

View File

@ -17,6 +17,7 @@ import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource; 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.HqlSqlWalker;
import org.hibernate.hql.internal.ast.QuerySyntaxException; import org.hibernate.hql.internal.ast.QuerySyntaxException;
import org.hibernate.hql.internal.ast.SqlGenerator; import org.hibernate.hql.internal.ast.SqlGenerator;
@ -40,7 +41,16 @@ public class BasicExecutor implements StatementExecutor {
try { try {
SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() ); SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() );
gen.statement( walker.getAST() ); 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(); gen.getParseErrorHandler().throwQueryException();
parameterSpecifications = gen.getCollectedParameters(); parameterSpecifications = gen.getCollectedParameters();
} }