diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java index 6c3bfd0299..107406330f 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java @@ -701,6 +701,11 @@ public class FirebirdDialect extends Dialect { return getVersion().isSameOrAfter( 3, 0 ); } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 4, 0 ); + } + @Override public String translateExtractField(TemporalUnit unit) { switch ( unit ) { 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 18c3f2a69e..bce3c6abaf 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 @@ -341,6 +341,16 @@ public class InformixDialect extends Dialect { return false; } + @Override + public boolean supportsWindowFunctions() { + return getVersion().isSameOrAfter( 12, 10 ); + } + + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 12, 10 ); + } + @Override public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { return EXTRACTOR; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java index 958f8d0f74..5e0848465e 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java @@ -500,7 +500,11 @@ public class IngresDialect extends Dialect { return false; } - // Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + @Override + public boolean supportsWindowFunctions() { + return getVersion().isSameOrAfter( 10, 2 ); + } +// Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public boolean doesReadCommittedCauseWritersToBlockReaders() { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteSqlAstTranslator.java index f8ddd4affe..629b1a6734 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteSqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteSqlAstTranslator.java @@ -16,6 +16,7 @@ import org.hibernate.sql.ast.tree.expression.Any; import org.hibernate.sql.ast.tree.expression.Every; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -92,6 +93,11 @@ public class SQLiteSqlAstTranslator extends AbstractSql } } + @Override + public void visitQueryPartTableReference(QueryPartTableReference tableReference) { + emulateQueryPartTableReferenceColumnAliasing( tableReference ); + } + @Override public void visitOffsetFetchClause(QueryPart queryPart) { if ( !isRowNumberingCurrentQueryPart() ) { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java index 657d2d46de..d12c20c28a 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java @@ -30,7 +30,7 @@ import org.hibernate.sql.exec.spi.JdbcOperation; import static org.hibernate.type.SqlTypes.*; /** - * SQL Dialect for Sybase Anywhere + * SQL Dialect for Sybase/SQL Anywhere * (Tested on ASA 8.x) */ public class SybaseAnywhereDialect extends SybaseDialect { @@ -135,6 +135,16 @@ public class SybaseAnywhereDialect extends SybaseDialect { return false; } + @Override + public boolean supportsWindowFunctions() { + return getVersion().isSameOrAfter( 12 ); + } + + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 10 ); + } + @Override public IdentityColumnSupport getIdentityColumnSupport() { return new SybaseAnywhereIdentityColumnSupport(); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java index 8164572c6c..b6863b6626 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java @@ -379,6 +379,11 @@ public class TeradataDialect extends Dialect { return false; } + @Override + public boolean supportsWindowFunctions() { + return getVersion().isSameOrAfter( 16, 10 ); + } + @Override public String getSelectClauseNullString(int sqlType) { String v = "null"; 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 ac1cef026d..ee149f799a 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 @@ -11,7 +11,6 @@ import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; import org.hibernate.query.IllegalQueryOperationException; -import org.hibernate.query.SemanticException; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; @@ -19,10 +18,11 @@ import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; 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.Junction; +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; @@ -70,13 +70,12 @@ public class TimesTenSqlAstTranslator extends AbstractS } final Predicate predicate; - if ( tableGroupJoin.isLateral() ) { - append( "lateral " ); - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new Junction( Junction.Nature.CONJUNCTION ); + if ( tableGroupJoin.getPredicate() == null ) { + if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) { + predicate = null; } else { - predicate = tableGroupJoin.getPredicate(); + predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); } } else { @@ -86,7 +85,7 @@ public class TimesTenSqlAstTranslator extends AbstractS renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); } else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); + renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java index bb41ca290a..7aca2db91c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java @@ -25,6 +25,7 @@ import org.hibernate.engine.config.spi.ConfigurationService.Converter; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.*; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.*; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.exception.ConstraintViolationException; @@ -228,6 +229,26 @@ public abstract class AbstractHANADialect extends Dialect { return false; } + protected static DatabaseVersion createVersion(DialectResolutionInfo info) { + // Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html + final String versionString = info.getDatabaseVersion(); + int majorVersion = 1; + int minorVersion = 0; + int patchLevel = 0; + final String[] components = versionString.split( "\\." ); + if ( components.length >= 3 ) { + try { + majorVersion = Integer.parseInt( components[0] ); + minorVersion = Integer.parseInt( components[1] ); + patchLevel = Integer.parseInt( components[2] ); + } + catch (NumberFormatException ex) { + // Ignore + } + } + return DatabaseVersion.make( majorVersion, minorVersion, patchLevel ); + } + @Override public String castPattern(CastType from, CastType to) { if ( to == CastType.BOOLEAN ) { @@ -991,6 +1012,11 @@ public abstract class AbstractHANADialect extends Dialect { return true; } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 2, 0, 40 ); + } + @Override public boolean supportsNoWait() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index da807bae6b..b4cd951cb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -672,6 +672,11 @@ public class CockroachDialect extends Dialect { return true; } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 20, 1 ); + } + @Override public boolean supportsNoWait() { return getVersion().isSameOrAfter( 20, 1 ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 67a5ea2362..359b7b2c76 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -671,6 +671,11 @@ public class DB2Dialect extends Dialect { return true; } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 9, 1 ); + } + @Override public void appendDatetimeFormat(SqlAppender appender, String format) { //DB2 does not need nor support FM diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java index 6816097016..bb8dbdbfcc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java @@ -99,6 +99,11 @@ public class DB2iDialect extends DB2Dialect { return true; } + @Override + public boolean supportsLateral() { + return getIVersion().isSameOrAfter( 7, 1 ); + } + @Override public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { return new StandardSqlAstTranslatorFactory() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index a1c04e5f78..65e0abacac 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -42,7 +42,7 @@ public class DB2zDialect extends DB2Dialect { } public DB2zDialect() { - this( DatabaseVersion.make(7) ); + this( DatabaseVersion.make( 7 ) ); } public DB2zDialect(DatabaseVersion version) { @@ -53,7 +53,7 @@ public class DB2zDialect extends DB2Dialect { @Override protected String columnType(int jdbcTypeCode) { // See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html - if ( jdbcTypeCode==TIMESTAMP_WITH_TIMEZONE && version.isAfter(10) ) { + if ( jdbcTypeCode==TIMESTAMP_WITH_TIMEZONE && version.isAfter(10) ) { return "timestamp with time zone"; } return super.columnType(jdbcTypeCode); @@ -95,6 +95,11 @@ public class DB2zDialect extends DB2Dialect { return true; } + @Override + public boolean supportsLateral() { + return true; + } + @Override public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { StringBuilder pattern = new StringBuilder(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zSqlAstTranslator.java index 0639384902..69d5842766 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zSqlAstTranslator.java @@ -6,11 +6,18 @@ */ package org.hibernate.dialect; +import java.util.List; + +import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; 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.from.FunctionTableReference; +import org.hibernate.sql.ast.tree.from.NamedTableReference; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -50,4 +57,24 @@ public class DB2zSqlAstTranslator extends DB2SqlAstTran renderComparisonStandard( lhs, operator, rhs ); } + @Override + protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) { + final TableReference tableReference = tableGroup.getPrimaryTableReference(); + if ( tableReference instanceof NamedTableReference ) { + return renderNamedTableReference( (NamedTableReference) tableReference, lockMode ); + } + // DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries + append( "table " ); + tableReference.accept( this ); + return false; + } + + @Override + public void visitFunctionTableReference(FunctionTableReference tableReference) { + // For the table qualifier we need parenthesis on DB2 z/OS + append( OPEN_PARENTHESIS ); + tableReference.getFunctionExpression().accept( this ); + append( CLOSE_PARENTHESIS ); + renderDerivedTableReference( tableReference ); + } } 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 4bd3b01b90..522f549233 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -3503,6 +3503,16 @@ public abstract class Dialect implements ConversionContext { return false; } + /** + * Does this dialect support the SQL lateral keyword or a proprietary alternative= + * + * @return {@code true} if the underlying database supports lateral, + * {@code false} otherwise. The default is {@code false}. + */ + public boolean supportsLateral() { + return false; + } + public CallableStatementSupport getCallableStatementSupport() { // most databases do not support returning cursors (ref_cursor)... return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANACloudColumnStoreDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANACloudColumnStoreDialect.java index 59e24ceea3..77ca795421 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANACloudColumnStoreDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANACloudColumnStoreDialect.java @@ -6,11 +6,6 @@ */ package org.hibernate.dialect; -import java.sql.Types; - -import org.hibernate.query.spi.QueryEngine; -import org.hibernate.type.StandardBasicTypes; - /** * An SQL dialect for the SAP HANA Cloud column store. *

@@ -30,7 +25,29 @@ import org.hibernate.type.StandardBasicTypes; public class HANACloudColumnStoreDialect extends HANAColumnStoreDialect { public HANACloudColumnStoreDialect() { + // No idea how the versioning scheme is here, but since this is deprecated anyway, keep it as is super( DatabaseVersion.make( 4 ) ); } + @Override + public boolean supportsLateral() { + // Couldn't find a reference since when this is supported + return true; + } + + @Override + protected boolean supportsAsciiStringTypes() { + return getVersion().isBefore( 4 ); + } + + @Override + protected Boolean useUnicodeStringTypesDefault() { + return getVersion().isSameOrAfter( 4 ); + } + + @Override + public boolean isUseUnicodeStringTypes() { + return getVersion().isSameOrAfter( 4 ) || super.isUseUnicodeStringTypes(); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java index 4eee363562..e540723141 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java @@ -35,13 +35,15 @@ import org.hibernate.type.StandardBasicTypes; * @author Jonathan Bregler */ public class HANAColumnStoreDialect extends AbstractHANADialect { + public HANAColumnStoreDialect(DialectResolutionInfo info) { - this( info.makeCopy() ); + this( AbstractHANADialect.createVersion( info ) ); registerKeywords( info ); } public HANAColumnStoreDialect() { - this( DatabaseVersion.make( 3, 0 ) ); + // SAP HANA 1.0 SP12 is the default + this( DatabaseVersion.make( 1, 0, 120 ) ); } public HANAColumnStoreDialect(DatabaseVersion version) { @@ -172,17 +174,11 @@ public class HANAColumnStoreDialect extends AbstractHANADialect { @Override protected boolean supportsAsciiStringTypes() { - return getVersion().isBefore( 4 ); + return true; } @Override protected Boolean useUnicodeStringTypesDefault() { - return getVersion().isSameOrAfter( 4 ); - } - - @Override - public boolean isUseUnicodeStringTypes() { - return getVersion().isSameOrAfter( 4 ) - || super.isUseUnicodeStringTypes(); + return true; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java index 9b64aeb800..f0ce4a6277 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java @@ -32,13 +32,14 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; */ public class HANARowStoreDialect extends AbstractHANADialect { - public HANARowStoreDialect() { - super( DatabaseVersion.make( 3, 0 )); + public HANARowStoreDialect(DialectResolutionInfo info) { + this( AbstractHANADialect.createVersion( info ) ); + registerKeywords( info ); } - public HANARowStoreDialect(DialectResolutionInfo info) { - this( info.makeCopy() ); - registerKeywords( info ); + public HANARowStoreDialect() { + // SAP HANA 1.0 SPS12 R0 is the default + this( DatabaseVersion.make( 1, 0, 120 ) ); } public HANARowStoreDialect(DatabaseVersion version) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index e53cb44109..b75cf302f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -720,6 +720,11 @@ public class HSQLDialect extends Dialect { return true; } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 2, 6, 1 ); + } + @Override public boolean requiresFloatCastingOfIntegerDivision() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java index a743a7eb51..8745dfdc32 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java @@ -33,6 +33,8 @@ import org.hibernate.type.StandardBasicTypes; * @author Gavin King */ public class MariaDBDialect extends MySQLDialect { + private static final DatabaseVersion VERSION5 = DatabaseVersion.make( 5 ); + private static final DatabaseVersion VERSION57 = DatabaseVersion.make( 5, 7 ); public MariaDBDialect() { this( DatabaseVersion.make( 5 ) ); @@ -49,8 +51,8 @@ public class MariaDBDialect extends MySQLDialect { @Override public DatabaseVersion getMySQLVersion() { return getVersion().isBefore( 5, 3 ) - ? DatabaseVersion.make( 5 ) - : DatabaseVersion.make( 5, 7 ); + ? VERSION5 + : VERSION57; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java index 2005af0e92..2abd14d836 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -14,6 +14,7 @@ import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; @@ -71,6 +72,11 @@ public class MariaDBSqlAstTranslator extends AbstractSq } } + @Override + public void visitQueryPartTableReference(QueryPartTableReference tableReference) { + emulateQueryPartTableReferenceColumnAliasing( tableReference ); + } + @Override public void visitOffsetFetchClause(QueryPart queryPart) { if ( !isRowNumberingCurrentQueryPart() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index abe9e2f73f..b06183955c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -1179,6 +1179,11 @@ public class MySQLDialect extends Dialect { return getMySQLVersion().isSameOrAfter( 8, 2 ); } + @Override + public boolean supportsLateral() { + return getMySQLVersion().isSameOrAfter( 8, 14 ); + } + @Override public boolean supportsSkipLocked() { return getMySQLVersion().isSameOrAfter( 8 ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java index 165b848f88..2b54745d48 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java @@ -14,6 +14,8 @@ import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; +import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; @@ -72,6 +74,21 @@ public class MySQLSqlAstTranslator extends AbstractSqlA } } + @Override + public void visitValuesTableReference(ValuesTableReference tableReference) { + emulateValuesTableReferenceColumnAliasing( tableReference ); + } + + @Override + public void visitQueryPartTableReference(QueryPartTableReference tableReference) { + if ( getDialect().getVersion().isSameOrAfter( 8 ) ) { + super.visitQueryPartTableReference( tableReference ); + } + else { + emulateQueryPartTableReferenceColumnAliasing( tableReference ); + } + } + @Override public void visitOffsetFetchClause(QueryPart queryPart) { if ( !isRowNumberingCurrentQueryPart() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index afe3dca3e9..a2e2060e59 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -1061,6 +1061,11 @@ public class OracleDialect extends Dialect { return true; } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 12, 1 ); + } + @Override public boolean supportsNoWait() { return getVersion().isSameOrAfter( 9 ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java index c26a60058f..7878e1e7b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java @@ -24,6 +24,8 @@ import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Over; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.FunctionTableReference; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.UnionTableGroup; import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.insert.Values; @@ -248,48 +250,20 @@ public class OracleSqlAstTranslator extends AbstractSql @Override public void visitValuesTableReference(ValuesTableReference tableReference) { - final List valuesList = tableReference.getValuesList(); - if ( valuesList.size() < 2 ) { - super.visitValuesTableReference( tableReference ); - } - else { - append( '(' ); - // Oracle doesn't support a multi-values insert - // So we render a select union emulation instead - final Stack clauseStack = getClauseStack(); - clauseStack.push( Clause.VALUES ); - try { - // We render the first select statement with aliases - clauseStack.push( Clause.SELECT ); + emulateValuesTableReferenceColumnAliasing( tableReference ); + } - try { - appendSql( "select " ); + @Override + public void visitQueryPartTableReference(QueryPartTableReference tableReference) { + emulateQueryPartTableReferenceColumnAliasing( tableReference ); + } - renderCommaSeparatedSelectExpression( - valuesList.get( 0 ).getExpressions(), - tableReference.getColumnNames() - ); - appendSql( getFromDualForSelectOnly() ); - } - finally { - clauseStack.pop(); - } - // The others, without the aliases - for ( int i = 1; i < valuesList.size(); i++ ) { - appendSql( " union all " ); - renderExpressionsAsSubquery( valuesList.get( i ).getExpressions() ); - } - } - finally { - clauseStack.pop(); - } - append( ')' ); - final String identificationVariable = tableReference.getIdentificationVariable(); - if ( identificationVariable != null ) { - append( WHITESPACE ); - append( tableReference.getIdentificationVariable() ); - } - } + @Override + public void visitFunctionTableReference(FunctionTableReference tableReference) { + append( "table(" ); + tableReference.getFunctionExpression().accept( this ); + append( CLOSE_PARENTHESIS ); + renderTableReferenceIdentificationVariable( tableReference ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 6082d0bd66..6ec1d0d7ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -1006,6 +1006,11 @@ public class PostgreSQLDialect extends Dialect { return true; } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 9, 3 ); + } + @Override public boolean supportsFetchClause(FetchClauseType type) { switch ( type ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index d42bcf2721..554b22027b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -562,6 +562,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { return true; } + @Override + public boolean supportsLateral() { + return getVersion().isSameOrAfter( 9 ); + } + @Override public boolean supportsFetchClause(FetchClauseType type) { return getVersion().isSameOrAfter( 11 ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java index d25b6ba52e..e02e006b2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java @@ -24,9 +24,10 @@ import org.hibernate.sql.ast.tree.expression.Literal; 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.TableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference; -import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; @@ -44,6 +45,8 @@ public class SQLServerSqlAstTranslator extends Abstract private static final String UNION_ALL = " union all "; + private Predicate lateralPredicate; + public SQLServerSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { super( sessionFactory, statement ); } @@ -51,7 +54,7 @@ public class SQLServerSqlAstTranslator extends Abstract @Override protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List tableGroupJoinCollector) { appendSql( WHITESPACE ); - if ( tableGroupJoin.isLateral() ) { + if ( tableGroupJoin.getJoinedGroup().isLateral() ) { if ( tableGroupJoin.getJoinType() == SqlAstJoinType.LEFT ) { appendSql( "outer apply " ); } @@ -66,19 +69,31 @@ public class SQLServerSqlAstTranslator extends Abstract final Predicate predicate = tableGroupJoin.getPredicate(); if ( predicate != null && !predicate.isEmpty() ) { - if ( tableGroupJoin.isLateral() ) { - renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); - addAdditionalWherePredicate( predicate ); + if ( tableGroupJoin.getJoinedGroup().isLateral() ) { + // We have to inject the lateral predicate into the sub-query + final Predicate lateralPredicate = this.lateralPredicate; + this.lateralPredicate = predicate; + renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); + this.lateralPredicate = lateralPredicate; } else { renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); } } else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); + renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); } } + protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) { + final TableReference tableReference = tableGroup.getPrimaryTableReference(); + if ( tableReference instanceof NamedTableReference ) { + return renderNamedTableReference( (NamedTableReference) tableReference, lockMode ); + } + tableReference.accept( this ); + return false; + } + @Override protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) { final String tableExpression = tableReference.getTableExpression(); @@ -237,6 +252,11 @@ public class SQLServerSqlAstTranslator extends Abstract @Override public void visitQueryGroup(QueryGroup queryGroup) { + final Predicate lateralPredicate = this.lateralPredicate; + if ( lateralPredicate != null ) { + this.lateralPredicate = null; + addAdditionalWherePredicate( lateralPredicate ); + } if ( shouldEmulateFetchClause( queryGroup ) ) { emulateFetchOffsetWithWindowFunctions( queryGroup, !isRowsOnlyFetchClauseType( queryGroup ) ); } @@ -255,6 +275,15 @@ public class SQLServerSqlAstTranslator extends Abstract } } + @Override + public void visitSelectClause(SelectClause selectClause) { + if ( lateralPredicate != null ) { + addAdditionalWherePredicate( lateralPredicate ); + lateralPredicate = null; + } + super.visitSelectClause( selectClause ); + } + @Override protected boolean needsRowsToSkip() { return getDialect().getVersion().isBefore( 9 ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java index 64f72153f1..f6488b162a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java @@ -8,6 +8,7 @@ package org.hibernate.dialect; import java.util.List; +import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; import org.hibernate.sql.ast.Clause; @@ -19,9 +20,10 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; 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.Junction; -import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.ast.tree.from.DerivedTableReference; +import org.hibernate.sql.ast.tree.from.NamedTableReference; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; @@ -118,40 +120,24 @@ public class SpannerSqlAstTranslator extends AbstractSq } @Override - protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List tableGroupJoinCollector) { - appendSql( WHITESPACE ); - appendSql( tableGroupJoin.getJoinType().getText() ); - appendSql( "join " ); - - boolean correlated = false; - final Predicate predicate; - if ( tableGroupJoin.isLateral() ) { - correlated = true; - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new Junction( Junction.Nature.CONJUNCTION ); - } - else { - predicate = tableGroupJoin.getPredicate(); - } - } - else { - predicate = tableGroupJoin.getPredicate(); + protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) { + final TableReference tableReference = tableGroup.getPrimaryTableReference(); + if ( tableReference instanceof NamedTableReference ) { + return renderNamedTableReference( (NamedTableReference) tableReference, lockMode ); } + final DerivedTableReference derivedTableReference = (DerivedTableReference) tableReference; + final boolean correlated = derivedTableReference.isLateral(); final boolean oldCorrelated = this.correlated; if ( correlated ) { this.correlated = true; appendSql( "unnest(array" ); } - if ( predicate != null && !predicate.isEmpty() ) { - renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); - } - else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); - } + tableReference.accept( this ); if ( correlated ) { this.correlated = oldCorrelated; appendSql( CLOSE_PARENTHESIS ); } + return false; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java index 6d4d98c6ae..942b41e750 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java @@ -23,13 +23,14 @@ import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; 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.predicate.Junction; +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; @@ -124,13 +125,12 @@ public class SybaseASESqlAstTranslator extends Abstract } final Predicate predicate; - if ( tableGroupJoin.isLateral() ) { - append( "lateral " ); - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new Junction( Junction.Nature.CONJUNCTION ); + if ( tableGroupJoin.getPredicate() == null ) { + if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) { + predicate = null; } else { - predicate = tableGroupJoin.getPredicate(); + predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); } } else { @@ -140,7 +140,7 @@ public class SybaseASESqlAstTranslator extends Abstract renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); } else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); + renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); } } @@ -229,11 +229,16 @@ public class SybaseASESqlAstTranslator extends Abstract } } + @Override + protected boolean supportsDistinctFromPredicate() { + return getDialect().getVersion().isSameOrAfter( 16, 3 ); + } + @Override protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { // I think intersect is only supported in 16.0 SP3 if ( getDialect().isAnsiNullOn() ) { - if ( getDialect().getVersion().isSameOrAfter( 16, 3 ) ) { + if ( supportsDistinctFromPredicate() ) { renderComparisonEmulateIntersect( lhs, operator, rhs ); } else { @@ -282,7 +287,7 @@ public class SybaseASESqlAstTranslator extends Abstract } } else { - if ( getDialect().getVersion().isSameOrAfter( 16, 3 ) ) { + if ( supportsDistinctFromPredicate() ) { renderComparisonEmulateIntersect( lhs, operator, rhs ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java index b483c1c952..1e22f4bb10 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java @@ -28,6 +28,8 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; */ public class TiDBDialect extends MySQLDialect { + private static final DatabaseVersion VERSION57 = DatabaseVersion.make( 5, 7 ); + public TiDBDialect() { this( DatabaseVersion.make(5, 4) ); } @@ -45,7 +47,7 @@ public class TiDBDialect extends MySQLDialect { @Override public DatabaseVersion getMySQLVersion() { // For simplicity’s sake, configure MySQL 5.7 compatibility - return DatabaseVersion.make( 5, 7 ); + return VERSION57; } private void registerKeywords() { diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java index 7669fbe324..f30f22c7b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java @@ -6,15 +6,13 @@ */ package org.hibernate.query.results; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.query.NavigablePath; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.AbstractTableGroup; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; @@ -23,97 +21,26 @@ import org.hibernate.sql.ast.tree.from.TableReferenceJoin; * * @author Steve Ebersole */ -public class TableGroupImpl implements TableGroup { - private final NavigablePath navigablePath; - private final String alias; +public class TableGroupImpl extends AbstractTableGroup { private final TableReference primaryTableReference; - private List tableGroupJoins; - - private final ModelPartContainer container; - private final String sourceAlias; public TableGroupImpl( NavigablePath navigablePath, String alias, TableReference primaryTableReference, - ModelPartContainer container, - String sourceAlias) { - this.navigablePath = navigablePath; - this.alias = alias; + ModelPartContainer container) { + super( false, navigablePath, container, alias, null, null ); this.primaryTableReference = primaryTableReference; - this.container = container; - this.sourceAlias = sourceAlias; - } - - @Override - public NavigablePath getNavigablePath() { - return navigablePath; } @Override public String getGroupAlias() { - return alias; - } - - @Override - public ModelPartContainer getModelPart() { - return container; - } - - @Override - public ModelPartContainer getExpressionType() { - return getModelPart(); - } - - @Override - public String getSourceAlias() { - return sourceAlias; - } - - @Override - public List getTableGroupJoins() { - return tableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( tableGroupJoins ); - } - - @Override - public List getNestedTableGroupJoins() { - return Collections.emptyList(); - } - - @Override - public boolean canUseInnerJoins() { - return false; - } - - @Override - public void addTableGroupJoin(TableGroupJoin join) { - if ( tableGroupJoins == null ) { - tableGroupJoins = new ArrayList<>(); - } - assert !tableGroupJoins.contains( join ); - tableGroupJoins.add( join ); - } - - @Override - public void addNestedTableGroupJoin(TableGroupJoin join) { - addTableGroupJoin( join ); - } - - @Override - public void visitTableGroupJoins(Consumer consumer) { - if ( tableGroupJoins != null ) { - tableGroupJoins.forEach( consumer ); - } - } - - @Override - public void visitNestedTableGroupJoins(Consumer consumer) { + return getSourceAlias(); } @Override public void applyAffectedTableNames(Consumer nameCollector) { - } @Override @@ -127,20 +54,7 @@ public class TableGroupImpl implements TableGroup { } @Override - public TableReference resolveTableReference( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { - final TableReference tableReference = getTableReference( navigablePath, tableExpression, allowFkOptimization, true ); - if ( tableReference == null ) { - throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" ); - } - - return tableReference; - } - - @Override - public TableReference getTableReference( + public TableReference getTableReferenceInternal( NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization, @@ -148,15 +62,7 @@ public class TableGroupImpl implements TableGroup { if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization, resolve ) != null ) { return primaryTableReference; } - - for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { - final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); - if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { - return primaryTableReference; - } - } - - return null; + return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java index ac6ddffe31..c3ef2f8688 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java @@ -166,7 +166,7 @@ public class DynamicResultBuilderEntityStandard if ( lockMode != null ) { domainResultCreationState.getSqlAstCreationState().registerLockMode( tableAlias, lockMode ); } - return new TableGroupImpl( navigablePath, tableAlias, tableReference, entityMapping, tableAlias ); + return new TableGroupImpl( navigablePath, tableAlias, tableReference, entityMapping ); } ); final TableReference tableReference = tableGroup.getPrimaryTableReference(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java index afda4222f8..b44b8bc2a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java @@ -471,7 +471,6 @@ public class CteInsertHandler implements InsertHandler { false, factory ), - null, null ); final TableGroup rowsWithSequenceTableGroup = new CteTableGroup( @@ -868,7 +867,6 @@ public class CteInsertHandler implements InsertHandler { false, factory ), - null, null ); final TableGroup rootInsertCteTableGroup = new CteTableGroup( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java index fb0b8f328e..1d2f5a9b4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java @@ -273,8 +273,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio updatingTableGroup.getNavigablePath(), null, temporaryTableReference, - entityDescriptor, - null + entityDescriptor ); querySpec.getFromClause().addRoot( temporaryTableGroup ); final InsertStatement insertStatement = new InsertStatement( dmlTableReference ); @@ -607,8 +606,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio false, sessionFactory ), - entityDescriptor, - null + entityDescriptor ); querySpec.getFromClause().addRoot( temporaryTableGroup ); final InsertStatement insertStatement = new InsertStatement( dmlTargetTableReference ); 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 db2d8e6c98..a878e7eb49 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 @@ -92,6 +92,8 @@ import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource; import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPath; import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.query.FetchClauseType; +import org.hibernate.query.SortOrder; import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; @@ -108,6 +110,8 @@ import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression; import org.hibernate.sql.ast.tree.from.CorrelatedPluralTableGroup; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.PluralTableGroup; +import org.hibernate.sql.ast.tree.from.QueryPartTableGroup; +import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification; import org.hibernate.persister.collection.CollectionPersister; @@ -3221,22 +3225,22 @@ public abstract class BaseSqmToSqlAstConverter extends Base @Override public Expression visitMaxElementPath(SqmMaxElementPath path) { - return createCorrelatedAggregateSubQuery( path, false, true ); + return createMinOrMaxIndexOrElement( path, false, true ); } @Override public Expression visitMinElementPath(SqmMinElementPath path) { - return createCorrelatedAggregateSubQuery( path, false, false ); + return createMinOrMaxIndexOrElement( path, false, false ); } @Override public Expression visitMaxIndexPath(SqmMaxIndexPath path) { - return createCorrelatedAggregateSubQuery( path, true, true ); + return createMinOrMaxIndexOrElement( path, true, true ); } @Override public Expression visitMinIndexPath(SqmMinIndexPath path) { - return createCorrelatedAggregateSubQuery( path, true, false ); + return createMinOrMaxIndexOrElement( path, true, false ); } @Override @@ -3388,6 +3392,19 @@ public abstract class BaseSqmToSqlAstConverter extends Base }; } + protected Expression createMinOrMaxIndexOrElement( + AbstractSqmSpecificPluralPartPath pluralPartPath, + boolean index, + boolean max) { + // Try to create a lateral sub-query join if possible which allows the re-use of the expression + if ( creationContext.getSessionFactory().getJdbcServices().getDialect().supportsLateral() ) { + return createLateralJoinExpression( pluralPartPath, index, max ); + } + else { + return createCorrelatedAggregateSubQuery( pluralPartPath, index, max ); + } + } + protected Expression createCorrelatedAggregateSubQuery( AbstractSqmSpecificPluralPartPath pluralPartPath, boolean index, @@ -3497,6 +3514,219 @@ public abstract class BaseSqmToSqlAstConverter extends Base return subQuerySpec; } + protected Expression createLateralJoinExpression( + AbstractSqmSpecificPluralPartPath pluralPartPath, + boolean index, + boolean max) { + prepareReusablePath( pluralPartPath.getLhs(), () -> null ); + + final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping( + pluralPartPath.getPluralDomainPath() ); + final FromClauseAccess parentFromClauseAccess = getFromClauseAccess(); + final TableGroup parentTableGroup = parentFromClauseAccess.findTableGroup( + pluralPartPath.getNavigablePath().getParent() + ); + final CollectionPart collectionPart = index + ? pluralAttributeMapping.getIndexDescriptor() + : pluralAttributeMapping.getElementDescriptor(); + final ModelPart modelPart; + if ( collectionPart instanceof EntityAssociationMapping ) { + modelPart = ( (EntityAssociationMapping) collectionPart ).getKeyTargetMatchPart(); + } + else { + modelPart = collectionPart; + } + final int jdbcTypeCount = modelPart.getJdbcTypeCount(); + final String pathName = ( max ? "max" : "min" ) + ( index ? "_index" : "_element" ); + final String identifierVariable = parentTableGroup.getPrimaryTableReference().getIdentificationVariable() + + "_" + pathName; + final NavigablePath queryPath = new NavigablePath( parentTableGroup.getNavigablePath(), pathName, identifierVariable ); + TableGroup lateralTableGroup = parentFromClauseAccess.findTableGroup( queryPath ); + if ( lateralTableGroup == null ) { + final QuerySpec subQuerySpec = new QuerySpec( false ); + pushProcessingState( + new SqlAstQueryPartProcessingStateImpl( + subQuerySpec, + getCurrentProcessingState(), + this, + currentClauseStack::getCurrent + ) + ); + try { + final TableGroup tableGroup = pluralAttributeMapping.createRootTableGroup( + true, + pluralPartPath.getNavigablePath(), + null, + () -> subQuerySpec::applyPredicate, + this, + creationContext + ); + final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate( + getLoadQueryInfluencers(), + (Joinable) pluralAttributeMapping.getCollectionDescriptor(), + tableGroup + ); + if ( filterPredicate != null ) { + subQuerySpec.applyPredicate( filterPredicate ); + } + + getFromClauseAccess().registerTableGroup( pluralPartPath.getNavigablePath(), tableGroup ); + registerPluralTableGroupParts( tableGroup ); + subQuerySpec.getFromClause().addRoot( tableGroup ); + + final List columnNames = new ArrayList<>( jdbcTypeCount ); + final List resultColumnReferences = new ArrayList<>( jdbcTypeCount ); + final NavigablePath navigablePath = pluralPartPath.getNavigablePath(); + modelPart.forEachSelectable( + (selectionIndex, selectionMapping) -> { + final ColumnReference columnReference = new ColumnReference( + tableGroup.resolveTableReference( + navigablePath, + selectionMapping.getContainingTableExpression() + ), + selectionMapping, + creationContext.getSessionFactory() + ); + final String columnName; + if ( selectionMapping.isFormula() ) { + columnName = "col" + columnNames.size(); + } + else { + columnName = selectionMapping.getSelectionExpression(); + } + columnNames.add( columnName ); + subQuerySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + selectionIndex - 1, + selectionIndex, + columnReference + ) + ); + subQuerySpec.addSortSpecification( + new SortSpecification( + columnReference, + null, + max ? SortOrder.DESCENDING : SortOrder.ASCENDING + ) + ); + resultColumnReferences.add( + new ColumnReference( + identifierVariable, + columnName, + false, + null, + null, + selectionMapping.getJdbcMapping(), + creationContext.getSessionFactory() + ) + ); + } + ); + + subQuerySpec.setFetchClauseExpression( + new QueryLiteral<>( 1, basicType( Integer.class ) ), + FetchClauseType.ROWS_ONLY + ); + subQuerySpec.applyPredicate( + pluralAttributeMapping.getKeyDescriptor().generateJoinPredicate( + parentFromClauseAccess.findTableGroup( + pluralPartPath.getPluralDomainPath().getNavigablePath().getParent() + ), + tableGroup, + SqlAstJoinType.INNER, + getSqlExpressionResolver(), + creationContext + ) + ); + lateralTableGroup = new QueryPartTableGroup( + queryPath, + null, + subQuerySpec, + identifierVariable, + columnNames, + true, + false, + creationContext.getSessionFactory() + ); + if ( currentlyProcessingJoin == null ) { + parentTableGroup.addTableGroupJoin( + new TableGroupJoin( + lateralTableGroup.getNavigablePath(), + SqlAstJoinType.LEFT, + lateralTableGroup + ) + ); + } + else { + // In case this is used in the ON condition, we must prepend this lateral join + final TableGroup targetTableGroup; + if ( currentlyProcessingJoin.getLhs() == null ) { + targetTableGroup = parentFromClauseAccess.getTableGroup( + currentlyProcessingJoin.findRoot().getNavigablePath() + ); + } + else { + targetTableGroup = parentFromClauseAccess.getTableGroup( + currentlyProcessingJoin.getLhs().getNavigablePath() + ); + } + // Many databases would support modelling this as nested table group join, + // but at least SQL Server doesn't like that, saying that the correlated columns can't be "bound" + // Since there is no dependency on the currentlyProcessingJoin, we can safely prepend this join + targetTableGroup.prependTableGroupJoin( + currentlyProcessingJoin.getNavigablePath(), + new TableGroupJoin( + lateralTableGroup.getNavigablePath(), + SqlAstJoinType.LEFT, + lateralTableGroup + ) + ); + } + parentFromClauseAccess.registerTableGroup( lateralTableGroup.getNavigablePath(), lateralTableGroup ); + if ( jdbcTypeCount == 1 ) { + return resultColumnReferences.get( 0 ); + } + else { + return new SqlTuple( resultColumnReferences, modelPart ); + } + } + finally { + popProcessingStateStack(); + } + } + final QueryPartTableReference tableReference = (QueryPartTableReference) lateralTableGroup.getPrimaryTableReference(); + if ( jdbcTypeCount == 1 ) { + return new ColumnReference( + identifierVariable, + tableReference.getColumnNames().get( 0 ), + false, + null, + null, + modelPart.getJdbcMappings().get( 0 ), + creationContext.getSessionFactory() + ); + } + else { + final List resultColumnReferences = new ArrayList<>( jdbcTypeCount ); + modelPart.forEachSelectable( + (selectionIndex, selectionMapping) -> { + resultColumnReferences.add( + new ColumnReference( + identifierVariable, + tableReference.getColumnNames().get( selectionIndex ), + false, + null, + null, + selectionMapping.getJdbcMapping(), + creationContext.getSessionFactory() + ) + ); + } + ); + return new SqlTuple( resultColumnReferences, modelPart ); + } + } + private Expression withTreatRestriction(Expression expression, SqmPath path) { final SqmPath lhs = path.getLhs(); if ( lhs instanceof SqmTreatedPath ) { 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 06c848d602..bb2647a531 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 @@ -35,6 +35,7 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.internal.SqlFragmentPredicate; import org.hibernate.query.IllegalQueryOperationException; +import org.hibernate.query.SetOperator; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind; @@ -114,9 +115,11 @@ import org.hibernate.sql.ast.tree.from.FromClause; import org.hibernate.sql.ast.tree.from.FunctionTableReference; import org.hibernate.sql.ast.tree.from.LazyTableGroup; import org.hibernate.sql.ast.tree.from.NamedTableReference; +import org.hibernate.sql.ast.tree.from.QueryPartTableGroup; import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.ValuesTableReference; @@ -194,6 +197,8 @@ public abstract class AbstractSqlAstTranslator implemen private final Set affectedTableNames = new HashSet<>(); private MutationStatement dmlStatement; private boolean needsSelectAliases; + // Column aliases that need to be injected + private List columnAliases; private Predicate additionalWherePredicate; // We must reset the queryPartForRowNumbering fields to null if a query part is visited that does not // contribute to the row numbering i.e. if the query part is a sub-query in the where clause. @@ -206,6 +211,7 @@ public abstract class AbstractSqlAstTranslator implemen private transient AbstractSqmSelfRenderingFunctionDescriptor castFunction; private transient LazySessionWrapperOptions lazySessionWrapperOptions; private transient BasicType integerType; + private transient BasicType booleanType; private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT; @@ -258,6 +264,15 @@ public abstract class AbstractSqlAstTranslator implemen return integerType; } + public BasicType getBooleanType() { + if ( booleanType == null ) { + booleanType = sessionFactory.getTypeConfiguration() + .getBasicTypeRegistry() + .resolve( StandardBasicTypes.BOOLEAN ); + } + return booleanType; + } + /** * A lazy session implementation that is needed for rendering literals. * Usually, only the {@link WrapperOptions} interface is needed, @@ -1418,9 +1433,13 @@ public abstract class AbstractSqlAstTranslator implemen this.queryPartForRowNumberingClauseDepth = -1; this.needsSelectAliases = false; } + final boolean needsParenthesis = !queryGroup.isRoot(); + if ( needsParenthesis ) { + appendSql( OPEN_PARENTHESIS ); + } // 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 - if ( queryPartForRowNumbering == queryGroup ) { + if ( queryPartForRowNumbering == queryGroup || additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) { this.needsSelectAliases = true; queryGroupAlias = "grp_" + queryGroupAliasCounter + '_'; queryGroupAliasCounter++; @@ -1470,6 +1489,12 @@ public abstract class AbstractSqlAstTranslator implemen if ( queryGroupAlias != null ) { appendSql( ") " ); appendSql( queryGroupAlias ); + if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) { + visitWhereClause( additionalWherePredicate ); + } + } + if ( needsParenthesis ) { + appendSql( CLOSE_PARENTHESIS ); } } finally { @@ -1504,7 +1529,8 @@ public abstract class AbstractSqlAstTranslator implemen 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 ( needsParenthesis = querySpec.hasOffsetOrFetchClause() ) { + needsParenthesis = querySpec.hasOffsetOrFetchClause(); + if ( needsParenthesis ) { // If the parent is a query group with a fetch clause, // or if the database does not support simple query grouping, we must use a select wrapper if ( !supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() ) { @@ -2849,7 +2875,7 @@ public abstract class AbstractSqlAstTranslator implemen if ( !needsParenthesis || queryPart.isRoot() ) { appendSql( CLOSE_PARENTHESIS ); } - appendSql( WHITESPACE); + appendSql( WHITESPACE ); appendSql( alias ); appendSql( " where " ); final Stack clauseStack = getClauseStack(); @@ -2905,6 +2931,12 @@ public abstract class AbstractSqlAstTranslator implemen } // todo: not sure if databases handle order by row number or the original ordering better.. if ( offsetExpression == null ) { + final Predicate additionalWherePredicate = this.additionalWherePredicate; + if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) { + this.additionalWherePredicate = null; + appendSql( " and " ); + additionalWherePredicate.accept( this ); + } if ( queryPart.isRoot() ) { switch ( fetchClauseType ) { case PERCENT_ONLY: @@ -2929,6 +2961,12 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( alias ); appendSql( ".rn>" ); offsetExpression.accept( this ); + final Predicate additionalWherePredicate = this.additionalWherePredicate; + if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) { + this.additionalWherePredicate = null; + appendSql( " and " ); + additionalWherePredicate.accept( this ); + } if ( queryPart.isRoot() ) { appendSql( " order by " ); appendSql( alias ); @@ -3010,6 +3048,46 @@ public abstract class AbstractSqlAstTranslator implemen } final SqlAstNodeRenderingMode original = parameterRenderingMode; if ( needsSelectAliases || referenceStrategy == SelectItemReferenceStrategy.ALIAS && hasSelectAliasInGroupByClause() ) { + String separator = NO_SEPARATOR; + if ( columnAliases == null ) { + for ( int i = 0; i < size; i++ ) { + final SqlSelection sqlSelection = sqlSelections.get( i ); + appendSql( separator ); + if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { + parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; + } + else { + parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER; + } + visitSqlSelection( sqlSelection ); + parameterRenderingMode = original; + appendSql( " c" ); + appendSql( i ); + separator = COMA_SEPARATOR; + } + } + else { + for ( int i = 0; i < size; i++ ) { + final SqlSelection sqlSelection = sqlSelections.get( i ); + appendSql( separator ); + if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { + parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; + } + else { + parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER; + } + visitSqlSelection( sqlSelection ); + parameterRenderingMode = original; + appendSql( WHITESPACE ); + appendSql( columnAliases.get( i ) ); + separator = COMA_SEPARATOR; + } + } + if ( queryPartForRowNumbering != null ) { + renderRowNumberingSelectItems( selectClause, queryPartForRowNumbering ); + } + } + else if ( columnAliases == null ) { String separator = NO_SEPARATOR; for ( int i = 0; i < size; i++ ) { final SqlSelection sqlSelection = sqlSelections.get( i ); @@ -3022,13 +3100,8 @@ public abstract class AbstractSqlAstTranslator implemen } visitSqlSelection( sqlSelection ); parameterRenderingMode = original; - appendSql( " c" ); - appendSql( i ); separator = COMA_SEPARATOR; } - if ( queryPartForRowNumbering != null ) { - renderRowNumberingSelectItems( selectClause, queryPartForRowNumbering ); - } } else { String separator = NO_SEPARATOR; @@ -3042,6 +3115,8 @@ public abstract class AbstractSqlAstTranslator implemen parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER; } visitSqlSelection( sqlSelection ); + appendSql( WHITESPACE ); + appendSql( columnAliases.get( i ) ); parameterRenderingMode = original; separator = COMA_SEPARATOR; } @@ -3486,7 +3561,7 @@ public abstract class AbstractSqlAstTranslator implemen String separator = NO_SEPARATOR; for ( TableGroup root : fromClause.getRoots() ) { appendSql( separator ); - renderTableGroup( root, null ); + renderRootTableGroup( root, null ); separator = COMA_SEPARATOR; } } @@ -3497,9 +3572,12 @@ public abstract class AbstractSqlAstTranslator implemen } @SuppressWarnings("WeakerAccess") - protected void renderTableGroup(TableGroup tableGroup, List tableGroupJoinCollector) { + protected void renderRootTableGroup(TableGroup tableGroup, List tableGroupJoinCollector) { final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() ); - final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode ); + final boolean usesLockHint = renderPrimaryTableReference( tableGroup, effectiveLockMode ); + if ( tableGroup.isLateral() && !getDialect().supportsLateral() ) { + addAdditionalWherePredicate( determineLateralEmulationPredicate( tableGroup ) ); + } renderTableReferenceJoins( tableGroup ); processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector ); @@ -3537,7 +3615,7 @@ public abstract class AbstractSqlAstTranslator implemen } final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() ); - final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode ); + final boolean usesLockHint = renderPrimaryTableReference( tableGroup, effectiveLockMode ); final List tableGroupJoins; if ( realTableGroup ) { @@ -3559,8 +3637,22 @@ public abstract class AbstractSqlAstTranslator implemen tableGroupJoins = null; } - appendSql( " on " ); - predicate.accept( this ); + if ( predicate != null ) { + appendSql( " on " ); + predicate.accept( this ); + } + if ( tableGroup.isLateral() && !getDialect().supportsLateral() ) { + final Predicate lateralEmulationPredicate = determineLateralEmulationPredicate( tableGroup ); + if ( lateralEmulationPredicate != null ) { + if ( predicate == null ) { + appendSql( " on " ); + } + else { + appendSql( " and " ); + } + lateralEmulationPredicate.accept( this ); + } + } if ( !realTableGroup ) { renderTableReferenceJoins( tableGroup ); @@ -3619,10 +3711,30 @@ public abstract class AbstractSqlAstTranslator implemen return false; } - protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) { + protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) { + final TableReference tableReference = tableGroup.getPrimaryTableReference(); if ( tableReference instanceof NamedTableReference ) { return renderNamedTableReference( (NamedTableReference) tableReference, lockMode ); } + final DerivedTableReference derivedTableReference = (DerivedTableReference) tableReference; + if ( derivedTableReference.isLateral() ) { + if ( getDialect().supportsLateral() ) { + appendSql( "lateral" ); + } + else if ( tableReference instanceof QueryPartTableReference ) { + final QueryPartTableReference queryPartTableReference = (QueryPartTableReference) tableReference; + final QueryPart emulationQueryPart = stripToSelectClause( queryPartTableReference.getQueryPart() ); + final QueryPartTableReference emulationTableReference = new QueryPartTableReference( + emulationQueryPart, + tableReference.getIdentificationVariable(), + queryPartTableReference.getColumnNames(), + false, + sessionFactory + ); + emulationTableReference.accept( this ); + return false; + } + } tableReference.accept( this ); return false; } @@ -3633,11 +3745,7 @@ public abstract class AbstractSqlAstTranslator implemen registerAffectedTable( tableReference ); final Clause currentClause = clauseStack.getCurrent(); if ( rendersTableReferenceAlias( currentClause ) ) { - final String identificationVariable = tableReference.getIdentificationVariable(); - if ( identificationVariable != null ) { - appendSql( WHITESPACE ); - appendSql( identificationVariable ); - } + renderTableReferenceIdentificationVariable( tableReference ); } return false; } @@ -3662,6 +3770,48 @@ public abstract class AbstractSqlAstTranslator implemen renderDerivedTableReference( tableReference ); } + protected void emulateQueryPartTableReferenceColumnAliasing(QueryPartTableReference tableReference) { + final List columnAliases = this.columnAliases; + this.columnAliases = tableReference.getColumnNames(); + tableReference.getQueryPart().accept( this ); + this.columnAliases = columnAliases; + renderTableReferenceIdentificationVariable( tableReference ); + } + + protected void emulateValuesTableReferenceColumnAliasing(ValuesTableReference tableReference) { + final List valuesList = tableReference.getValuesList(); + append( '(' ); + final Stack clauseStack = getClauseStack(); + clauseStack.push( Clause.VALUES ); + try { + // We render the first select statement with aliases + clauseStack.push( Clause.SELECT ); + + try { + appendSql( "select " ); + + renderCommaSeparatedSelectExpression( + valuesList.get( 0 ).getExpressions(), + tableReference.getColumnNames() + ); + appendSql( getFromDualForSelectOnly() ); + } + finally { + clauseStack.pop(); + } + // The others, without the aliases + for ( int i = 1; i < valuesList.size(); i++ ) { + appendSql( " union all " ); + renderExpressionsAsSubquery( valuesList.get( i ).getExpressions() ); + } + } + finally { + clauseStack.pop(); + } + append( ')' ); + renderTableReferenceIdentificationVariable( tableReference ); + } + protected void renderDerivedTableReference(DerivedTableReference tableReference) { final String identificationVariable = tableReference.getIdentificationVariable(); if ( identificationVariable != null ) { @@ -3678,6 +3828,14 @@ public abstract class AbstractSqlAstTranslator implemen } } + protected final void renderTableReferenceIdentificationVariable(TableReference tableReference) { + final String identificationVariable = tableReference.getIdentificationVariable(); + if ( identificationVariable != null ) { + append( WHITESPACE ); + append( tableReference.getIdentificationVariable() ); + } + } + public static boolean rendersTableReferenceAlias(Clause clause) { // todo (6.0) : For now we just skip the alias rendering in the delete and update clauses // We need some dialect support if we want to support joins in delete and update statements @@ -3762,13 +3920,12 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( "join " ); final Predicate predicate; - if ( tableGroupJoin.isLateral() ) { - append( "lateral " ); - if ( tableGroupJoin.getPredicate() == null ) { - predicate = new Junction( Junction.Nature.CONJUNCTION ); + if ( tableGroupJoin.getPredicate() == null ) { + if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) { + predicate = null; } else { - predicate = tableGroupJoin.getPredicate(); + predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) ); } } else { @@ -3778,10 +3935,158 @@ public abstract class AbstractSqlAstTranslator implemen renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); } else { - renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); + renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector ); } } + protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) { + if ( tableGroup.getPrimaryTableReference() instanceof QueryPartTableReference ) { + final QueryPartTableReference tableReference = (QueryPartTableReference) tableGroup.getPrimaryTableReference(); + final List columnNames = tableReference.getColumnNames(); + final List columnReferences = new ArrayList<>( columnNames.size() ); + final List subColumnReferences = new ArrayList<>( columnNames.size() ); + final QueryPart queryPart = tableReference.getQueryPart(); + for ( String columnName : columnNames ) { + columnReferences.add( + new ColumnReference( + tableReference, + columnName, + false, + null, + null, + null, + sessionFactory + ) + ); + } + + // The following optimization only makes sense if the necessary features are supported natively + if ( ( columnReferences.size() == 1 || supportsRowValueConstructorSyntax() ) + && supportsDistinctFromPredicate() ) { + // Special case for limit 1 sub-queries to avoid double nested sub-query + // ... x(c) on x.c is not distinct from (... fetch first 1 rows only) + if ( queryPart.getFetchClauseType() == FetchClauseType.ROWS_ONLY + && queryPart.getFetchClauseExpression() instanceof QueryLiteral + && Integer.valueOf( 1 ) + .equals( ( (QueryLiteral) queryPart.getFetchClauseExpression() ).getLiteralValue() ) ) { + return new ComparisonPredicate( + new SqlTuple( columnReferences, tableGroup.getModelPart() ), + ComparisonOperator.NOT_DISTINCT_FROM, + queryPart + ); + } + } + + // Render with exists intersect sub-query if possible as that is shorter and more efficient + // ... x(c) on exists(select x.c intersect ...) + if ( supportsIntersect() ) { + final QuerySpec lhsReferencesQuery = new QuerySpec( false ); + for ( ColumnReference columnReference : columnReferences ) { + lhsReferencesQuery.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + 1, + 0, + columnReference + ) + ); + } + final List queryParts = new ArrayList<>( 2 ); + queryParts.add( lhsReferencesQuery ); + queryParts.add( queryPart ); + return new ExistsPredicate( + new QueryGroup( false, SetOperator.INTERSECT, queryParts ), + false, + getBooleanType() + ); + } + + // Double nested sub-query rendering if nothing else works + // We try to avoid this as much as possible as it is not very efficient and some DBs don't like it + // when a correlation happens in a sub-query that is not a direct child + // ... x(c) on exists(select 1 from (...) synth_(c) where x.c = synth_.c) + final QueryPartTableGroup subTableGroup = new QueryPartTableGroup( + tableGroup.getNavigablePath(), + (TableGroupProducer) tableGroup.getModelPart(), + queryPart, + "synth_", + columnNames, + false, + true, + sessionFactory + ); + for ( String columnName : columnNames ) { + subColumnReferences.add( + new ColumnReference( + subTableGroup.getPrimaryTableReference(), + columnName, + false, + null, + null, + null, + sessionFactory + ) + ); + } + final QuerySpec existsQuery = new QuerySpec( false, 1 ); + existsQuery.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + 1, + 0, + new QueryLiteral<>( 1, getIntegerType() ) + ) + ); + existsQuery.getFromClause().addRoot( subTableGroup ); + existsQuery.applyPredicate( + new ComparisonPredicate( + new SqlTuple( columnReferences, tableGroup.getModelPart() ), + ComparisonOperator.NOT_DISTINCT_FROM, + new SqlTuple( subColumnReferences, tableGroup.getModelPart() ) + ) + ); + + return new ExistsPredicate( existsQuery, false, getBooleanType() ); + } + return null; + } + + private QueryPart stripToSelectClause(QueryPart queryPart) { + if ( queryPart instanceof QueryGroup ) { + return stripToSelectClause( (QueryGroup) queryPart ); + } + else { + return stripToSelectClause( (QuerySpec) queryPart ); + } + } + + private QueryGroup stripToSelectClause(QueryGroup queryGroup) { + final List parts = new ArrayList<>( queryGroup.getQueryParts().size() ); + 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() ) { + throw new UnsupportedOperationException( "Can't emulate lateral join for query spec with group by clause" ); + } + if ( querySpec.getHavingClauseRestrictions() != null && !querySpec.getHavingClauseRestrictions().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() ) { + newQuerySpec.getFromClause().addRoot( root ); + } + for ( SqlSelection selection : querySpec.getSelectClause().getSqlSelections() ) { + if ( AggregateFunctionChecker.hasAggregateFunctions( selection.getExpression() ) ) { + throw new UnsupportedOperationException( "Can't emulate lateral join for query spec with aggregate function" ); + } + newQuerySpec.getSelectClause().addSqlSelection( selection ); + } + return newQuerySpec; + } + @Override public void visitTableGroup(TableGroup tableGroup) { // TableGroup and TableGroup handling should be performed as part of `#visitFromClause`... @@ -4555,7 +4860,8 @@ public abstract class AbstractSqlAstTranslator implemen SubQueryRelationalRestrictionEmulationRenderer renderer, ComparisonOperator tupleComparisonOperator) { final QuerySpec subQuery; - if ( queryPart instanceof QuerySpec && queryPart.getFetchClauseExpression() == null && queryPart.getOffsetClauseExpression() == null ) { + if ( queryPart instanceof QuerySpec && queryPart.getFetchClauseExpression() == null + && queryPart.getOffsetClauseExpression() == null ) { subQuery = (QuerySpec) queryPart; // We can only emulate the tuple sub query predicate as exists predicate when there are no limit/offsets if ( negated ) { @@ -4573,8 +4879,8 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( "exists (select 1" ); visitFromClause( subQuery.getFromClause() ); - if ( !subQuery.getGroupByClauseExpressions() - .isEmpty() || subQuery.getHavingClauseRestrictions() != null ) { + if ( !subQuery.getGroupByClauseExpressions().isEmpty() + || subQuery.getHavingClauseRestrictions() != null ) { // If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause visitWhereClause( subQuery.getWhereClauseRestrictions() ); visitGroupByClause( subQuery, SelectItemReferenceStrategy.EXPRESSION ); @@ -5014,6 +5320,18 @@ public abstract class AbstractSqlAstTranslator implemen return true; } + /** + * Is this SQL dialect known to support some kind of distinct from predicate. + *

+ * Basically, does it support syntax like + * "... where FIRST_NAME IS DISTINCT FROM LAST_NAME" + * + * @return True if this SQL dialect is known to support some kind of distinct from predicate; false otherwise + */ + protected boolean supportsDistinctFromPredicate() { + 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/sql/ast/spi/AggregateFunctionChecker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AggregateFunctionChecker.java index e6d216a607..e3966e1fa8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AggregateFunctionChecker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AggregateFunctionChecker.java @@ -65,6 +65,16 @@ public class AggregateFunctionChecker extends AbstractSqlAstWalker { private static class AggregateFunctionException extends RuntimeException {} + public static boolean hasAggregateFunctions(Expression expression) { + try { + expression.accept( INSTANCE ); + return false; + } + catch (AggregateFunctionException ex) { + return true; + } + } + public static boolean hasAggregateFunctions(QuerySpec querySpec) { try { querySpec.getSelectClause().accept( INSTANCE ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java index 013c5d62f7..00420e0a13 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java @@ -10,12 +10,10 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.from.AbstractTableGroup; 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.TableReference; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; @@ -25,92 +23,29 @@ import org.hibernate.sql.ast.tree.from.TableReferenceJoin; * * @author Steve Ebersole */ -public class CteTableGroup implements TableGroup { - private final NavigablePath navigablePath; +public class CteTableGroup extends AbstractTableGroup { private final NamedTableReference cteTableReference; - @SuppressWarnings("WeakerAccess") public CteTableGroup(NamedTableReference cteTableReference) { - this.navigablePath = new NavigablePath( cteTableReference.getTableExpression() ); + this( false, cteTableReference ); + } + + @SuppressWarnings("WeakerAccess") + public CteTableGroup(boolean canUseInnerJoins, NamedTableReference cteTableReference) { + super( + canUseInnerJoins, + new NavigablePath( cteTableReference.getTableExpression() ), + null, + cteTableReference.getIdentificationVariable(), + null, + null + ); this.cteTableReference = cteTableReference; } - @Override - public NavigablePath getNavigablePath() { - return navigablePath; - } - - @Override - public String getSourceAlias() { - return null; - } - - @Override - public ModelPartContainer getModelPart() { - return null; - } - - @Override - public ModelPart getExpressionType() { - return getModelPart(); - } - - @Override - public List getTableGroupJoins() { - return Collections.emptyList(); - } - - @Override - public List getNestedTableGroupJoins() { - return Collections.emptyList(); - } - - @Override - public boolean canUseInnerJoins() { - return false; - } - - @Override - public TableReference getTableReference( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization, - boolean resolve) { - if ( cteTableReference.getTableExpression().equals( tableExpression ) ) { - return cteTableReference; - } - return null; - } - - @Override - public TableReference resolveTableReference( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { - return cteTableReference; - } - - @Override - public void visitTableGroupJoins(Consumer consumer) { - } - - @Override - public void visitNestedTableGroupJoins(Consumer consumer) { - } - @Override public String getGroupAlias() { - return null; - } - - @Override - public void addTableGroupJoin(TableGroupJoin join) { - throw new UnsupportedOperationException( ); - } - - @Override - public void addNestedTableGroupJoin(TableGroupJoin join) { - throw new UnsupportedOperationException( ); + return cteTableReference.getIdentificationVariable(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java index 5c26f989b5..57ef0ec9e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java @@ -9,10 +9,12 @@ package org.hibernate.sql.ast.tree.from; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.NoSuchElementException; import java.util.function.Consumer; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAliasBase; @@ -22,7 +24,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase; public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifier implements TableGroup { private final boolean canUseInnerJoins; private final NavigablePath navigablePath; - private final TableGroupProducer producer; + private final ModelPartContainer modelPartContainer; private final String sourceAlias; private final SqlAliasBase sqlAliasBase; @@ -35,14 +37,14 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie public AbstractTableGroup( boolean canUseInnerJoins, NavigablePath navigablePath, - TableGroupProducer producer, + ModelPartContainer modelPartContainer, String sourceAlias, SqlAliasBase sqlAliasBase, SessionFactoryImplementor sessionFactory) { super(); this.canUseInnerJoins = canUseInnerJoins; this.navigablePath = navigablePath; - this.producer = producer; + this.modelPartContainer = modelPartContainer; this.sourceAlias = sourceAlias; this.sqlAliasBase = sqlAliasBase; this.sessionFactory = sessionFactory; @@ -59,12 +61,12 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie @Override public String getGroupAlias() { - return sqlAliasBase.getAliasStem(); + return sqlAliasBase == null ? null : sqlAliasBase.getAliasStem(); } @Override - public TableGroupProducer getModelPart() { - return producer; + public ModelPartContainer getModelPart() { + return modelPartContainer; } @Override @@ -111,6 +113,29 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie tableGroupJoins.add( join ); } + @Override + public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) { + int i = 0; + if ( tableGroupJoins != null ) { + for ( ; i < tableGroupJoins.size(); i++ ) { + if ( tableGroupJoins.get( i ).getNavigablePath() == navigablePath ) { + tableGroupJoins.add( i, join ); + return; + } + } + } + i = 0; + if ( nestedTableGroupJoins != null ) { + for ( ; i < nestedTableGroupJoins.size(); i++ ) { + if ( nestedTableGroupJoins.get( i ).getNavigablePath() == navigablePath ) { + nestedTableGroupJoins.add( i, join ); + return; + } + } + } + throw new NoSuchElementException("Table group for navigable path not found: " + navigablePath); + } + @Override public void addNestedTableGroupJoin(TableGroupJoin join) { if ( nestedTableGroupJoins == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java index ecb90dd3f8..eb7c0a500a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java @@ -56,6 +56,11 @@ public class CorrelatedTableGroup extends AbstractTableGroup { super.addTableGroupJoin( join ); } + @Override + public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) { + throw new UnsupportedOperationException(); + } + @Override public void addNestedTableGroupJoin(TableGroupJoin join) { assert !getTableGroupJoins().contains( join ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java new file mode 100644 index 0000000000..3adc3aee0f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java @@ -0,0 +1,201 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.ModelPartContainer; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.SqlAstWalker; +import org.hibernate.sql.ast.spi.SqlSelection; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * @author Christian Beikov + */ +public abstract class DelegatingTableGroup implements TableGroup { + + protected abstract TableGroup getTableGroup(); + + @Override + public ModelPart getExpressionType() { + return getTableGroup().getExpressionType(); + } + + @Override + public Expression getSqlExpression() { + return getTableGroup().getSqlExpression(); + } + + @Override + public T unwrap(Class target) { + return getTableGroup().unwrap( target ); + } + + @Override + public SqlSelection createSqlSelection( + int jdbcPosition, + int valuesArrayPosition, + JavaType javaTypeDescriptor, + TypeConfiguration typeConfiguration) { + return getTableGroup().createSqlSelection( + jdbcPosition, + valuesArrayPosition, + javaTypeDescriptor, + typeConfiguration + ); + } + + @Override + public TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression) { + return resolveTableReference( navigablePath, tableExpression, true ); + } + + @Override + public TableReference resolveTableReference(String tableExpression) { + return resolveTableReference( null, tableExpression, true ); + } + + @Override + public TableReference resolveTableReference( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization) { + return getTableGroup().resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); + } + + @Override + public TableReference getTableReference(NavigablePath navigablePath, String tableExpression) { + return getTableReference( navigablePath, tableExpression, true, false ); + } + + @Override + public TableReference getTableReference(String tableExpression) { + return getTableReference( null, tableExpression, true, false ); + } + + @Override + public TableReference getTableReference( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization, boolean resolve) { + return getTableGroup().getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + } + + @Override + public NavigablePath getNavigablePath() { + return getTableGroup().getNavigablePath(); + } + + @Override + public String getGroupAlias() { + return getTableGroup().getGroupAlias(); + } + + @Override + public ModelPartContainer getModelPart() { + return getTableGroup().getModelPart(); + } + + @Override + public String getSourceAlias() { + return getTableGroup().getSourceAlias(); + } + + @Override + public List getTableGroupJoins() { + return getTableGroup().getTableGroupJoins(); + } + + @Override + public List getNestedTableGroupJoins() { + return getTableGroup().getNestedTableGroupJoins(); + } + + @Override + public boolean canUseInnerJoins() { + return getTableGroup().canUseInnerJoins(); + } + + @Override + public boolean isLateral() { + return getTableGroup().isLateral(); + } + + @Override + public void addTableGroupJoin(TableGroupJoin join) { + getTableGroup().addTableGroupJoin( join ); + } + + @Override + public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) { + getTableGroup().prependTableGroupJoin( navigablePath, join ); + } + + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + getTableGroup().addNestedTableGroupJoin( join ); + } + + @Override + public void visitTableGroupJoins(Consumer consumer) { + getTableGroup().visitTableGroupJoins( consumer ); + } + + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + getTableGroup().visitNestedTableGroupJoins( consumer ); + } + + @Override + public void applyAffectedTableNames(Consumer nameCollector) { + getTableGroup().applyAffectedTableNames( nameCollector ); + } + + @Override + public TableReference getPrimaryTableReference() { + return getTableGroup().getPrimaryTableReference(); + } + + @Override + public List getTableReferenceJoins() { + return getTableGroup().getTableReferenceJoins(); + } + + @Override + public DomainResult createDomainResult( + String resultVariable, + DomainResultCreationState creationState) { + return getTableGroup().createDomainResult( resultVariable, creationState ); + } + + @Override + public void applySqlSelections(DomainResultCreationState creationState) { + getTableGroup().applySqlSelections( creationState ); + } + + @Override + public void accept(SqlAstWalker sqlTreeWalker) { + getTableGroup().accept( sqlTreeWalker ); + } + + @Override + public boolean isRealTableGroup() { + return getTableGroup().isRealTableGroup(); + } + + @Override + public boolean isFetched() { + return getTableGroup().isFetched(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FunctionTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FunctionTableGroup.java new file mode 100644 index 0000000000..f6f7eaffb8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FunctionTableGroup.java @@ -0,0 +1,100 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.expression.FunctionExpression; + +/** + * A special table group for a table valued functions. + * + * @author Christian Beikov + */ +public class FunctionTableGroup extends AbstractTableGroup { + + private final FunctionTableReference functionTableReference; + + public FunctionTableGroup( + NavigablePath navigablePath, + TableGroupProducer tableGroupProducer, + FunctionExpression functionExpression, + String sourceAlias, + List columnNames, + boolean lateral, + boolean canUseInnerJoins, + SessionFactoryImplementor sessionFactory) { + super( + canUseInnerJoins, + navigablePath, + tableGroupProducer, + sourceAlias, + null, + sessionFactory + ); + this.functionTableReference = new FunctionTableReference( + functionExpression, + sourceAlias, + columnNames, + lateral, + sessionFactory + ); + } + + @Override + public boolean isLateral() { + return getPrimaryTableReference().isLateral(); + } + + @Override + protected TableReference getTableReferenceInternal( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization, + boolean resolve) { + if ( tableExpression == null ) { + return getPrimaryTableReference(); + } + for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { + final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + if ( groupTableReference != null ) { + return groupTableReference; + } + } + for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { + final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + if ( groupTableReference != null ) { + return groupTableReference; + } + } + return null; + } + + @Override + public void applyAffectedTableNames(Consumer nameCollector) { + functionTableReference.applyAffectedTableNames( nameCollector ); + } + + @Override + public FunctionTableReference getPrimaryTableReference() { + return functionTableReference; + } + + @Override + public List getTableReferenceJoins() { + return Collections.emptyList(); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java index 002fdd4a5f..9b472a2062 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java @@ -8,8 +8,8 @@ package org.hibernate.sql.ast.tree.from; import java.util.Collections; import java.util.List; -import java.util.function.Consumer; import java.util.function.BiPredicate; +import java.util.function.Consumer; import java.util.function.Supplier; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -25,7 +25,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase; * * @author Christian Beikov */ -public class LazyTableGroup extends AbstractColumnReferenceQualifier implements TableGroup { +public class LazyTableGroup extends DelegatingTableGroup { private final boolean canUseInnerJoins; private final NavigablePath navigablePath; @@ -33,7 +33,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements private final TableGroupProducer producer; private final String sourceAlias; private final SqlAliasBase sqlAliasBase; - private final SessionFactoryImplementor sessionFactory; private final Supplier tableGroupSupplier; private final TableGroup parentTableGroup; private final BiPredicate navigablePathChecker; @@ -60,14 +59,13 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements this.tableGroupSupplier = tableGroupSupplier; this.navigablePathChecker = navigablePathChecker; this.parentTableGroup = parentTableGroup; - this.sessionFactory = sessionFactory; - } public TableGroup getUnderlyingTableGroup() { return tableGroup; } + @Override public TableGroup getTableGroup() { if ( tableGroup != null ) { return tableGroup; @@ -97,11 +95,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements } } - @Override - public TableReference getPrimaryTableReference() { - return getTableGroup().getPrimaryTableReference(); - } - @Override public List getTableReferenceJoins() { return tableGroup == null ? Collections.emptyList() : tableGroup.getTableReferenceJoins(); @@ -117,16 +110,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements return tableGroup == null ? Collections.emptyList() : tableGroup.getNestedTableGroupJoins(); } - @Override - public void addTableGroupJoin(TableGroupJoin join) { - getTableGroup().addTableGroupJoin( join ); - } - - @Override - public void addNestedTableGroupJoin(TableGroupJoin join) { - getTableGroup().addNestedTableGroupJoin( join ); - } - @Override public void visitTableGroupJoins(Consumer consumer) { if ( tableGroup != null ) { @@ -146,11 +129,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements return canUseInnerJoins; } - @Override - public boolean isLateral() { - return false; - } - @Override public NavigablePath getNavigablePath() { return navigablePath; @@ -176,11 +154,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements return sourceAlias; } - @Override - protected SessionFactoryImplementor getSessionFactory() { - return sessionFactory; - } - @Override public boolean isRealTableGroup() { return tableGroup != null && tableGroup.isRealTableGroup(); @@ -192,6 +165,39 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements } @Override + public boolean isLateral() { + return false; + } + + @Override + public TableReference resolveTableReference( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization) { + assert tableExpression != null; + + final TableReference tableReference = getTableReferenceInternal( + navigablePath, + tableExpression, + allowFkOptimization, + true + ); + if ( tableReference == null ) { + throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" ); + } + + return tableReference; + } + + @Override + public TableReference getTableReference( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization, + boolean resolve) { + return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); + } + protected TableReference getTableReferenceInternal( NavigablePath navigablePath, String tableExpression, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java index 7f1a9f478e..db82394b32 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java @@ -17,7 +17,7 @@ import org.hibernate.query.NavigablePath; /** * @author Christian Beikov */ -public class MappedByTableGroup implements VirtualTableGroup { +public class MappedByTableGroup extends DelegatingTableGroup implements VirtualTableGroup { private final NavigablePath navigablePath; private final ModelPartContainer modelPart; @@ -41,6 +41,11 @@ public class MappedByTableGroup implements VirtualTableGroup { this.navigablePathChecker = navigablePathChecker; } + @Override + protected TableGroup getTableGroup() { + return underlyingTableGroup; + } + @Override public NavigablePath getNavigablePath() { return navigablePath; @@ -67,9 +72,17 @@ public class MappedByTableGroup implements VirtualTableGroup { return modelPart; } + // Don't provide access to table group joins as this is table group is just a "named reference" + // The underlying table group contains the joins and will render them + @Override - public String getSourceAlias() { - return underlyingTableGroup.getSourceAlias(); + public boolean isRealTableGroup() { + return false; + } + + @Override + public boolean isLateral() { + return false; } @Override @@ -82,26 +95,6 @@ public class MappedByTableGroup implements VirtualTableGroup { return Collections.emptyList(); } - @Override - public boolean isRealTableGroup() { - return false; - } - - @Override - public boolean canUseInnerJoins() { - return underlyingTableGroup.canUseInnerJoins(); - } - - @Override - public void addTableGroupJoin(TableGroupJoin join) { - underlyingTableGroup.addTableGroupJoin( join ); - } - - @Override - public void addNestedTableGroupJoin(TableGroupJoin join) { - underlyingTableGroup.addNestedTableGroupJoin( join ); - } - @Override public void visitTableGroupJoins(Consumer consumer) { // No-op @@ -112,19 +105,9 @@ public class MappedByTableGroup implements VirtualTableGroup { // No-op } - @Override - public void applyAffectedTableNames(Consumer nameCollector) { - underlyingTableGroup.applyAffectedTableNames( nameCollector ); - } - - @Override - public TableReference getPrimaryTableReference() { - return underlyingTableGroup.getPrimaryTableReference(); - } - @Override public List getTableReferenceJoins() { - return underlyingTableGroup.getTableReferenceJoins(); + return Collections.emptyList(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java index 4a706cb52f..5e63ef2f79 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java @@ -108,6 +108,11 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup { throw new UnsupportedOperationException(); } + @Override + public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) { + throw new UnsupportedOperationException(); + } + @Override public void addNestedTableGroupJoin(TableGroupJoin join) { throw new UnsupportedOperationException(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java index 81bb3ea529..400d51370a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java @@ -120,6 +120,13 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem } } + @Override + public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) { + if ( join.getJoinedGroup() != elementTableGroup ) { + elementTableGroup.prependTableGroupJoin( navigablePath, join ); + } + } + @Override public void addNestedTableGroupJoin(TableGroupJoin join) { if ( join.getJoinedGroup() != elementTableGroup ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/QueryPartTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/QueryPartTableGroup.java new file mode 100644 index 0000000000..3e0393f7d4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/QueryPartTableGroup.java @@ -0,0 +1,100 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.select.QueryPart; + +/** + * A special table group for a sub-queries. + * + * @author Christian Beikov + */ +public class QueryPartTableGroup extends AbstractTableGroup { + + private final QueryPartTableReference queryPartTableReference; + + public QueryPartTableGroup( + NavigablePath navigablePath, + TableGroupProducer tableGroupProducer, + QueryPart queryPart, + String sourceAlias, + List columnNames, + boolean lateral, + boolean canUseInnerJoins, + SessionFactoryImplementor sessionFactory) { + super( + canUseInnerJoins, + navigablePath, + tableGroupProducer, + sourceAlias, + null, + sessionFactory + ); + this.queryPartTableReference = new QueryPartTableReference( + queryPart, + sourceAlias, + columnNames, + lateral, + sessionFactory + ); + } + + @Override + public boolean isLateral() { + return getPrimaryTableReference().isLateral(); + } + + @Override + protected TableReference getTableReferenceInternal( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization, + boolean resolve) { + if ( tableExpression == null ) { + return getPrimaryTableReference(); + } + for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { + final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + if ( groupTableReference != null ) { + return groupTableReference; + } + } + for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { + final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + if ( groupTableReference != null ) { + return groupTableReference; + } + } + return null; + } + + @Override + public void applyAffectedTableNames(Consumer nameCollector) { + queryPartTableReference.applyAffectedTableNames( nameCollector ); + } + + @Override + public QueryPartTableReference getPrimaryTableReference() { + return queryPartTableReference; + } + + @Override + public List getTableReferenceJoins() { + return Collections.emptyList(); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java index d2b8f7e6ab..a072a7cfd5 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java @@ -6,8 +6,6 @@ */ package org.hibernate.sql.ast.tree.from; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -17,109 +15,47 @@ import org.hibernate.query.NavigablePath; /** * @author Christian Beikov */ -public class StandardVirtualTableGroup implements VirtualTableGroup { - private final NavigablePath navigablePath; - private final ModelPartContainer modelPart; +public class StandardVirtualTableGroup extends AbstractTableGroup implements VirtualTableGroup { private final TableGroup underlyingTableGroup; private final boolean fetched; - private List tableGroupJoins; - private List nestedTableGroupJoins; - public StandardVirtualTableGroup( NavigablePath navigablePath, ModelPartContainer modelPart, TableGroup underlyingTableGroup, boolean fetched) { - this.navigablePath = navigablePath; - this.modelPart = modelPart; + super( + underlyingTableGroup.canUseInnerJoins(), + navigablePath, + modelPart, + underlyingTableGroup.getSourceAlias(), + null, + null + ); this.underlyingTableGroup = underlyingTableGroup; this.fetched = fetched; } - @Override - public NavigablePath getNavigablePath() { - return navigablePath; - } - @Override public ModelPartContainer getExpressionType() { return getModelPart(); } - @Override - public String getGroupAlias() { - // none, although we could also delegate to the underlyingTableGroup's group-alias - return null; - } - @Override public boolean isFetched() { return fetched; } - @Override - public ModelPartContainer getModelPart() { - return modelPart; - } - @Override public String getSourceAlias() { return underlyingTableGroup.getSourceAlias(); } - @Override - public List getTableGroupJoins() { - return tableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( tableGroupJoins ); - } - - @Override - public List getNestedTableGroupJoins() { - return nestedTableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( nestedTableGroupJoins ); - } - - @Override - public boolean isRealTableGroup() { - return nestedTableGroupJoins != null && !nestedTableGroupJoins.isEmpty(); - } - @Override public boolean canUseInnerJoins() { return underlyingTableGroup.canUseInnerJoins(); } - @Override - public void addTableGroupJoin(TableGroupJoin join) { - if ( tableGroupJoins == null ) { - tableGroupJoins = new ArrayList<>(); - } - assert !tableGroupJoins.contains( join ); - tableGroupJoins.add( join ); - } - - @Override - public void addNestedTableGroupJoin(TableGroupJoin join) { - if ( nestedTableGroupJoins == null ) { - nestedTableGroupJoins = new ArrayList<>(); - } - assert !nestedTableGroupJoins.contains( join ); - nestedTableGroupJoins.add( join ); - } - - @Override - public void visitTableGroupJoins(Consumer consumer) { - if ( tableGroupJoins != null ) { - tableGroupJoins.forEach( consumer ); - } - } - - @Override - public void visitNestedTableGroupJoins(Consumer consumer) { - if ( nestedTableGroupJoins != null ) { - nestedTableGroupJoins.forEach( consumer ); - } - } - @Override public void applyAffectedTableNames(Consumer nameCollector) { underlyingTableGroup.applyAffectedTableNames( nameCollector ); @@ -136,25 +72,7 @@ public class StandardVirtualTableGroup implements VirtualTableGroup { } @Override - public TableReference resolveTableReference( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { - final TableReference tableReference = getTableReference( - navigablePath, - tableExpression, - allowFkOptimization, - true - ); - if ( tableReference == null ) { - throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" ); - } - - return tableReference; - } - - @Override - public TableReference getTableReference( + public TableReference getTableReferenceInternal( NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization, @@ -168,23 +86,7 @@ public class StandardVirtualTableGroup implements VirtualTableGroup { if ( tableReference != null ) { return tableReference; } - for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { - final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup() - .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); - if ( primaryTableReference != null ) { - return primaryTableReference; - } - } - for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { - final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup() - .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); - if ( primaryTableReference != null ) { - return primaryTableReference; - } - } - return null; + return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java index afc4a4683c..6922369a9b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java @@ -47,12 +47,16 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat boolean canUseInnerJoins(); default boolean isLateral() { - return getPrimaryTableReference() instanceof DerivedTableReference - && ( (DerivedTableReference) getPrimaryTableReference() ).isLateral(); + return false; } void addTableGroupJoin(TableGroupJoin join); + /** + * Adds the given table group join before a join as found via the given navigable path. + */ + void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join); + /** * A nested table group join is a join against a table group, * that is ensured to be joined against the primary table reference and table reference joins in isolation, @@ -136,12 +140,6 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat ); } - default ColumnReference locateColumnReferenceByName(String name) { - throw new UnsupportedOperationException( - "Cannot call #locateColumnReferenceByName on this type of TableGroup" - ); - } - @Override default void accept(SqlAstWalker sqlTreeWalker) { sqlTreeWalker.visitTableGroup( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java index 2e954e2c12..426b51321f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java @@ -19,14 +19,7 @@ import org.hibernate.query.NavigablePath; /** * @author Andrea Boriero */ -public class UnionTableGroup implements VirtualTableGroup { - private final boolean canUseInnerJoins; - private final NavigablePath navigablePath; - private List tableGroupJoins; - private List nestedTableGroupJoins; - - private final UnionSubclassEntityPersister modelPart; - private final String sourceAlias; +public class UnionTableGroup extends AbstractTableGroup implements VirtualTableGroup { private final UnionTableReference tableReference; public UnionTableGroup( @@ -35,88 +28,8 @@ public class UnionTableGroup implements VirtualTableGroup { UnionTableReference tableReference, UnionSubclassEntityPersister modelPart, String sourceAlias) { - this.canUseInnerJoins = canUseInnerJoins; - this.navigablePath = navigablePath; + super( canUseInnerJoins, navigablePath, modelPart, sourceAlias, null, null ); this.tableReference = tableReference; - this.modelPart = modelPart; - this.sourceAlias = sourceAlias; - } - - @Override - public NavigablePath getNavigablePath() { - return navigablePath; - } - - @Override - public ModelPart getExpressionType() { - return getModelPart(); - } - - @Override - public String getGroupAlias() { - return null; - } - - @Override - public ModelPartContainer getModelPart() { - return modelPart; - } - - @Override - public String getSourceAlias() { - return sourceAlias; - } - - @Override - public List getTableGroupJoins() { - return tableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( tableGroupJoins ); - } - - @Override - public List getNestedTableGroupJoins() { - return nestedTableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( nestedTableGroupJoins ); - } - - @Override - public boolean isRealTableGroup() { - return nestedTableGroupJoins != null && !nestedTableGroupJoins.isEmpty(); - } - - @Override - public boolean canUseInnerJoins() { - return canUseInnerJoins; - } - - @Override - public void addTableGroupJoin(TableGroupJoin join) { - if ( tableGroupJoins == null ) { - tableGroupJoins = new ArrayList<>(); - } - assert !tableGroupJoins.contains( join ); - tableGroupJoins.add( join ); - } - - @Override - public void addNestedTableGroupJoin(TableGroupJoin join) { - if ( nestedTableGroupJoins == null ) { - nestedTableGroupJoins = new ArrayList<>(); - } - assert !nestedTableGroupJoins.contains( join ); - nestedTableGroupJoins.add( join ); - } - - @Override - public void visitTableGroupJoins(Consumer consumer) { - if ( tableGroupJoins != null ) { - tableGroupJoins.forEach( consumer ); - } - } - - @Override - public void visitNestedTableGroupJoins(Consumer consumer) { - if ( nestedTableGroupJoins != null ) { - nestedTableGroupJoins.forEach( consumer ); - } } @Override @@ -134,40 +47,14 @@ public class UnionTableGroup implements VirtualTableGroup { } @Override - public TableReference getTableReference( + public TableReference getTableReferenceInternal( NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization, boolean resolve) { - return resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); - } - - @Override - public TableReference resolveTableReference( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { - if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, true ) != null ) { + if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { return tableReference; } - if ( nestedTableGroupJoins != null ) { - for ( TableGroupJoin tableGroupJoin : nestedTableGroupJoins ) { - final TableReference tableReference = tableGroupJoin.getJoinedGroup() - .resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); - if ( tableReference != null ) { - return tableReference; - } - } - } - if ( tableGroupJoins != null ) { - for ( TableGroupJoin tableGroupJoin : tableGroupJoins ) { - final TableReference tableReference = tableGroupJoin.getJoinedGroup() - .resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); - if ( tableReference != null ) { - return tableReference; - } - } - } - return null; + return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java index 8ae1dd47b1..cc5f7a5042 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java @@ -48,7 +48,7 @@ public class ValuesTableGroup extends AbstractTableGroup { String tableExpression, boolean allowFkOptimization, boolean resolve) { - if ( getModelPart().containsTableReference( tableExpression ) ) { + if ( ( (TableGroupProducer) getModelPart() ).containsTableReference( tableExpression ) ) { return getPrimaryTableReference(); } for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/BooleanExpressionPredicate.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/BooleanExpressionPredicate.java index f2b56d78e0..05a94780f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/BooleanExpressionPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/BooleanExpressionPredicate.java @@ -16,6 +16,11 @@ import org.hibernate.sql.ast.tree.expression.Expression; public class BooleanExpressionPredicate extends AbstractPredicate { private final Expression expression; + public BooleanExpressionPredicate(Expression expression) { + super( expression.getExpressionType(), false ); + this.expression = expression; + } + public BooleanExpressionPredicate(Expression expression, boolean negated, JdbcMappingContainer expressionType) { super( expressionType, negated ); this.expression = expression; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/map/MapIndexFormulaTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/map/MapIndexFormulaTest.java index 794778ba00..13175d1eb5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/map/MapIndexFormulaTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/map/MapIndexFormulaTest.java @@ -48,6 +48,9 @@ public class MapIndexFormulaTest { session.createQuery( "from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada'" ) .list(); + session.createQuery( + "from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada' and maxindex(u) = 'nada'" ) + .list(); } ); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java index aeb0f64943..b80ff7cb1d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java @@ -8,10 +8,7 @@ package org.hibernate.envers.function; import java.util.List; -import java.util.function.Consumer; -import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.ordering.OrderByFragment; import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; @@ -29,11 +26,10 @@ import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.sql.ast.spi.SqlAstQueryPartProcessingState; +import org.hibernate.sql.ast.tree.from.DelegatingTableGroup; 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.TableReference; -import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.type.spi.TypeConfiguration; @@ -110,7 +106,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { }; } - private static class AuditingTableGroup implements TableGroup { + private static class AuditingTableGroup extends DelegatingTableGroup { private final TableGroup delegate; private final String auditTableExpression; @@ -123,8 +119,8 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { } @Override - public ModelPart getExpressionType() { - return delegate.getExpressionType(); + protected TableGroup getTableGroup() { + return delegate; } @Override @@ -135,7 +131,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { if ( tableExpression.equals( normalTableExpression ) ) { tableExpression = auditTableExpression; } - return delegate.resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); + return super.resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); } @Override @@ -147,77 +143,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { if ( tableExpression.equals( normalTableExpression ) ) { tableExpression = auditTableExpression; } - return delegate.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); - } - - @Override - public NavigablePath getNavigablePath() { - return delegate.getNavigablePath(); - } - - @Override - public String getGroupAlias() { - return delegate.getGroupAlias(); - } - - @Override - public ModelPartContainer getModelPart() { - return delegate.getModelPart(); - } - - @Override - public String getSourceAlias() { - return delegate.getSourceAlias(); - } - - @Override - public List getTableGroupJoins() { - return delegate.getTableGroupJoins(); - } - - @Override - public List getNestedTableGroupJoins() { - return delegate.getNestedTableGroupJoins(); - } - - @Override - public boolean canUseInnerJoins() { - return delegate.canUseInnerJoins(); - } - - @Override - public void addTableGroupJoin(TableGroupJoin join) { - delegate.addTableGroupJoin( join ); - } - - @Override - public void addNestedTableGroupJoin(TableGroupJoin join) { - delegate.addNestedTableGroupJoin( join ); - } - - @Override - public void visitTableGroupJoins(Consumer consumer) { - delegate.visitTableGroupJoins( consumer ); - } - - @Override - public void visitNestedTableGroupJoins(Consumer consumer) { - delegate.visitNestedTableGroupJoins( consumer ); - } - - @Override - public void applyAffectedTableNames(Consumer nameCollector) { - delegate.applyAffectedTableNames( nameCollector ); - } - - @Override - public TableReference getPrimaryTableReference() { - return delegate.getPrimaryTableReference(); - } - - @Override - public List getTableReferenceJoins() { - return delegate.getTableReferenceJoins(); + return super.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); } } }