diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java index 7b5671f5e17c..1456196f6a1b 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java @@ -590,6 +590,11 @@ public boolean supportsOuterJoinForUpdate() { return false; } + @Override + public boolean supportsCrossJoin() { + return false; + } + @Override public SequenceSupport getSequenceSupport() { return AltibaseSequenceSupport.INSTANCE; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseSqlAstTranslator.java index be1f10cf42fe..d070871c6d3a 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseSqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseSqlAstTranslator.java @@ -12,7 +12,6 @@ import org.hibernate.query.common.FrameExclusion; import org.hibernate.query.common.FrameKind; import org.hibernate.sql.ast.Clause; -import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.delete.DeleteStatement; @@ -21,14 +20,11 @@ import org.hibernate.sql.ast.tree.expression.FunctionExpression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Over; -import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; -import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -95,31 +91,6 @@ protected boolean shouldEmulateFetchClause(QueryPart queryPart) { && getDialect().supportsWindowFunctions() && !isRowsOnlyFetchClauseType( queryPart ); } - @Override - protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List tableGroupJoinCollector) { - // Use join instead because Altibase does not support cross apply - appendSql( WHITESPACE ); - if ( tableGroupJoin.getJoinType() != SqlAstJoinType.CROSS ) { - // No support for cross joins, so we emulate it with an inner join and always true on condition - appendSql( tableGroupJoin.getJoinType().getText() ); - } - appendSql( "join " ); - - final Predicate predicate; - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); - } - else { - predicate = tableGroupJoin.getPredicate(); - } - if ( predicate != null && !predicate.isEmpty() ) { - renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); - } - else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); - } - } - @Override protected void renderPartitionItem(Expression expression) { if ( expression instanceof Literal ) { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index 505122f8de2f..e80d480514ab 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -24,6 +24,7 @@ import org.hibernate.community.dialect.unique.InformixUniqueDelegate; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.dialect.NullOrdering; import org.hibernate.dialect.Replacer; import org.hibernate.dialect.SelectItemReferenceStrategy; @@ -348,6 +349,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.variance(); functionFactory.bitLength_pattern( "length(?1)*8" ); functionFactory.varPop_sumCount(); + functionFactory.hypotheticalOrderedSetAggregates(); final SqmFunctionRegistry functionRegistry = functionContributions.getFunctionRegistry(); final TypeConfiguration typeConfiguration = functionContributions.getTypeConfiguration(); @@ -1050,6 +1052,11 @@ public String getDual() { return "(select 0 from systables where tabid=1)"; } + @Override + public boolean supportsCrossJoin() { + return false; + } + @Override public boolean supportsRowValueConstructorSyntax() { return false; @@ -1078,4 +1085,9 @@ public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, @ } return super.buildIdentifierHelper( builder, metadata ); } + + @Override + public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { + return DmlTargetColumnQualifierSupport.TABLE_ALIAS; + } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java index 91d7fb49f761..d5c3b92d9701 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java @@ -23,6 +23,7 @@ import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.insert.ConflictClause; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; @@ -30,6 +31,7 @@ import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; +import org.hibernate.sql.ast.tree.update.UpdateStatement; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.model.internal.TableInsertStandard; @@ -136,6 +138,25 @@ public void visitOffsetFetchClause(QueryPart queryPart) { if ( !queryPart.isRoot() && queryPart.getOffsetClauseExpression() != null ) { throw new IllegalArgumentException( "Can't emulate offset clause in subquery" ); } + // We use 'select first n' on Informix, so nothing to do here + } + + @Override + protected void beforeQueryGroup(QueryGroup queryGroup, QueryPart currentQueryPart) { + if ( queryGroup.isRoot() && queryGroup.hasOffsetOrFetchClause() ) { + append( "select "); + renderFirstSkipClause( queryGroup.getOffsetClauseExpression(), + queryGroup.getFetchClauseExpression() ); + append( "* from " ); + append( OPEN_PARENTHESIS ); + } + } + + @Override + protected void afterQueryGroup(QueryGroup queryGroup, QueryPart currentQueryPart) { + if ( queryGroup.isRoot() && queryGroup.hasOffsetOrFetchClause() ) { + append( CLOSE_PARENTHESIS ); + } } @Override @@ -323,4 +344,22 @@ public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeti } super.visitBinaryArithmeticExpression( arithmeticExpression ); } + + @Override + protected void renderDmlTargetTableExpression(NamedTableReference tableReference) { + super.renderDmlTargetTableExpression( tableReference ); + if ( getClauseStack().getCurrent() != Clause.INSERT ) { + renderTableReferenceIdentificationVariable( tableReference ); + } + } + + @Override + protected void visitUpdateStatementOnly(UpdateStatement statement) { + if ( hasNonTrivialFromClause( statement.getFromClause() ) ) { + visitUpdateStatementEmulateMerge( statement ); + } + else { + super.visitUpdateStatementOnly( statement ); + } + } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacyDialect.java index 9a5731621d6b..a6a701dc0600 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacyDialect.java @@ -739,4 +739,8 @@ public boolean supportsJoinsInDelete() { return true; } + @Override + public boolean supportsCrossJoin() { + return false; + } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java index d61d0a4b4244..06e8b6cec319 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseASELegacySqlAstTranslator.java @@ -15,7 +15,6 @@ import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.sql.ast.Clause; -import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; @@ -28,19 +27,15 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.insert.ConflictClause; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.Values; -import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -236,30 +231,6 @@ private void renderLockHint(LockMode lockMode) { } } - @Override - protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List tableGroupJoinCollector) { - appendSql( WHITESPACE ); - if ( tableGroupJoin.getJoinType() != SqlAstJoinType.CROSS ) { - // No support for cross joins, so we emulate it with an inner join and always true on condition - appendSql( tableGroupJoin.getJoinType().getText() ); - } - appendSql( "join " ); - - final Predicate predicate; - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); - } - else { - predicate = tableGroupJoin.getPredicate(); - } - if ( predicate != null && !predicate.isEmpty() ) { - renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); - } - else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); - } - } - @Override protected LockStrategy determineLockingStrategy( QuerySpec querySpec, diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java index 47bf46cb80c0..a1a3be604cde 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java @@ -353,6 +353,11 @@ public boolean isCurrentTimestampSelectStringCallable() { return false; } + @Override + public boolean supportsCrossJoin() { + return false; + } + @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenSqlAstTranslator.java index 2fb793c59830..212bd59e5ad4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenSqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenSqlAstTranslator.java @@ -9,18 +9,13 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.sqm.ComparisonOperator; -import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; -import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -55,30 +50,6 @@ protected LockStrategy determineLockingStrategy( return strategy; } - @Override - protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List tableGroupJoinCollector) { - appendSql( WHITESPACE ); - if ( tableGroupJoin.getJoinType() != SqlAstJoinType.CROSS ) { - // No support for cross joins, so we emulate it with an inner join and always true on condition - appendSql( tableGroupJoin.getJoinType().getText() ); - } - appendSql( "join " ); - - final Predicate predicate; - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); - } - else { - predicate = tableGroupJoin.getPredicate(); - } - if ( predicate != null && !predicate.isEmpty() ) { - renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); - } - else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); - } - } - @Override protected void visitSqlSelections(SelectClause selectClause) { renderRowsToClause( (QuerySpec) getQueryPartStack().getCurrent() ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index de7067f3d978..87f41aefa8d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -6134,6 +6134,13 @@ public boolean supportsSimpleQueryGrouping() { return true; } + /** + * Is the {@code cross join} syntax supported? + */ + public boolean supportsCrossJoin() { + return true; + } + /** * Is this dialect known to support what ANSI-SQL terms "row value * constructor" syntax; sometimes called tuple syntax. diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java index 206c521d26f5..b4d70018047d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java @@ -764,4 +764,8 @@ public boolean supportsJoinsInDelete() { return true; } + @Override + public boolean supportsCrossJoin() { + return false; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SybaseASESqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SybaseASESqlAstTranslator.java index 42e18ee22e8c..af41fc09b2c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SybaseASESqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SybaseASESqlAstTranslator.java @@ -17,7 +17,6 @@ import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.sql.ast.Clause; -import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; @@ -30,19 +29,15 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.insert.ConflictClause; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.Values; -import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -224,30 +219,6 @@ private void renderLockHint(LockMode lockMode) { } } - @Override - protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List tableGroupJoinCollector) { - appendSql( WHITESPACE ); - if ( tableGroupJoin.getJoinType() != SqlAstJoinType.CROSS ) { - // No support for cross joins, so we emulate it with an inner join and always true on condition - appendSql( tableGroupJoin.getJoinType().getText() ); - } - appendSql( "join " ); - - final Predicate predicate; - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); - } - else { - predicate = tableGroupJoin.getPredicate(); - } - if ( predicate != null && !predicate.isEmpty() ) { - renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); - } - else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); - } - } - @Override protected LockStrategy determineLockingStrategy( QuerySpec querySpec, diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmTreeCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmTreeCreationHelper.java index bd308e3e5e26..691749f553a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmTreeCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmTreeCreationHelper.java @@ -7,10 +7,9 @@ import java.util.Locale; import java.util.Set; +import org.hibernate.AssertionFailure; import org.hibernate.grammars.hql.HqlParser; import org.hibernate.jpa.spi.JpaCompliance; -import org.hibernate.query.hql.spi.SqmCreationProcessingState; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.sqm.SqmTreeCreationLogger; import org.hibernate.query.sqm.StrictJpaComplianceViolation; import org.hibernate.query.sqm.tree.SqmJoinType; @@ -136,8 +135,7 @@ public static void handleRootAsCrossJoin( HqlParser.EntityWithJoinsContext entityWithJoinsContext, SqmRoot sqmPrimaryRoot, SemanticQueryBuilder sqmBuilder) { - final HqlParser.RootEntityContext fromRootContext = - (HqlParser.RootEntityContext) entityWithJoinsContext.fromRoot(); + final var fromRootContext = (HqlParser.RootEntityContext) entityWithJoinsContext.fromRoot(); //noinspection unchecked final SqmRoot sqmRoot = (SqmRoot) fromRootContext.accept( sqmBuilder ); @@ -150,9 +148,8 @@ public static void handleRootAsCrossJoin( ); sqmPrimaryRoot.addSqmJoin( pseudoCrossJoin ); - final SqmCreationProcessingState processingState = sqmBuilder.getProcessingStateStack().getCurrent(); - final SqmPathRegistry pathRegistry = processingState.getPathRegistry(); - pathRegistry.replace( pseudoCrossJoin, sqmRoot ); + sqmBuilder.getProcessingStateStack().getCurrent().getPathRegistry() + .replace( pseudoCrossJoin, sqmRoot ); final int size = entityWithJoinsContext.getChildCount(); for ( int i = 1; i < size; i++ ) { @@ -196,46 +193,39 @@ public static String extractVariable(HqlParser.VariableContext ctx, SemanticQuer if ( ctx == null ) { return null; } - - final ParseTree lastChild = ctx.getChild( ctx.getChildCount() - 1 ); - if ( lastChild instanceof HqlParser.IdentifierContext identifierContext ) { - // in this branch, the alias could be a reserved word ("keyword as identifier") - // which JPA disallows... - if ( sqmBuilder.getCreationOptions().useStrictJpaCompliance() ) { - final Token identificationVariableToken = identifierContext.getStart(); - if ( RESERVED_WORDS.contains( identificationVariableToken.getText().toLowerCase( Locale.ENGLISH ) ) ) { - throw new StrictJpaComplianceViolation( - String.format( - Locale.ROOT, - "Strict JPQL compliance was violated : %s [%s]", - StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS.description(), - identificationVariableToken.getText() - ), - StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS - ); - } + else { + final ParseTree lastChild = ctx.getChild( ctx.getChildCount() - 1 ); + if ( lastChild instanceof HqlParser.IdentifierContext identifierContext ) { + // in this branch, the alias could be a reserved word ("keyword as identifier") + // which JPA disallows... + checkJpaCompliance( sqmBuilder, identifierContext.getStart() ); + return sqmBuilder.visitIdentifier( identifierContext ); + } + else if ( lastChild instanceof HqlParser.NakedIdentifierContext identifierContext ) { + // in this branch, the alias could be a reserved word ("keyword as identifier") + // which JPA disallows... + checkJpaCompliance( sqmBuilder, identifierContext.getStart() ); + return sqmBuilder.visitNakedIdentifier( identifierContext ); + } + else { + throw new AssertionFailure( "Unexpected type parse of tree" ); } - return sqmBuilder.visitIdentifier( identifierContext ); } - else { - final HqlParser.NakedIdentifierContext identifierContext = (HqlParser.NakedIdentifierContext) lastChild; - // in this branch, the alias could be a reserved word ("keyword as identifier") - // which JPA disallows... - if ( sqmBuilder.getCreationOptions().useStrictJpaCompliance() ) { - final Token identificationVariableToken = identifierContext.getStart(); - if ( RESERVED_WORDS.contains( identificationVariableToken.getText().toLowerCase( Locale.ENGLISH ) ) ) { - throw new StrictJpaComplianceViolation( - String.format( - Locale.ROOT, - "Strict JPQL compliance was violated : %s [%s]", - StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS.description(), - identificationVariableToken.getText() - ), - StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS - ); - } + } + + private static void checkJpaCompliance(SemanticQueryBuilder sqmBuilder, Token identificationVariable) { + if ( sqmBuilder.getCreationOptions().useStrictJpaCompliance() ) { + if ( RESERVED_WORDS.contains( identificationVariable.getText().toLowerCase( Locale.ENGLISH ) ) ) { + throw new StrictJpaComplianceViolation( + String.format( + Locale.ROOT, + "Strict JPQL compliance was violated : %s [%s]", + StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS.description(), + identificationVariable.getText() + ), + StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS + ); } - return sqmBuilder.visitNakedIdentifier( identifierContext ); } } @@ -243,14 +233,9 @@ public static String extractVariable(HqlParser.VariableContext ctx, SemanticQuer * Handle JPA requirement that variables (aliases) be case-insensitive */ public static String applyJpaCompliance(String text, SemanticQueryBuilder sqmBuilder) { - if ( text == null ) { - return null; - } - - if ( sqmBuilder.getCreationOptions().useStrictJpaCompliance() ) { - return text.toLowerCase( Locale.getDefault() ); - } - - return text; + return text != null + && sqmBuilder.getCreationOptions().useStrictJpaCompliance() + ? text.toLowerCase( Locale.getDefault() ) + : text; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 6da178f30f3e..403523c804fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -3221,9 +3221,6 @@ private TableGroup consumeAttributeJoin( final SqmPathSource pathSource = sqmJoin.getReferencedPathSource(); final SqmJoinType sqmJoinType = sqmJoin.getSqmJoinType(); - final TableGroupJoin joinedTableGroupJoin; - final TableGroup joinedTableGroup; - final NavigablePath sqmJoinNavigablePath = sqmJoin.getNavigablePath(); final ModelPart modelPart = @@ -3231,43 +3228,27 @@ private TableGroup consumeAttributeJoin( .findSubPart( pathSource.getPathName(), resolveExplicitTreatTarget( sqmJoin, this ) ); + final TableGroupJoin joinedTableGroupJoin = + ((TableGroupJoinProducer) modelPart) + .createTableGroupJoin( + sqmJoinNavigablePath, + ownerTableGroup, + sqmJoin.getExplicitAlias(), + null, + sqmJoinType.getCorrespondingSqlJoinType(), + sqmJoin.isFetched(), + sqmJoin.getJoinPredicate() != null, + this + ); + final TableGroup joinedTableGroup = joinedTableGroupJoin.getJoinedGroup(); + if ( pathSource instanceof PluralPersistentAttribute ) { assert modelPart instanceof PluralAttributeMapping; - final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) modelPart; - if ( sqmJoin.isFetched() ) { containsCollectionFetches = true; } - - joinedTableGroupJoin = pluralAttributeMapping.createTableGroupJoin( - sqmJoinNavigablePath, - ownerTableGroup, - sqmJoin.getExplicitAlias(), - null, - sqmJoinType.getCorrespondingSqlJoinType(), - sqmJoin.isFetched(), - sqmJoin.getJoinPredicate() != null, - this - ); - - joinedTableGroup = joinedTableGroupJoin.getJoinedGroup(); } else { - assert modelPart instanceof TableGroupJoinProducer; - - joinedTableGroupJoin = ( (TableGroupJoinProducer) modelPart ).createTableGroupJoin( - sqmJoinNavigablePath, - ownerTableGroup, - sqmJoin.getExplicitAlias(), - null, - sqmJoinType.getCorrespondingSqlJoinType(), - sqmJoin.isFetched(), - sqmJoin.getJoinPredicate() != null, - this - ); - - joinedTableGroup = joinedTableGroupJoin.getJoinedGroup(); - // Since this is an explicit join, we force the initialization of a possible lazy table group // to retain the cardinality, but only if this is a non-trivial attribute join. // Left or inner singular attribute joins without a predicate can be safely optimized away @@ -3381,7 +3362,8 @@ private TableGroup consumeEntityJoin(SqmEntityJoin sqmJoin, TableGroup lhsT final SqlAstJoinType correspondingSqlJoinType = sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(); final TableGroup tableGroup = entityDescriptor.createRootTableGroup( - correspondingSqlJoinType == SqlAstJoinType.INNER || correspondingSqlJoinType == SqlAstJoinType.CROSS, + correspondingSqlJoinType == SqlAstJoinType.INNER + || correspondingSqlJoinType == SqlAstJoinType.CROSS, sqmJoin.getNavigablePath(), sqmJoin.getExplicitAlias(), null, @@ -3504,7 +3486,8 @@ private TableGroup consumeFunctionJoin(SqmFunctionJoin sqmJoin, TableGroup pa sqmJoin.getNavigablePath(), sqmJoin.getExplicitAlias(), sqmJoin.isLateral(), - correspondingSqlJoinType == SqlAstJoinType.INNER || correspondingSqlJoinType == SqlAstJoinType.CROSS, + correspondingSqlJoinType == SqlAstJoinType.INNER + || correspondingSqlJoinType == SqlAstJoinType.CROSS, sqmJoin.getReusablePath( CollectionPart.Nature.INDEX.getName() ) != null ); getFromClauseIndex().register( sqmJoin, tableGroup ); @@ -3537,7 +3520,8 @@ private TableGroup consumeCteJoin(SqmCteJoin sqmJoin, TableGroup parentTableG getCteName( sqmJoin.getCte().getCteTable() ), sqmJoin.getNavigablePath(), sqmJoin.getExplicitAlias(), - correspondingSqlJoinType == SqlAstJoinType.INNER || correspondingSqlJoinType == SqlAstJoinType.CROSS + correspondingSqlJoinType == SqlAstJoinType.INNER + || correspondingSqlJoinType == SqlAstJoinType.CROSS ); getFromClauseIndex().register( sqmJoin, tableGroup ); @@ -3613,10 +3597,8 @@ private X prepareReusablePath( if ( sqmPath instanceof SqmEntityValuedSimplePath || sqmPath instanceof SqmEmbeddedValuedSimplePath || sqmPath instanceof SqmAnyValuedSimplePath ) { - final TableGroup existingTableGroup = fromClauseIndex.findTableGroupForGetOrCreate( - sqmPath.getNavigablePath(), - allowLeftJoins - ); + final TableGroup existingTableGroup = + fromClauseIndex.findTableGroupForGetOrCreate( sqmPath.getNavigablePath(), allowLeftJoins ); if ( existingTableGroup == null ) { final TableGroup createdTableGroup = createTableGroup( getActualTableGroup( @@ -3626,10 +3608,8 @@ private X prepareReusablePath( sqmPath, allowLeftJoins ); - if ( createdTableGroup != null ) { - if ( sqmPath instanceof SqmTreatedPath ) { - fromClauseIndex.register( sqmPath, createdTableGroup ); - } + if ( createdTableGroup != null && sqmPath instanceof SqmTreatedPath ) { + fromClauseIndex.register( sqmPath, createdTableGroup ); } } } @@ -3643,8 +3623,8 @@ private TableGroup prepareReusablePath( final SqmPath sqmPath = (SqmPath) path; final SqmPath parentPath; final boolean treated; - if ( sqmPath instanceof SqmTreatedPath ) { - parentPath = ( (SqmTreatedPath) sqmPath ).getWrappedPath(); + if ( sqmPath instanceof SqmTreatedPath treatedPath ) { + parentPath = treatedPath.getWrappedPath(); treated = true; } else { @@ -3652,23 +3632,18 @@ private TableGroup prepareReusablePath( treated = false; } if ( parentPath == null ) { - if ( sqmPath instanceof SqmFunctionPath functionPath ) { - if ( functionPath.getReferencedPathSource() instanceof CompositeSqmPathSource ) { - return (TableGroup) visitFunctionPath( functionPath ); - } - } - return null; + return sqmPath instanceof SqmFunctionPath functionPath + && functionPath.getReferencedPathSource() instanceof CompositeSqmPathSource + ? (TableGroup) visitFunctionPath( functionPath ) + : null; } final TableGroup parentTableGroup = getActualTableGroup( fromClauseIndex.findTableGroupForGetOrCreate( parentPath.getNavigablePath() ), sqmPath ); if ( parentTableGroup == null ) { - final TableGroup createdParentTableGroup = prepareReusablePath( - fromClauseIndex, - parentPath, - implicitJoinChecker - ); + final TableGroup createdParentTableGroup = + prepareReusablePath( fromClauseIndex, parentPath, implicitJoinChecker ); if ( createdParentTableGroup == null ) { throw new SqlTreeCreationException( "Could not locate TableGroup - " + parentPath.getNavigablePath() ); } @@ -3677,12 +3652,10 @@ private TableGroup prepareReusablePath( fromClauseIndex.register( parentPath, createdParentTableGroup ); newTableGroup = createdParentTableGroup; } - else if ( createdParentTableGroup instanceof PluralTableGroup ) { - final CollectionPart.Nature nature = CollectionPart.Nature.fromName( - parentPath.getNavigablePath().getLocalName() - ); + else if ( createdParentTableGroup instanceof PluralTableGroup pluralTableGroup ) { + final var nature = CollectionPart.Nature.fromName( parentPath.getNavigablePath().getLocalName() ); assert nature != null; - newTableGroup = ( (PluralTableGroup) createdParentTableGroup ).getTableGroup( nature ); + newTableGroup = pluralTableGroup.getTableGroup( nature ); } else { newTableGroup = getActualTableGroup( @@ -3726,10 +3699,9 @@ private void upgradeToInnerJoinIfNeeded( // fetched - the fetch would use a left-join. however, since the path is // used outside the select-clause also, we need to force the join to be inner final String partName = sqmPath.getResolvedModel().getPathName(); - final ModelPart pathPart; if ( !toOneAttributeMapping.isFkOptimizationAllowed() - || !( ( pathPart = toOneAttributeMapping.findSubPart( partName ) ) instanceof ValuedModelPart ) - || !toOneAttributeMapping.getForeignKeyDescriptor().isKeyPart( (ValuedModelPart) pathPart ) ) { + || !( toOneAttributeMapping.findSubPart( partName ) instanceof ValuedModelPart pathPart ) + || !toOneAttributeMapping.getForeignKeyDescriptor().isKeyPart( pathPart ) ) { final NavigablePath parentParentPath = parentPath.getParentPath().getNavigablePath(); final TableGroup parentParentTableGroup = fromClauseIndex.findTableGroup( parentParentPath ); final TableGroupJoin tableGroupJoin = parentParentTableGroup.findTableGroupJoin( parentTableGroup ); @@ -3743,20 +3715,16 @@ private void upgradeToInnerJoinIfNeeded( } private void prepareForSelection(SqmPath selectionPath) { - final SqmPath path; // Don't create joins for plural part paths as that will be handled // through a cardinality preserving mechanism in visitIndexAggregateFunction/visitElementAggregateFunction - if ( selectionPath instanceof AbstractSqmSpecificPluralPartPath ) { - path = selectionPath.getLhs().getLhs(); - } - else { - path = selectionPath; - } + final SqmPath path = + selectionPath instanceof AbstractSqmSpecificPluralPartPath + ? selectionPath.getLhs().getLhs() + : selectionPath; final FromClauseIndex fromClauseIndex = getFromClauseIndex(); final TableGroup tableGroup = fromClauseIndex.findTableGroupForGetOrCreate( path.getNavigablePath() ); if ( tableGroup == null ) { prepareReusablePath( path, () -> null ); - if ( path.getLhs() != null && !( path instanceof SqmEntityValuedSimplePath || path instanceof SqmEmbeddedValuedSimplePath || path instanceof SqmAnyValuedSimplePath @@ -3792,8 +3760,8 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath join final FromClauseIndex fromClauseIndex = getFromClauseIndex(); final ModelPart subPart = parentTableGroup.getModelPart().findSubPart( joinedPath.getReferencedPathSource().getPathName(), - lhsPath instanceof SqmTreatedPath && ( (SqmTreatedPath) lhsPath ).getTreatTarget().getPersistenceType() == ENTITY - ? resolveEntityPersister( (EntityDomainType) ( (SqmTreatedPath) lhsPath ).getTreatTarget() ) + lhsPath instanceof SqmTreatedPath treatedPath && treatedPath.getTreatTarget().getPersistenceType() == ENTITY + ? resolveEntityPersister( (EntityDomainType) treatedPath.getTreatTarget() ) : null ); @@ -3816,23 +3784,16 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath join queryNodeProcessingState.getFromClause().addRoot( tableGroup ); } else { - final TableGroupJoin compatibleLeftJoin; - final SqlAstJoinType sqlAstJoinType; - if ( isMappedByOrNotFoundToOne( joinProducer ) ) { - compatibleLeftJoin = parentTableGroup.findCompatibleJoin( - joinProducer, - SqlAstJoinType.LEFT - ); - sqlAstJoinType = SqlAstJoinType.LEFT; - } - else { - compatibleLeftJoin = null; - sqlAstJoinType = null; - } - - final TableGroup compatibleTableGroup = compatibleLeftJoin != null ? - compatibleLeftJoin.getJoinedGroup() : - parentTableGroup.findCompatibleJoinedGroup( joinProducer, SqlAstJoinType.INNER ); + final boolean mappedByOrNotFoundToOne = isMappedByOrNotFoundToOne( joinProducer ); + final TableGroupJoin compatibleLeftJoin = + mappedByOrNotFoundToOne + ? parentTableGroup.findCompatibleJoin( joinProducer, SqlAstJoinType.LEFT ) + : null; + final SqlAstJoinType sqlAstJoinType = mappedByOrNotFoundToOne ? SqlAstJoinType.LEFT : null; + final TableGroup compatibleTableGroup = + compatibleLeftJoin != null + ? compatibleLeftJoin.getJoinedGroup() + : parentTableGroup.findCompatibleJoinedGroup( joinProducer, SqlAstJoinType.INNER ); if ( compatibleTableGroup == null ) { final TableGroupJoin tableGroupJoin = joinProducer.createTableGroupJoin( joinedPath.getNavigablePath(), @@ -3877,18 +3838,16 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath join private boolean isMappedByOrNotFoundToOne(TableGroupJoinProducer joinProducer) { if ( joinProducer instanceof ToOneAttributeMapping toOne ) { - return toOne.hasNotFoundAction() || - // ToOne( mappedBy = "..." ) always has a referenced property name and is the target side - ( toOne.getReferencedPropertyName() != null && toOne.getSideNature() == ForeignKeyDescriptor.Nature.TARGET ); + return toOne.hasNotFoundAction() + // ToOne( mappedBy = "..." ) always has a referenced property name and is the target side + || toOne.getReferencedPropertyName() != null && toOne.getSideNature() == ForeignKeyDescriptor.Nature.TARGET; } return false; } private boolean isRecursiveCte(TableGroup tableGroup) { - if ( tableGroup instanceof CteTableGroup cteTableGroup ) { - return cteContainer.getCteStatement( cteTableGroup.getPrimaryTableReference().getTableId() ).isRecursive(); - } - return false; + return tableGroup instanceof CteTableGroup cteTableGroup + && cteContainer.getCteStatement( cteTableGroup.getPrimaryTableReference().getTableId() ).isRecursive(); } private void registerPluralTableGroupParts(TableGroup tableGroup) { @@ -4244,18 +4203,15 @@ else if ( actualModelPart instanceof EmbeddableValuedModelPart mapping ) { mapping.getContainingTableExpression() ); - final Expression expression = getSqlExpressionResolver().resolveSqlExpression( - tableReference, - mapping - ); + final Expression expression = + getSqlExpressionResolver() + .resolveSqlExpression( tableReference, mapping ); final ColumnReference columnReference; - if ( expression instanceof ColumnReference ) { - columnReference = (ColumnReference) expression; + if ( expression instanceof ColumnReference columnRef ) { + columnReference = columnRef; } - else if ( expression instanceof SqlSelectionExpression ) { - final Expression selectedExpression = ( (SqlSelectionExpression) expression ).getSelection().getExpression(); - assert selectedExpression instanceof ColumnReference; - columnReference = (ColumnReference) selectedExpression; + else if ( expression instanceof SqlSelectionExpression sqlSelectionExpression ) { + columnReference = (ColumnReference) sqlSelectionExpression.getSelection().getExpression(); } else { throw new UnsupportedOperationException( "Unsupported basic-valued path expression : " + expression ); @@ -4352,25 +4308,7 @@ else if ( appliedByUnit != null ) { // producing a literal scalar value, so // we must convert this duration from // nanoseconds to the given unit - - JdbcMappingContainer durationType = scaledExpression.getExpressionType(); - Duration duration; - if ( durationType.getSingleJdbcMapping().getJdbcType().isInterval() ) { - // For interval types, we need to extract the epoch for integer arithmetic for the 'by unit' operator - duration = new Duration( - extractEpoch( scaledExpression ), - SECOND, - (BasicValuedMapping) durationType - ); - } - else { - // Durations are stored as nanoseconds (see DurationJavaType) - duration = new Duration( scaledExpression, NANOSECOND, (BasicValuedMapping) durationType ); - } - - TemporalUnit appliedUnit = appliedByUnit.getUnit().getUnit(); - BasicValuedMapping scalarType = (BasicValuedMapping) appliedByUnit.getNodeType(); - result = new Conversion( duration, appliedUnit, scalarType ); + result = unitConversion( scaledExpression ); } else { // a "bare" Duration value in nanoseconds @@ -4381,11 +4319,21 @@ else if ( appliedByUnit != null ) { return withTreatRestriction( result, sqmPath ); } + private Expression unitConversion(Expression scaledExpression) { + final var durationType = (BasicValuedMapping) scaledExpression.getExpressionType(); + final var scalarType = (BasicValuedMapping) appliedByUnit.getNodeType(); + final var duration = + durationType.getSingleJdbcMapping().getJdbcType().isInterval() + // For interval types, we need to extract the epoch for integer arithmetic for the 'by unit' operator + ? new Duration( extractEpoch( scaledExpression ), SECOND, durationType ) + // Durations are stored as nanoseconds (see DurationJavaType) + : new Duration( scaledExpression, NANOSECOND, durationType ); + return new Conversion( duration, appliedByUnit.getUnit().getUnit(), scalarType ); + } + private Expression extractEpoch(Expression intervalExpression) { - final BasicType intType = getTypeConfiguration().getBasicTypeForJavaType( Integer.class ); - final PatternRenderer patternRenderer = new PatternRenderer( - creationContext.getDialect().extractPattern( EPOCH ) - ); + final var intType = getTypeConfiguration().getBasicTypeForJavaType( Integer.class ); + final var patternRenderer = new PatternRenderer( creationContext.getDialect().extractPattern( EPOCH ) ); return new SelfRenderingFunctionSqlAstExpression<>( "extract", (sqlAppender, sqlAstArguments, returnType, walker) -> diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index a530fdb0f911..72847ecb2d5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -3473,7 +3473,6 @@ protected void renderQueryGroup(QueryGroup queryGroup, boolean renderOrderByAndO final int queryPartForRowNumberingClauseDepth = this.queryPartForRowNumberingClauseDepth; final boolean needsSelectAliases = this.needsSelectAliases; try { - String queryGroupAlias = null; // See the field documentation of queryPartForRowNumbering etc. for an explanation about this final QueryPart currentQueryPart = queryPartStack.getCurrent(); if ( currentQueryPart != null && queryPartForRowNumberingClauseDepth != clauseStack.depth() ) { @@ -3482,88 +3481,12 @@ protected void renderQueryGroup(QueryGroup queryGroup, boolean renderOrderByAndO // If explicit column aliases were defined we should still use them when rendering the select clause this.needsSelectAliases = columnAliases != null; } - // If we are row numbering the current query group, this means that we can't render the - // order by and offset fetch clause, so we must do row counting on the query group level - final boolean needsRowNumberingWrapper = queryPartForRowNumbering == queryGroup - || additionalWherePredicate != null && !additionalWherePredicate.isEmpty(); - final boolean needsQueryGroupWrapper = - currentQueryPart instanceof QueryGroup && !dialect.supportsSimpleQueryGrouping(); - final boolean needsParenthesis; - if ( currentQueryPart instanceof QueryGroup ) { - // When this is query group within a query group, we can only do simple grouping if that is supported, - // and we don't already add a query group wrapper - needsParenthesis = !needsRowNumberingWrapper && !needsQueryGroupWrapper; - } - else { - needsParenthesis = queryGroup.hasOffsetOrFetchClause() && !queryGroup.isRoot(); - } - if ( needsParenthesis ) { - appendSql( OPEN_PARENTHESIS ); - } - if ( needsRowNumberingWrapper ) { - this.needsSelectAliases = true; - queryGroupAlias = "grp_" + queryGroupAliasCounter + '_'; - queryGroupAliasCounter++; - appendSql( "select " ); - appendSql( queryGroupAlias ); - appendSql( ".* " ); - final SelectClause firstSelectClause = queryGroup.getFirstQuerySpec().getSelectClause(); - final List sqlSelections = firstSelectClause.getSqlSelections(); - final int sqlSelectionsSize = sqlSelections.size(); - // We need this synthetic select clause to properly render the ORDER BY within the OVER clause - // of the row numbering functions - final SelectClause syntheticSelectClause = new SelectClause( sqlSelectionsSize ); - for ( int i = 0; i < sqlSelectionsSize; i++ ) { - syntheticSelectClause.addSqlSelection( - new SqlSelectionImpl( - i, - new ColumnReference( - queryGroupAlias, - "c" + i, - false, - null, - getIntegerType() - ) - ) - ); - } - renderRowNumberingSelectItems( syntheticSelectClause, queryPartForRowNumbering ); - appendSql( " from (" ); - } - else if ( needsQueryGroupWrapper ) { - // Query group nested inside a query group - this.needsSelectAliases = true; - queryGroupAlias = "grp_" + queryGroupAliasCounter + '_'; - queryGroupAliasCounter++; - appendSql( "select " ); - appendSql( queryGroupAlias ); - appendSql( ".* " ); - appendSql( " from (" ); - } - queryPartStack.push( queryGroup ); - final List queryParts = queryGroup.getQueryParts(); - final String setOperatorString = ' ' + queryGroup.getSetOperator().sqlString() + ' '; - String separator = ""; - for ( int i = 0; i < queryParts.size(); i++ ) { - appendSql( separator ); - queryParts.get( i ).accept( this ); - separator = setOperatorString; - } - - if ( renderOrderByAndOffsetFetchClause ) { - visitOrderBy( queryGroup.getSortSpecifications() ); - visitOffsetFetchClause( queryGroup ); - } - if ( queryGroupAlias != null ) { - appendSql( ") " ); - appendSql( queryGroupAlias ); - if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) { - visitWhereClause( additionalWherePredicate ); - } - } - if ( needsParenthesis ) { - appendSql( CLOSE_PARENTHESIS ); - } + renderQueryGroup( + queryGroup, + queryPartForRowNumbering, + currentQueryPart, + renderOrderByAndOffsetFetchClause + ); } finally { queryPartStack.pop(); @@ -3573,6 +3496,146 @@ else if ( needsQueryGroupWrapper ) { } } + private void renderQueryGroup( + QueryGroup queryGroup, + QueryPart queryPartForRowNumbering, + QueryPart currentQueryPart, + boolean renderOrderByAndOffsetFetchClause) { + final boolean needsRowNumberingWrapper = + needsRowNumbering( queryGroup, queryPartForRowNumbering ); + final boolean needsQueryGroupWrapper = + nonsimpleQueryGrouping( currentQueryPart ); + final boolean needsParenthesis = + needsParenthesesAroundQueryGroup( + queryGroup, + currentQueryPart, + needsRowNumberingWrapper, + needsQueryGroupWrapper + ); + if ( needsParenthesis ) { + appendSql( OPEN_PARENTHESIS ); + } + beforeQueryGroup( queryGroup, currentQueryPart ); + final String queryGroupAlias = + wrapQueryPartsIfNecessary( + queryGroup, + queryPartForRowNumbering, + needsRowNumberingWrapper, + needsQueryGroupWrapper + ); + queryPartStack.push( queryGroup ); + renderQueryParts( queryGroup ); + afterQueryGroup( queryGroup, currentQueryPart ); + + if ( renderOrderByAndOffsetFetchClause ) { + visitOrderBy( queryGroup.getSortSpecifications() ); + visitOffsetFetchClause( queryGroup ); + } + if ( queryGroupAlias != null ) { + appendSql( CLOSE_PARENTHESIS ); + appendSql( WHITESPACE ); + appendSql( queryGroupAlias ); + if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) { + visitWhereClause( additionalWherePredicate ); + } + } + if ( needsParenthesis ) { + appendSql( CLOSE_PARENTHESIS ); + } + } + + private boolean nonsimpleQueryGrouping(QueryPart currentQueryPart) { + return currentQueryPart instanceof QueryGroup + && !dialect.supportsSimpleQueryGrouping(); + } + + private boolean needsRowNumbering(QueryGroup queryGroup, QueryPart queryPartForRowNumbering) { + // If we are row numbering the current query group, this means that we can't render the + // order by and offset fetch clause, so we must do row counting on the query group level + return queryPartForRowNumbering == queryGroup + || additionalWherePredicate != null && !additionalWherePredicate.isEmpty(); + } + + private String wrapQueryPartsIfNecessary( + QueryGroup queryGroup, + QueryPart queryPartForRowNumbering, + boolean needsRowNumberingWrapper, + boolean needsQueryGroupWrapper) { + if ( needsRowNumberingWrapper ) { + this.needsSelectAliases = true; + final String queryGroupAlias = "grp_" + queryGroupAliasCounter + '_'; + queryGroupAliasCounter++; + appendSql( "select " ); + appendSql( queryGroupAlias ); + appendSql( ".* " ); + final SelectClause firstSelectClause = queryGroup.getFirstQuerySpec().getSelectClause(); + final int sqlSelectionsSize = firstSelectClause.getSqlSelections().size(); + // We need this synthetic select clause to properly render the ORDER BY within the OVER clause + // of the row numbering functions + final SelectClause syntheticSelectClause = new SelectClause( sqlSelectionsSize ); + for ( int i = 0; i < sqlSelectionsSize; i++ ) { + syntheticSelectClause.addSqlSelection( + new SqlSelectionImpl( + i, + new ColumnReference( + queryGroupAlias, + "c" + i, + false, + null, + getIntegerType() + ) + ) + ); + } + renderRowNumberingSelectItems( syntheticSelectClause, queryPartForRowNumbering ); + appendSql( " from " ); + appendSql( OPEN_PARENTHESIS ); + return queryGroupAlias; + } + else if ( needsQueryGroupWrapper ) { + // Query group nested inside a query group + this.needsSelectAliases = true; + final String queryGroupAlias = "grp_" + queryGroupAliasCounter + '_'; + queryGroupAliasCounter++; + appendSql( "select " ); + appendSql( queryGroupAlias ); + appendSql( ".* " ); + appendSql( " from " ); + appendSql( OPEN_PARENTHESIS ); + return queryGroupAlias; + } + else { + return null; + } + } + + private void renderQueryParts(QueryGroup queryGroup) { + final var queryParts = queryGroup.getQueryParts(); + final String setOperatorString = ' ' + queryGroup.getSetOperator().sqlString() + ' '; + String separator = ""; + for ( int i = 0; i < queryParts.size(); i++ ) { + appendSql( separator ); + queryParts.get( i ).accept( this ); + separator = setOperatorString; + } + } + + protected void afterQueryGroup(QueryGroup queryGroup, QueryPart currentQueryPart) { + } + + protected void beforeQueryGroup(QueryGroup queryGroup, QueryPart currentQueryPart) { + } + + protected boolean needsParenthesesAroundQueryGroup( + QueryGroup queryGroup, QueryPart currentQueryPart, + boolean needsRowNumberingWrapper, boolean needsQueryGroupWrapper) { + return currentQueryPart instanceof QueryGroup + // When this is query group within a query group, we can only do simple grouping + // if that is supported, and we don't already add a query group wrapper + ? !needsRowNumberingWrapper && !needsQueryGroupWrapper + : queryGroup.hasOffsetOrFetchClause() && !queryGroup.isRoot(); + } + @Override public void visitQuerySpec(QuerySpec querySpec) { final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering; @@ -6395,27 +6458,25 @@ else if ( joinedGroup instanceof LazyTableGroup ) { protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List tableGroupJoinCollector) { appendSql( WHITESPACE ); - appendSql( tableGroupJoin.getJoinType().getText() ); + final boolean isCrossJoin = tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS; + if ( !isCrossJoin || dialect.supportsCrossJoin() ) { + appendSql( tableGroupJoin.getJoinType().getText() ); + } appendSql( "join " ); + final Predicate joinPredicate = tableGroupJoin.getPredicate(); final Predicate predicate; - if ( tableGroupJoin.getPredicate() == null ) { - if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) { - predicate = null; - } - else { - predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); - } - } - else { - predicate = tableGroupJoin.getPredicate(); - } - if ( predicate != null && !predicate.isEmpty() ) { - renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); + if ( joinPredicate == null ) { + predicate = + !isCrossJoin || !dialect.supportsCrossJoin() + ? new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ) + : null; } else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); + predicate = joinPredicate.isEmpty() ? null : joinPredicate; } + + renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); } protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { @@ -6449,6 +6510,9 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { } } + final BasicType intType = getIntegerType(); + final BasicType booleanType = getBooleanType(); + // Render with exists intersect sub-query if possible as that is shorter and more efficient // ... x(c) on exists(select x.c intersect ...) if ( shouldEmulateLateralWithIntersect( statement.getQueryPart() ) ) { @@ -6466,7 +6530,7 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { emptyList() ), false, - getBooleanType() + booleanType ); } @@ -6499,7 +6563,7 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { } final QuerySpec existsQuery = new QuerySpec( false, 1 ); existsQuery.getSelectClause().addSqlSelection( - new SqlSelectionImpl( new QueryLiteral<>( 1, getIntegerType() ) ) + new SqlSelectionImpl( new QueryLiteral<>( 1, intType ) ) ); existsQuery.getFromClause().addRoot( subTableGroup ); existsQuery.applyPredicate( @@ -6511,21 +6575,16 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { ); return new ExistsPredicate( - new SelectStatement( - statement, - existsQuery, - emptyList() - ), + new SelectStatement( statement, existsQuery, emptyList() ), false, - getBooleanType() + booleanType ); } final QueryPart queryPart = statement.getQueryPart(); - if ( queryPart instanceof QueryGroup ) { + if ( !( queryPart instanceof QuerySpec querySpec ) ) { // We can't use double nesting, but we need to add filter conditions, so fail if this is a query group throw new UnsupportedOperationException( "Can't emulate lateral query group with limit/offset" ); } - final QuerySpec querySpec = (QuerySpec) queryPart; // The last possible way to emulate lateral subqueries is to check if the correlated subquery has a result for a row. // Note though, that if the subquery has a limit/offset, an additional condition is needed as can be seen below @@ -6548,7 +6607,7 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { existsQuery.setGroupByClauseExpressions( querySpec.getGroupByClauseExpressions() ); existsQuery.setHavingClauseRestrictions( querySpec.getHavingClauseRestrictions() ); existsQuery.getSelectClause().addSqlSelection( - new SqlSelectionImpl( new QueryLiteral<>( 1, getIntegerType() ) ) + new SqlSelectionImpl( new QueryLiteral<>( 1, intType ) ) ); existsQuery.applyPredicate( new ComparisonPredicate( @@ -6559,13 +6618,9 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { ); final ExistsPredicate existsPredicate = new ExistsPredicate( - new SelectStatement( - statement, - existsQuery, - emptyList() - ), + new SelectStatement( statement, existsQuery, emptyList() ), false, - getBooleanType() + booleanType ); if ( !queryPart.hasOffsetOrFetchClause() ) { return existsPredicate; @@ -6584,11 +6639,12 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { new SqlSelectionImpl( new SelfRenderingAggregateFunctionSqlAstExpression<>( "count", - (sqlAppender, sqlAstArguments, returnType, walker) -> sqlAppender.append( "count(*)" ), + (sqlAppender, sqlAstArguments, returnType, walker) + -> sqlAppender.append( "count(*)" ), List.of( Star.INSTANCE ), null, - getIntegerType(), - getIntegerType() + intType, + intType ) ) ); @@ -6634,16 +6690,9 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { new NullnessPredicate( sortExpression ), new NullnessPredicate( currentRowColumnReference ) ), - getBooleanType() + booleanType ); } - final ComparisonOperator comparisonOperator; - if ( sortSpecification.getSortOrder() == SortDirection.DESCENDING ) { - comparisonOperator = ComparisonOperator.GREATER_THAN_OR_EQUAL; - } - else { - comparisonOperator = ComparisonOperator.LESS_THAN_OR_EQUAL; - } countQuery.applyPredicate( new Junction( Junction.Nature.DISJUNCTION, @@ -6651,11 +6700,13 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { nullHandlingPredicate, new ComparisonPredicate( sortExpression, - comparisonOperator, + sortSpecification.getSortOrder() == SortDirection.DESCENDING + ? ComparisonOperator.GREATER_THAN_OR_EQUAL + : ComparisonOperator.LESS_THAN_OR_EQUAL, currentRowColumnReference ) ), - getBooleanType() + booleanType ) ); } @@ -6663,21 +6714,21 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { final Expression countLower; final Expression countUpper; if ( queryPart.getOffsetClauseExpression() == null ) { - countLower = new QueryLiteral<>( 1, getIntegerType() ); + countLower = new QueryLiteral<>( 1, intType ); countUpper = queryPart.getFetchClauseExpression(); } else { countLower = new BinaryArithmeticExpression( queryPart.getOffsetClauseExpression(), BinaryArithmeticOperator.ADD, - new QueryLiteral<>( 1, getIntegerType() ), - getIntegerType() + new QueryLiteral<>( 1, intType ), + intType ); countUpper = new BinaryArithmeticExpression( queryPart.getOffsetClauseExpression(), BinaryArithmeticOperator.ADD, queryPart.getFetchClauseExpression(), - getIntegerType() + intType ); } return new Junction( @@ -6685,18 +6736,14 @@ protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { List.of( existsPredicate, new BetweenPredicate( - new SelectStatement( - statement, - countQuery, - emptyList() - ), + new SelectStatement( statement, countQuery, emptyList() ), countLower, countUpper, false, - getBooleanType() + booleanType ) ), - getBooleanType() + booleanType ); } return null; @@ -6773,22 +6820,25 @@ private QueryGroup stripToSelectClause(QueryGroup queryGroup) { for ( QueryPart queryPart : queryGroup.getQueryParts() ) { parts.add( stripToSelectClause( queryPart ) ); } - return new QueryGroup( queryGroup.isRoot(), queryGroup.getSetOperator(), parts ); } private QuerySpec stripToSelectClause(QuerySpec querySpec) { - if ( querySpec.getGroupByClauseExpressions() != null && !querySpec.getGroupByClauseExpressions().isEmpty() ) { + final var groupByExpressions = querySpec.getGroupByClauseExpressions(); + if ( groupByExpressions != null && !groupByExpressions.isEmpty() ) { throw new UnsupportedOperationException( "Can't emulate lateral join for query spec with group by clause" ); } - if ( querySpec.getHavingClauseRestrictions() != null && !querySpec.getHavingClauseRestrictions().isEmpty() ) { + final Predicate havingRestrictions = querySpec.getHavingClauseRestrictions(); + if ( havingRestrictions != null && !havingRestrictions.isEmpty() ) { throw new UnsupportedOperationException( "Can't emulate lateral join for query spec with having clause" ); } - final QuerySpec newQuerySpec = new QuerySpec( querySpec.isRoot(), querySpec.getFromClause().getRoots().size() ); - for ( TableGroup root : querySpec.getFromClause().getRoots() ) { + final var roots = querySpec.getFromClause().getRoots(); + final QuerySpec newQuerySpec = new QuerySpec( querySpec.isRoot(), roots.size() ); + for ( TableGroup root : roots ) { newQuerySpec.getFromClause().addRoot( root ); } - for ( SqlSelection selection : querySpec.getSelectClause().getSqlSelections() ) { + final SelectClause selectClause = querySpec.getSelectClause(); + for ( SqlSelection selection : selectClause.getSqlSelections() ) { if ( AggregateFunctionChecker.hasAggregateFunctions( selection.getExpression() ) ) { throw new UnsupportedOperationException( "Can't emulate lateral join for query spec with aggregate function" ); } @@ -6799,12 +6849,12 @@ private QuerySpec stripToSelectClause(QuerySpec querySpec) { private boolean needsLateralSortExpressionVirtualSelections(QuerySpec querySpec) { return !( ( querySpec.getSelectClause().getSqlSelections().size() == 1 - || dialect.supportsRowValueConstructorSyntax() ) - && dialect.supportsDistinctFromPredicate() - && isFetchFirstRowOnly( querySpec ) ) - && !shouldEmulateLateralWithIntersect( querySpec ) - && !dialect.supportsNestedSubqueryCorrelation() - && querySpec.hasOffsetOrFetchClause(); + || dialect.supportsRowValueConstructorSyntax() ) + && dialect.supportsDistinctFromPredicate() + && isFetchFirstRowOnly( querySpec ) ) + && !shouldEmulateLateralWithIntersect( querySpec ) + && !dialect.supportsNestedSubqueryCorrelation() + && querySpec.hasOffsetOrFetchClause(); } @Override @@ -6839,13 +6889,7 @@ public void visitColumnReference(ColumnReference columnReference) { if ( columnReference.isColumnExpressionFormula() ) { // For formulas, we have to replace the qualifier as the alias was already rendered into the formula // This is fine for now as this is only temporary anyway until we render aliases for table references - final String replacement; - if ( qualifier != null ) { - replacement = "$1" + qualifier + ".$3"; - } - else { - replacement = "$1$3"; - } + final String replacement = qualifier != null ? "$1" + qualifier + ".$3" : "$1$3"; appendSql( columnReference.getColumnExpression() .replaceAll( "(\\b)(" + columnReference.getQualifier() + "\\.)(\\b)", replacement ) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/QueryTimeOutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/QueryTimeOutTest.java index 6cacc23bde73..92d68bcf6a78 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/QueryTimeOutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/QueryTimeOutTest.java @@ -10,7 +10,6 @@ import java.util.Map; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.community.dialect.InformixDialect; import org.hibernate.dialect.AbstractTransactSQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SybaseDialect; @@ -79,9 +78,6 @@ else if ( DialectContext.getDialect() instanceof SybaseDialect ) { else if ( DialectContext.getDialect() instanceof AbstractTransactSQLDialect ) { baseQuery = "update ae1_0 set name=? from AnEntity ae1_0"; } - else if (DialectContext.getDialect() instanceof InformixDialect ) { - baseQuery = "update AnEntity set name=?"; - } else { baseQuery = "update AnEntity ae1_0 set name=?"; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaWindowFunctionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaWindowFunctionTest.java index 19512c8ad928..3a3c7ea601e9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaWindowFunctionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaWindowFunctionTest.java @@ -9,6 +9,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.AltibaseDialect; +import org.hibernate.community.dialect.InformixDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; @@ -199,6 +200,7 @@ public void testFirstValue(SessionFactoryScope scope) { @Test @SkipForDialect(dialectClass = SQLServerDialect.class, reason = "No support for nth_value function") @SkipForDialect(dialectClass = DB2Dialect.class, majorVersion = 10, reason = "No support for nth_value function") + @SkipForDialect(dialectClass = InformixDialect.class, reason = "No support for nth_value function") public void testNthValue(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaCrossJoinTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaCrossJoinTests.java index f9a148b56956..1dd694a6540b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaCrossJoinTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaCrossJoinTests.java @@ -6,6 +6,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.AltibaseDialect; +import org.hibernate.community.dialect.InformixDialect; import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.testing.jdbc.SQLStatementInspector; @@ -46,6 +47,7 @@ public void testCrossJoin(SessionFactoryScope scope) { @Test @SkipForDialect( dialectClass = AltibaseDialect.class, reason = "Altibase dialect emulate cross join with inner join") + @SkipForDialect(dialectClass = InformixDialect.class, reason = "Informix does not have cross joins") public void test2Roots(SessionFactoryScope scope) { final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); statementInspector.clear(); @@ -59,6 +61,7 @@ public void test2Roots(SessionFactoryScope scope) { @Test @SkipForDialect( dialectClass = AltibaseDialect.class, reason = "Altibase dialect emulate cross join with inner join") + @SkipForDialect(dialectClass = InformixDialect.class, reason = "Informix does not have cross joins") public void test2Roots2(SessionFactoryScope scope) { final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); statementInspector.clear();