HHH-15766 Add query spec parenthesis also when order by is used within query group

This commit is contained in:
Christian Beikov 2023-03-03 15:25:20 +01:00
parent 59f57b6811
commit 2c666c3835
9 changed files with 54 additions and 18 deletions

View File

@ -335,8 +335,17 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
public void visitOffsetFetchClause(QueryPart queryPart) {
if ( !isRowNumberingCurrentQueryPart() ) {
if ( supportsOffsetFetchClause() ) {
if ( getQueryPartStack().depth() > 1 && queryPart.hasSortSpecifications()
&& getQueryPartStack().peek( 1 ) instanceof QueryGroup
&& ( queryPart.isRoot() && !hasLimit() || !queryPart.hasOffsetOrFetchClause() ) ) {
// If the current query part has a query group parent, no offset/fetch clause, but an order by clause,
// then we must render "offset 0 rows" as that is needed for the SQL to be valid
appendSql( " offset 0 rows" );
}
else {
renderOffsetFetchClause( queryPart, true );
}
}
else {
assertRowsOnlyFetchClauseType( queryPart );
}

View File

@ -323,6 +323,12 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
else if ( offsetFetchClauseMode == OffsetFetchClauseMode.EMULATED ) {
renderTopClause( querySpec, isRowsOnlyFetchClauseType( querySpec ), true );
}
else if ( getQueryPartStack().depth() > 1 && querySpec.hasSortSpecifications()
&& getQueryPartStack().peek( 1 ) instanceof QueryGroup ) {
// If the current query spec has a query group parent, no offset/fetch clause, but an order by clause,
// then we must render "top 100 percent" as that is needed for the SQL to be valid
appendSql( "top 100 percent " );
}
super.visitSqlSelections( selectClause );
}

View File

@ -339,8 +339,17 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
public void visitOffsetFetchClause(QueryPart queryPart) {
if ( !isRowNumberingCurrentQueryPart() ) {
if ( supportsOffsetFetchClause() ) {
if ( getQueryPartStack().depth() > 1 && queryPart.hasSortSpecifications()
&& getQueryPartStack().peek( 1 ) instanceof QueryGroup
&& ( queryPart.isRoot() && !hasLimit() || !queryPart.hasOffsetOrFetchClause() ) ) {
// If the current query part has a query group parent, no offset/fetch clause, but an order by clause,
// then we must render "offset 0 rows" as that is needed for the SQL to be valid
appendSql( " offset 0 rows" );
}
else {
renderOffsetFetchClause( queryPart, true );
}
}
else {
assertRowsOnlyFetchClauseType( queryPart );
}

View File

@ -302,6 +302,12 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
else if ( offsetFetchClauseMode == OffsetFetchClauseMode.EMULATED ) {
renderTopClause( querySpec, isRowsOnlyFetchClauseType( querySpec ), true );
}
else if ( getQueryPartStack().depth() > 1 && querySpec.hasSortSpecifications()
&& getQueryPartStack().peek( 1 ) instanceof QueryGroup ) {
// If the current query spec has a query group parent, no offset/fetch clause, but an order by clause,
// then we must render "top 100 percent" as that is needed for the SQL to be valid
appendSql( "top 100 percent " );
}
super.visitSqlSelections( selectClause );
}

View File

@ -146,9 +146,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
@Override
protected void visitSqlSelections(SelectClause selectClause) {
if ( supportsTopClause() ) {
renderTopClause( (QuerySpec) getQueryPartStack().getCurrent(), true, false );
}
super.visitSqlSelections( selectClause );
}
@ -192,7 +190,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
public void visitOffsetFetchClause(QueryPart queryPart) {
assertRowsOnlyFetchClauseType( queryPart );
if ( !queryPart.isRoot() && queryPart.hasOffsetOrFetchClause() ) {
if ( queryPart.getFetchClauseExpression() != null && !supportsTopClause() || queryPart.getOffsetClauseExpression() != null ) {
if ( queryPart.getFetchClauseExpression() != null && queryPart.getOffsetClauseExpression() != null ) {
throw new IllegalArgumentException( "Can't emulate offset fetch clause in subquery" );
}
}
@ -387,11 +385,6 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
return true;
}
@Override
protected boolean needsMaxRows() {
return !supportsTopClause();
}
@Override
protected boolean supportsRowValueConstructorSyntax() {
return false;
@ -412,10 +405,6 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
return " from (select 1) dual(c1)";
}
private boolean supportsTopClause() {
return true;
}
private boolean supportsParameterOffsetFetchExpression() {
return false;
}

View File

@ -35,6 +35,11 @@ public interface Stack<T> {
*/
T getCurrent();
/**
* The element at the given offset, relative to the top of the stack
*/
T peek(int offsetFromTop);
/**
* The element currently at the bottom of the stack
*/

View File

@ -72,6 +72,14 @@ public final class StandardStack<T> implements Stack<T> {
return elements[top - 1];
}
@Override
public T peek(int offsetFromTop) {
if ( isEmpty() ) {
return null;
}
return elements[top - offsetFromTop - 1];
}
@Override
public T getRoot() {
if ( isEmpty() ) {

View File

@ -2992,9 +2992,9 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
String queryGroupAlias = null;
if ( currentQueryPart instanceof QueryGroup ) {
// We always need query wrapping if we are in a query group and this query spec has a fetch clause
// because of order by precedence in SQL
if ( querySpec.hasOffsetOrFetchClause() ) {
// We always need query wrapping if we are in a query group and this query spec has a fetch or order by
// clause, because of order by precedence in SQL
if ( querySpec.hasOffsetOrFetchClause() || querySpec.hasSortSpecifications() ) {
queryGroupAlias = "";
// If the parent is a query group with a fetch clause we must use a select wrapper,
// or if the database does not support simple query grouping, we must use a select wrapper

View File

@ -2,10 +2,13 @@ package org.hibernate.orm.test.query.hql;
import java.util.List;
import org.hibernate.dialect.SybaseASEDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -22,6 +25,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
)
@SessionFactory
@TestForIssue(jiraKey = "HHH-15766")
@SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "Sybase ASE does not support order by in subqueries")
public class HqlUnionTest {
@BeforeAll