HHH-15928 Check distinct and sub-queries for SQLServer fetch / offset

This commit is contained in:
Marco Belladelli 2023-01-11 13:02:53 +01:00 committed by Christian Beikov
parent 21d7d2bb10
commit b5d58e69ae
2 changed files with 12 additions and 31 deletions

View File

@ -11,16 +11,15 @@ import java.util.List;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
@ -252,6 +251,10 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
else if ( version.isBefore( 11 ) || !isRowsOnlyFetchClauseType( queryPart ) ) { else if ( version.isBefore( 11 ) || !isRowsOnlyFetchClauseType( queryPart ) ) {
return OffsetFetchClauseMode.EMULATED; return OffsetFetchClauseMode.EMULATED;
} }
else if ( !queryPart.hasSortSpecifications() && ((QuerySpec) queryPart).getSelectClause().isDistinct() ) {
// order by (select 0) workaround for offset / fetch does not work when query is distinct
return OffsetFetchClauseMode.EMULATED;
}
else { else {
return OffsetFetchClauseMode.STANDARD; return OffsetFetchClauseMode.STANDARD;
} }
@ -310,14 +313,6 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
return getDialect().getVersion().isBefore( 9 ); return getDialect().getVersion().isBefore( 9 );
} }
@Override
protected void renderFetchPlusOffsetExpression(
Expression fetchClauseExpression,
Expression offsetClauseExpression,
int offset) {
renderFetchPlusOffsetExpressionAsSingleParameter( fetchClauseExpression, offsetClauseExpression, offset );
}
@Override @Override
protected void visitSqlSelections(SelectClause selectClause) { protected void visitSqlSelections(SelectClause selectClause) {
final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent(); final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent();
@ -346,7 +341,7 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
protected void renderEmptyOrderBy() { protected void renderEmptyOrderBy() {
// Always need an order by clause: https://blog.jooq.org/2014/05/13/sql-server-trick-circumvent-missing-order-by-clause/ // Always need an order by clause: https://blog.jooq.org/2014/05/13/sql-server-trick-circumvent-missing-order-by-clause/
appendSql( "order by @@version" ); appendSql( "order by (select 0)" );
} }
@Override @Override
@ -355,7 +350,6 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
if ( getDialect().getVersion().isBefore( 9 ) && !queryPart.isRoot() && queryPart.getOffsetClauseExpression() != null ) { if ( getDialect().getVersion().isBefore( 9 ) && !queryPart.isRoot() && queryPart.getOffsetClauseExpression() != null ) {
throw new IllegalArgumentException( "Can't emulate offset clause in subquery" ); throw new IllegalArgumentException( "Can't emulate offset clause in subquery" );
} }
// Note that SQL Server is very strict i.e. it requires an order by clause for TOP or OFFSET
final OffsetFetchClauseMode offsetFetchClauseMode = getOffsetFetchClauseMode( queryPart ); final OffsetFetchClauseMode offsetFetchClauseMode = getOffsetFetchClauseMode( queryPart );
if ( offsetFetchClauseMode == OffsetFetchClauseMode.STANDARD ) { if ( offsetFetchClauseMode == OffsetFetchClauseMode.STANDARD ) {
if ( !queryPart.hasSortSpecifications() ) { if ( !queryPart.hasSortSpecifications() ) {
@ -387,10 +381,6 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
renderFetch( fetchExpression, null, fetchClauseType ); renderFetch( fetchExpression, null, fetchClauseType );
} }
} }
else if ( offsetFetchClauseMode == OffsetFetchClauseMode.TOP_ONLY && !queryPart.hasSortSpecifications() ) {
appendSql( ' ' );
renderEmptyOrderBy();
}
} }
} }

View File

@ -230,6 +230,10 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
else if ( version.isBefore( 11 ) || !isRowsOnlyFetchClauseType( queryPart ) ) { else if ( version.isBefore( 11 ) || !isRowsOnlyFetchClauseType( queryPart ) ) {
return OffsetFetchClauseMode.EMULATED; return OffsetFetchClauseMode.EMULATED;
} }
else if ( !queryPart.hasSortSpecifications() && ((QuerySpec) queryPart).getSelectClause().isDistinct() ) {
// order by (select 0) workaround for offset / fetch does not work when query is distinct
return OffsetFetchClauseMode.EMULATED;
}
else { else {
return OffsetFetchClauseMode.STANDARD; return OffsetFetchClauseMode.STANDARD;
} }
@ -288,14 +292,6 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
return false; return false;
} }
@Override
protected void renderFetchPlusOffsetExpression(
Expression fetchClauseExpression,
Expression offsetClauseExpression,
int offset) {
renderFetchPlusOffsetExpressionAsSingleParameter( fetchClauseExpression, offsetClauseExpression, offset );
}
@Override @Override
protected void visitSqlSelections(SelectClause selectClause) { protected void visitSqlSelections(SelectClause selectClause) {
final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent(); final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent();
@ -324,13 +320,12 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
protected void renderEmptyOrderBy() { protected void renderEmptyOrderBy() {
// Always need an order by clause: https://blog.jooq.org/2014/05/13/sql-server-trick-circumvent-missing-order-by-clause/ // Always need an order by clause: https://blog.jooq.org/2014/05/13/sql-server-trick-circumvent-missing-order-by-clause/
appendSql( "order by @@version" ); appendSql( "order by (select 0)" );
} }
@Override @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
if ( !isRowNumberingCurrentQueryPart() ) { if ( !isRowNumberingCurrentQueryPart() ) {
// Note that SQL Server is very strict i.e. it requires an order by clause for TOP or OFFSET
final OffsetFetchClauseMode offsetFetchClauseMode = getOffsetFetchClauseMode( queryPart ); final OffsetFetchClauseMode offsetFetchClauseMode = getOffsetFetchClauseMode( queryPart );
if ( offsetFetchClauseMode == OffsetFetchClauseMode.STANDARD ) { if ( offsetFetchClauseMode == OffsetFetchClauseMode.STANDARD ) {
if ( !queryPart.hasSortSpecifications() ) { if ( !queryPart.hasSortSpecifications() ) {
@ -362,10 +357,6 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
renderFetch( fetchExpression, null, fetchClauseType ); renderFetch( fetchExpression, null, fetchClauseType );
} }
} }
else if ( offsetFetchClauseMode == OffsetFetchClauseMode.TOP_ONLY && !queryPart.hasSortSpecifications() ) {
appendSql( ' ' );
renderEmptyOrderBy();
}
} }
} }