Add emulation for simple lateral joins and make use of lateral joins for min/max index/element paths if possible

This commit is contained in:
Christian Beikov 2021-12-15 14:18:08 +01:00 committed by Steve Ebersole
parent ab954925e4
commit fb30206387
54 changed files with 1455 additions and 725 deletions

View File

@ -701,6 +701,11 @@ public class FirebirdDialect extends Dialect {
return getVersion().isSameOrAfter( 3, 0 ); return getVersion().isSameOrAfter( 3, 0 );
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 4, 0 );
}
@Override @Override
public String translateExtractField(TemporalUnit unit) { public String translateExtractField(TemporalUnit unit) {
switch ( unit ) { switch ( unit ) {

View File

@ -341,6 +341,16 @@ public class InformixDialect extends Dialect {
return false; return false;
} }
@Override
public boolean supportsWindowFunctions() {
return getVersion().isSameOrAfter( 12, 10 );
}
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 12, 10 );
}
@Override @Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return EXTRACTOR; return EXTRACTOR;

View File

@ -500,7 +500,11 @@ public class IngresDialect extends Dialect {
return false; return false;
} }
// Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override
public boolean supportsWindowFunctions() {
return getVersion().isSameOrAfter( 10, 2 );
}
// Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override @Override
public boolean doesReadCommittedCauseWritersToBlockReaders() { public boolean doesReadCommittedCauseWritersToBlockReaders() {

View File

@ -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.Every;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Summarization; 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.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
@ -92,6 +93,11 @@ public class SQLiteSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
} }
} }
@Override
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
emulateQueryPartTableReferenceColumnAliasing( tableReference );
}
@Override @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
if ( !isRowNumberingCurrentQueryPart() ) { if ( !isRowNumberingCurrentQueryPart() ) {

View File

@ -30,7 +30,7 @@ import org.hibernate.sql.exec.spi.JdbcOperation;
import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.SqlTypes.*;
/** /**
* SQL Dialect for Sybase Anywhere * SQL Dialect for Sybase/SQL Anywhere
* (Tested on ASA 8.x) * (Tested on ASA 8.x)
*/ */
public class SybaseAnywhereDialect extends SybaseDialect { public class SybaseAnywhereDialect extends SybaseDialect {
@ -135,6 +135,16 @@ public class SybaseAnywhereDialect extends SybaseDialect {
return false; return false;
} }
@Override
public boolean supportsWindowFunctions() {
return getVersion().isSameOrAfter( 12 );
}
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 10 );
}
@Override @Override
public IdentityColumnSupport getIdentityColumnSupport() { public IdentityColumnSupport getIdentityColumnSupport() {
return new SybaseAnywhereIdentityColumnSupport(); return new SybaseAnywhereIdentityColumnSupport();

View File

@ -379,6 +379,11 @@ public class TeradataDialect extends Dialect {
return false; return false;
} }
@Override
public boolean supportsWindowFunctions() {
return getVersion().isSameOrAfter( 16, 10 );
}
@Override @Override
public String getSelectClauseNullString(int sqlType) { public String getSelectClauseNullString(int sqlType) {
String v = "null"; String v = "null";

View File

@ -11,7 +11,6 @@ import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.ComparisonOperator; import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.SemanticException;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
@ -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.cte.CteStatement;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; 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.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
@ -70,13 +70,12 @@ public class TimesTenSqlAstTranslator<T extends JdbcOperation> extends AbstractS
} }
final Predicate predicate; final Predicate predicate;
if ( tableGroupJoin.isLateral() ) { if ( tableGroupJoin.getPredicate() == null ) {
append( "lateral " ); if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) {
if ( tableGroupJoin.getPredicate() == null ) { predicate = null;
predicate = new Junction( Junction.Nature.CONJUNCTION );
} }
else { else {
predicate = tableGroupJoin.getPredicate(); predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) );
} }
} }
else { else {
@ -86,7 +85,7 @@ public class TimesTenSqlAstTranslator<T extends JdbcOperation> extends AbstractS
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
} }
else { else {
renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
} }
} }

View File

@ -25,6 +25,7 @@ import org.hibernate.engine.config.spi.ConfigurationService.Converter;
import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.*; import org.hibernate.engine.jdbc.*;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; 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.jdbc.env.spi.*;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
@ -228,6 +229,26 @@ public abstract class AbstractHANADialect extends Dialect {
return false; 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 @Override
public String castPattern(CastType from, CastType to) { public String castPattern(CastType from, CastType to) {
if ( to == CastType.BOOLEAN ) { if ( to == CastType.BOOLEAN ) {
@ -991,6 +1012,11 @@ public abstract class AbstractHANADialect extends Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 2, 0, 40 );
}
@Override @Override
public boolean supportsNoWait() { public boolean supportsNoWait() {
return true; return true;

View File

@ -672,6 +672,11 @@ public class CockroachDialect extends Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 20, 1 );
}
@Override @Override
public boolean supportsNoWait() { public boolean supportsNoWait() {
return getVersion().isSameOrAfter( 20, 1 ); return getVersion().isSameOrAfter( 20, 1 );

View File

@ -671,6 +671,11 @@ public class DB2Dialect extends Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 9, 1 );
}
@Override @Override
public void appendDatetimeFormat(SqlAppender appender, String format) { public void appendDatetimeFormat(SqlAppender appender, String format) {
//DB2 does not need nor support FM //DB2 does not need nor support FM

View File

@ -99,6 +99,11 @@ public class DB2iDialect extends DB2Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getIVersion().isSameOrAfter( 7, 1 );
}
@Override @Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() { return new StandardSqlAstTranslatorFactory() {

View File

@ -42,7 +42,7 @@ public class DB2zDialect extends DB2Dialect {
} }
public DB2zDialect() { public DB2zDialect() {
this( DatabaseVersion.make(7) ); this( DatabaseVersion.make( 7 ) );
} }
public DB2zDialect(DatabaseVersion version) { public DB2zDialect(DatabaseVersion version) {
@ -53,7 +53,7 @@ public class DB2zDialect extends DB2Dialect {
@Override @Override
protected String columnType(int jdbcTypeCode) { protected String columnType(int jdbcTypeCode) {
// See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html // 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 "timestamp with time zone";
} }
return super.columnType(jdbcTypeCode); return super.columnType(jdbcTypeCode);
@ -95,6 +95,11 @@ public class DB2zDialect extends DB2Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return true;
}
@Override @Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) { public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
StringBuilder pattern = new StringBuilder(); StringBuilder pattern = new StringBuilder();

View File

@ -6,11 +6,18 @@
*/ */
package org.hibernate.dialect; package org.hibernate.dialect;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.ComparisonOperator; import org.hibernate.query.ComparisonOperator;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
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.ast.tree.select.QueryPart;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
@ -50,4 +57,24 @@ public class DB2zSqlAstTranslator<T extends JdbcOperation> extends DB2SqlAstTran
renderComparisonStandard( lhs, operator, rhs ); 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 );
}
} }

View File

@ -3503,6 +3503,16 @@ public abstract class Dialect implements ConversionContext {
return false; 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() { public CallableStatementSupport getCallableStatementSupport() {
// most databases do not support returning cursors (ref_cursor)... // most databases do not support returning cursors (ref_cursor)...
return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE; return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE;

View File

@ -6,11 +6,6 @@
*/ */
package org.hibernate.dialect; 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. * An SQL dialect for the SAP HANA Cloud column store.
* <p> * <p>
@ -30,7 +25,29 @@ import org.hibernate.type.StandardBasicTypes;
public class HANACloudColumnStoreDialect extends HANAColumnStoreDialect { public class HANACloudColumnStoreDialect extends HANAColumnStoreDialect {
public HANACloudColumnStoreDialect() { public HANACloudColumnStoreDialect() {
// No idea how the versioning scheme is here, but since this is deprecated anyway, keep it as is
super( DatabaseVersion.make( 4 ) ); 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();
}
} }

View File

@ -35,13 +35,15 @@ import org.hibernate.type.StandardBasicTypes;
* @author <a href="mailto:jonathan.bregler@sap.com">Jonathan Bregler</a> * @author <a href="mailto:jonathan.bregler@sap.com">Jonathan Bregler</a>
*/ */
public class HANAColumnStoreDialect extends AbstractHANADialect { public class HANAColumnStoreDialect extends AbstractHANADialect {
public HANAColumnStoreDialect(DialectResolutionInfo info) { public HANAColumnStoreDialect(DialectResolutionInfo info) {
this( info.makeCopy() ); this( AbstractHANADialect.createVersion( info ) );
registerKeywords( info ); registerKeywords( info );
} }
public HANAColumnStoreDialect() { 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) { public HANAColumnStoreDialect(DatabaseVersion version) {
@ -172,17 +174,11 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
@Override @Override
protected boolean supportsAsciiStringTypes() { protected boolean supportsAsciiStringTypes() {
return getVersion().isBefore( 4 ); return true;
} }
@Override @Override
protected Boolean useUnicodeStringTypesDefault() { protected Boolean useUnicodeStringTypesDefault() {
return getVersion().isSameOrAfter( 4 ); return true;
}
@Override
public boolean isUseUnicodeStringTypes() {
return getVersion().isSameOrAfter( 4 )
|| super.isUseUnicodeStringTypes();
} }
} }

View File

@ -32,13 +32,14 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
*/ */
public class HANARowStoreDialect extends AbstractHANADialect { public class HANARowStoreDialect extends AbstractHANADialect {
public HANARowStoreDialect() { public HANARowStoreDialect(DialectResolutionInfo info) {
super( DatabaseVersion.make( 3, 0 )); this( AbstractHANADialect.createVersion( info ) );
registerKeywords( info );
} }
public HANARowStoreDialect(DialectResolutionInfo info) { public HANARowStoreDialect() {
this( info.makeCopy() ); // SAP HANA 1.0 SPS12 R0 is the default
registerKeywords( info ); this( DatabaseVersion.make( 1, 0, 120 ) );
} }
public HANARowStoreDialect(DatabaseVersion version) { public HANARowStoreDialect(DatabaseVersion version) {

View File

@ -720,6 +720,11 @@ public class HSQLDialect extends Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 2, 6, 1 );
}
@Override @Override
public boolean requiresFloatCastingOfIntegerDivision() { public boolean requiresFloatCastingOfIntegerDivision() {
return true; return true;

View File

@ -33,6 +33,8 @@ import org.hibernate.type.StandardBasicTypes;
* @author Gavin King * @author Gavin King
*/ */
public class MariaDBDialect extends MySQLDialect { public class MariaDBDialect extends MySQLDialect {
private static final DatabaseVersion VERSION5 = DatabaseVersion.make( 5 );
private static final DatabaseVersion VERSION57 = DatabaseVersion.make( 5, 7 );
public MariaDBDialect() { public MariaDBDialect() {
this( DatabaseVersion.make( 5 ) ); this( DatabaseVersion.make( 5 ) );
@ -49,8 +51,8 @@ public class MariaDBDialect extends MySQLDialect {
@Override @Override
public DatabaseVersion getMySQLVersion() { public DatabaseVersion getMySQLVersion() {
return getVersion().isBefore( 5, 3 ) return getVersion().isBefore( 5, 3 )
? DatabaseVersion.make( 5 ) ? VERSION5
: DatabaseVersion.make( 5, 7 ); : VERSION57;
} }
@Override @Override

View File

@ -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.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; 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.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
@ -71,6 +72,11 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
} }
} }
@Override
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
emulateQueryPartTableReferenceColumnAliasing( tableReference );
}
@Override @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
if ( !isRowNumberingCurrentQueryPart() ) { if ( !isRowNumberingCurrentQueryPart() ) {

View File

@ -1179,6 +1179,11 @@ public class MySQLDialect extends Dialect {
return getMySQLVersion().isSameOrAfter( 8, 2 ); return getMySQLVersion().isSameOrAfter( 8, 2 );
} }
@Override
public boolean supportsLateral() {
return getMySQLVersion().isSameOrAfter( 8, 14 );
}
@Override @Override
public boolean supportsSkipLocked() { public boolean supportsSkipLocked() {
return getMySQLVersion().isSameOrAfter( 8 ); return getMySQLVersion().isSameOrAfter( 8 );

View File

@ -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.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; 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.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
@ -72,6 +74,21 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> 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 @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
if ( !isRowNumberingCurrentQueryPart() ) { if ( !isRowNumberingCurrentQueryPart() ) {

View File

@ -1061,6 +1061,11 @@ public class OracleDialect extends Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 12, 1 );
}
@Override @Override
public boolean supportsNoWait() { public boolean supportsNoWait() {
return getVersion().isSameOrAfter( 9 ); return getVersion().isSameOrAfter( 9 );

View File

@ -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.Over;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; 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.UnionTableGroup;
import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.insert.Values;
@ -248,48 +250,20 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
@Override @Override
public void visitValuesTableReference(ValuesTableReference tableReference) { public void visitValuesTableReference(ValuesTableReference tableReference) {
final List<Values> valuesList = tableReference.getValuesList(); emulateValuesTableReferenceColumnAliasing( tableReference );
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<Clause> clauseStack = getClauseStack();
clauseStack.push( Clause.VALUES );
try {
// We render the first select statement with aliases
clauseStack.push( Clause.SELECT );
try { @Override
appendSql( "select " ); public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
emulateQueryPartTableReferenceColumnAliasing( tableReference );
}
renderCommaSeparatedSelectExpression( @Override
valuesList.get( 0 ).getExpressions(), public void visitFunctionTableReference(FunctionTableReference tableReference) {
tableReference.getColumnNames() append( "table(" );
); tableReference.getFunctionExpression().accept( this );
appendSql( getFromDualForSelectOnly() ); append( CLOSE_PARENTHESIS );
} renderTableReferenceIdentificationVariable( tableReference );
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 @Override

View File

@ -1006,6 +1006,11 @@ public class PostgreSQLDialect extends Dialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 9, 3 );
}
@Override @Override
public boolean supportsFetchClause(FetchClauseType type) { public boolean supportsFetchClause(FetchClauseType type) {
switch ( type ) { switch ( type ) {

View File

@ -562,6 +562,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
return true; return true;
} }
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 9 );
}
@Override @Override
public boolean supportsFetchClause(FetchClauseType type) { public boolean supportsFetchClause(FetchClauseType type) {
return getVersion().isSameOrAfter( 11 ); return getVersion().isSameOrAfter( 11 );

View File

@ -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.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference; 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.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableReference; 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.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
@ -44,6 +45,8 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
private static final String UNION_ALL = " union all "; private static final String UNION_ALL = " union all ";
private Predicate lateralPredicate;
public SQLServerSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { public SQLServerSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@ -51,7 +54,7 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
@Override @Override
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) { protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
appendSql( WHITESPACE ); appendSql( WHITESPACE );
if ( tableGroupJoin.isLateral() ) { if ( tableGroupJoin.getJoinedGroup().isLateral() ) {
if ( tableGroupJoin.getJoinType() == SqlAstJoinType.LEFT ) { if ( tableGroupJoin.getJoinType() == SqlAstJoinType.LEFT ) {
appendSql( "outer apply " ); appendSql( "outer apply " );
} }
@ -66,19 +69,31 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
final Predicate predicate = tableGroupJoin.getPredicate(); final Predicate predicate = tableGroupJoin.getPredicate();
if ( predicate != null && !predicate.isEmpty() ) { if ( predicate != null && !predicate.isEmpty() ) {
if ( tableGroupJoin.isLateral() ) { if ( tableGroupJoin.getJoinedGroup().isLateral() ) {
renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); // We have to inject the lateral predicate into the sub-query
addAdditionalWherePredicate( predicate ); final Predicate lateralPredicate = this.lateralPredicate;
this.lateralPredicate = predicate;
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
this.lateralPredicate = lateralPredicate;
} }
else { else {
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
} }
} }
else { 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 @Override
protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) { protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) {
final String tableExpression = tableReference.getTableExpression(); final String tableExpression = tableReference.getTableExpression();
@ -237,6 +252,11 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
@Override @Override
public void visitQueryGroup(QueryGroup queryGroup) { public void visitQueryGroup(QueryGroup queryGroup) {
final Predicate lateralPredicate = this.lateralPredicate;
if ( lateralPredicate != null ) {
this.lateralPredicate = null;
addAdditionalWherePredicate( lateralPredicate );
}
if ( shouldEmulateFetchClause( queryGroup ) ) { if ( shouldEmulateFetchClause( queryGroup ) ) {
emulateFetchOffsetWithWindowFunctions( queryGroup, !isRowsOnlyFetchClauseType( queryGroup ) ); emulateFetchOffsetWithWindowFunctions( queryGroup, !isRowsOnlyFetchClauseType( queryGroup ) );
} }
@ -255,6 +275,15 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
} }
} }
@Override
public void visitSelectClause(SelectClause selectClause) {
if ( lateralPredicate != null ) {
addAdditionalWherePredicate( lateralPredicate );
lateralPredicate = null;
}
super.visitSelectClause( selectClause );
}
@Override @Override
protected boolean needsRowsToSkip() { protected boolean needsRowsToSkip() {
return getDialect().getVersion().isBefore( 9 ); return getDialect().getVersion().isBefore( 9 );

View File

@ -8,6 +8,7 @@ package org.hibernate.dialect;
import java.util.List; import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.ComparisonOperator; import org.hibernate.query.ComparisonOperator;
import org.hibernate.sql.ast.Clause; 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.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.DerivedTableReference;
import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate; 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.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
@ -118,40 +120,24 @@ public class SpannerSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
} }
@Override @Override
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) { protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
appendSql( WHITESPACE ); final TableReference tableReference = tableGroup.getPrimaryTableReference();
appendSql( tableGroupJoin.getJoinType().getText() ); if ( tableReference instanceof NamedTableReference ) {
appendSql( "join " ); return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
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();
} }
final DerivedTableReference derivedTableReference = (DerivedTableReference) tableReference;
final boolean correlated = derivedTableReference.isLateral();
final boolean oldCorrelated = this.correlated; final boolean oldCorrelated = this.correlated;
if ( correlated ) { if ( correlated ) {
this.correlated = true; this.correlated = true;
appendSql( "unnest(array" ); appendSql( "unnest(array" );
} }
if ( predicate != null && !predicate.isEmpty() ) { tableReference.accept( this );
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
}
else {
renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector );
}
if ( correlated ) { if ( correlated ) {
this.correlated = oldCorrelated; this.correlated = oldCorrelated;
appendSql( CLOSE_PARENTHESIS ); appendSql( CLOSE_PARENTHESIS );
} }
return false;
} }
@Override @Override

View File

@ -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.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.UnionTableReference; 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.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
@ -124,13 +125,12 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
} }
final Predicate predicate; final Predicate predicate;
if ( tableGroupJoin.isLateral() ) { if ( tableGroupJoin.getPredicate() == null ) {
append( "lateral " ); if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) {
if ( tableGroupJoin.getPredicate() == null ) { predicate = null;
predicate = new Junction( Junction.Nature.CONJUNCTION );
} }
else { else {
predicate = tableGroupJoin.getPredicate(); predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) );
} }
} }
else { else {
@ -140,7 +140,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
} }
else { else {
renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector ); renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
} }
} }
@ -229,11 +229,16 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
} }
} }
@Override
protected boolean supportsDistinctFromPredicate() {
return getDialect().getVersion().isSameOrAfter( 16, 3 );
}
@Override @Override
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
// I think intersect is only supported in 16.0 SP3 // I think intersect is only supported in 16.0 SP3
if ( getDialect().isAnsiNullOn() ) { if ( getDialect().isAnsiNullOn() ) {
if ( getDialect().getVersion().isSameOrAfter( 16, 3 ) ) { if ( supportsDistinctFromPredicate() ) {
renderComparisonEmulateIntersect( lhs, operator, rhs ); renderComparisonEmulateIntersect( lhs, operator, rhs );
} }
else { else {
@ -282,7 +287,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
} }
} }
else { else {
if ( getDialect().getVersion().isSameOrAfter( 16, 3 ) ) { if ( supportsDistinctFromPredicate() ) {
renderComparisonEmulateIntersect( lhs, operator, rhs ); renderComparisonEmulateIntersect( lhs, operator, rhs );
} }
else { else {

View File

@ -28,6 +28,8 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
*/ */
public class TiDBDialect extends MySQLDialect { public class TiDBDialect extends MySQLDialect {
private static final DatabaseVersion VERSION57 = DatabaseVersion.make( 5, 7 );
public TiDBDialect() { public TiDBDialect() {
this( DatabaseVersion.make(5, 4) ); this( DatabaseVersion.make(5, 4) );
} }
@ -45,7 +47,7 @@ public class TiDBDialect extends MySQLDialect {
@Override @Override
public DatabaseVersion getMySQLVersion() { public DatabaseVersion getMySQLVersion() {
// For simplicitys sake, configure MySQL 5.7 compatibility // For simplicitys sake, configure MySQL 5.7 compatibility
return DatabaseVersion.make( 5, 7 ); return VERSION57;
} }
private void registerKeywords() { private void registerKeywords() {

View File

@ -6,15 +6,13 @@
*/ */
package org.hibernate.query.results; package org.hibernate.query.results;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.AbstractTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
@ -23,97 +21,26 @@ import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class TableGroupImpl implements TableGroup { public class TableGroupImpl extends AbstractTableGroup {
private final NavigablePath navigablePath;
private final String alias;
private final TableReference primaryTableReference; private final TableReference primaryTableReference;
private List<TableGroupJoin> tableGroupJoins;
private final ModelPartContainer container;
private final String sourceAlias;
public TableGroupImpl( public TableGroupImpl(
NavigablePath navigablePath, NavigablePath navigablePath,
String alias, String alias,
TableReference primaryTableReference, TableReference primaryTableReference,
ModelPartContainer container, ModelPartContainer container) {
String sourceAlias) { super( false, navigablePath, container, alias, null, null );
this.navigablePath = navigablePath;
this.alias = alias;
this.primaryTableReference = primaryTableReference; this.primaryTableReference = primaryTableReference;
this.container = container;
this.sourceAlias = sourceAlias;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
} }
@Override @Override
public String getGroupAlias() { public String getGroupAlias() {
return alias; return getSourceAlias();
}
@Override
public ModelPartContainer getModelPart() {
return container;
}
@Override
public ModelPartContainer getExpressionType() {
return getModelPart();
}
@Override
public String getSourceAlias() {
return sourceAlias;
}
@Override
public List<TableGroupJoin> getTableGroupJoins() {
return tableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( tableGroupJoins );
}
@Override
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
tableGroupJoins.forEach( consumer );
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
} }
@Override @Override
public void applyAffectedTableNames(Consumer<String> nameCollector) { public void applyAffectedTableNames(Consumer<String> nameCollector) {
} }
@Override @Override
@ -127,20 +54,7 @@ public class TableGroupImpl implements TableGroup {
} }
@Override @Override
public TableReference resolveTableReference( public TableReference getTableReferenceInternal(
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(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization, boolean allowFkOptimization,
@ -148,15 +62,7 @@ public class TableGroupImpl implements TableGroup {
if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization, resolve ) != null ) { if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization, resolve ) != null ) {
return primaryTableReference; return primaryTableReference;
} }
return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference();
if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) {
return primaryTableReference;
}
}
return null;
} }
} }

View File

@ -166,7 +166,7 @@ public class DynamicResultBuilderEntityStandard
if ( lockMode != null ) { if ( lockMode != null ) {
domainResultCreationState.getSqlAstCreationState().registerLockMode( tableAlias, lockMode ); 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(); final TableReference tableReference = tableGroup.getPrimaryTableReference();

View File

@ -471,7 +471,6 @@ public class CteInsertHandler implements InsertHandler {
false, false,
factory factory
), ),
null,
null null
); );
final TableGroup rowsWithSequenceTableGroup = new CteTableGroup( final TableGroup rowsWithSequenceTableGroup = new CteTableGroup(
@ -868,7 +867,6 @@ public class CteInsertHandler implements InsertHandler {
false, false,
factory factory
), ),
null,
null null
); );
final TableGroup rootInsertCteTableGroup = new CteTableGroup( final TableGroup rootInsertCteTableGroup = new CteTableGroup(

View File

@ -273,8 +273,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
null, null,
temporaryTableReference, temporaryTableReference,
entityDescriptor, entityDescriptor
null
); );
querySpec.getFromClause().addRoot( temporaryTableGroup ); querySpec.getFromClause().addRoot( temporaryTableGroup );
final InsertStatement insertStatement = new InsertStatement( dmlTableReference ); final InsertStatement insertStatement = new InsertStatement( dmlTableReference );
@ -607,8 +606,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
false, false,
sessionFactory sessionFactory
), ),
entityDescriptor, entityDescriptor
null
); );
querySpec.getFromClause().addRoot( temporaryTableGroup ); querySpec.getFromClause().addRoot( temporaryTableGroup );
final InsertStatement insertStatement = new InsertStatement( dmlTargetTableReference ); final InsertStatement insertStatement = new InsertStatement( dmlTargetTableReference );

View File

@ -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.CompositeSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPath; import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPath;
import org.hibernate.persister.entity.AbstractEntityPersister; 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.criteria.JpaPath;
import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; 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.CorrelatedPluralTableGroup;
import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.PluralTableGroup; 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.ast.tree.from.TableReference;
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification; import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
@ -3221,22 +3225,22 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public Expression visitMaxElementPath(SqmMaxElementPath<?> path) { public Expression visitMaxElementPath(SqmMaxElementPath<?> path) {
return createCorrelatedAggregateSubQuery( path, false, true ); return createMinOrMaxIndexOrElement( path, false, true );
} }
@Override @Override
public Expression visitMinElementPath(SqmMinElementPath<?> path) { public Expression visitMinElementPath(SqmMinElementPath<?> path) {
return createCorrelatedAggregateSubQuery( path, false, false ); return createMinOrMaxIndexOrElement( path, false, false );
} }
@Override @Override
public Expression visitMaxIndexPath(SqmMaxIndexPath<?> path) { public Expression visitMaxIndexPath(SqmMaxIndexPath<?> path) {
return createCorrelatedAggregateSubQuery( path, true, true ); return createMinOrMaxIndexOrElement( path, true, true );
} }
@Override @Override
public Expression visitMinIndexPath(SqmMinIndexPath<?> path) { public Expression visitMinIndexPath(SqmMinIndexPath<?> path) {
return createCorrelatedAggregateSubQuery( path, true, false ); return createMinOrMaxIndexOrElement( path, true, false );
} }
@Override @Override
@ -3388,6 +3392,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> 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( protected Expression createCorrelatedAggregateSubQuery(
AbstractSqmSpecificPluralPartPath<?> pluralPartPath, AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
boolean index, boolean index,
@ -3497,6 +3514,219 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return subQuerySpec; 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<String> columnNames = new ArrayList<>( jdbcTypeCount );
final List<ColumnReference> 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<ColumnReference> 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) { private Expression withTreatRestriction(Expression expression, SqmPath<?> path) {
final SqmPath<?> lhs = path.getLhs(); final SqmPath<?> lhs = path.getLhs();
if ( lhs instanceof SqmTreatedPath<?, ?> ) { if ( lhs instanceof SqmTreatedPath<?, ?> ) {

View File

@ -35,6 +35,7 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.internal.SqlFragmentPredicate; import org.hibernate.persister.internal.SqlFragmentPredicate;
import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.SetOperator;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind; 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.FunctionTableReference;
import org.hibernate.sql.ast.tree.from.LazyTableGroup; import org.hibernate.sql.ast.tree.from.LazyTableGroup;
import org.hibernate.sql.ast.tree.from.NamedTableReference; 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.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; 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.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference;
@ -194,6 +197,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
private final Set<String> affectedTableNames = new HashSet<>(); private final Set<String> affectedTableNames = new HashSet<>();
private MutationStatement dmlStatement; private MutationStatement dmlStatement;
private boolean needsSelectAliases; private boolean needsSelectAliases;
// Column aliases that need to be injected
private List<String> columnAliases;
private Predicate additionalWherePredicate; private Predicate additionalWherePredicate;
// We must reset the queryPartForRowNumbering fields to null if a query part is visited that does not // 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. // 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<T extends JdbcOperation> implemen
private transient AbstractSqmSelfRenderingFunctionDescriptor castFunction; private transient AbstractSqmSelfRenderingFunctionDescriptor castFunction;
private transient LazySessionWrapperOptions lazySessionWrapperOptions; private transient LazySessionWrapperOptions lazySessionWrapperOptions;
private transient BasicType<Integer> integerType; private transient BasicType<Integer> integerType;
private transient BasicType<Boolean> booleanType;
private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT; private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT;
@ -258,6 +264,15 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
return integerType; return integerType;
} }
public BasicType<Boolean> getBooleanType() {
if ( booleanType == null ) {
booleanType = sessionFactory.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve( StandardBasicTypes.BOOLEAN );
}
return booleanType;
}
/** /**
* A lazy session implementation that is needed for rendering literals. * A lazy session implementation that is needed for rendering literals.
* Usually, only the {@link WrapperOptions} interface is needed, * Usually, only the {@link WrapperOptions} interface is needed,
@ -1418,9 +1433,13 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
this.queryPartForRowNumberingClauseDepth = -1; this.queryPartForRowNumberingClauseDepth = -1;
this.needsSelectAliases = false; 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 // 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 // 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; this.needsSelectAliases = true;
queryGroupAlias = "grp_" + queryGroupAliasCounter + '_'; queryGroupAlias = "grp_" + queryGroupAliasCounter + '_';
queryGroupAliasCounter++; queryGroupAliasCounter++;
@ -1470,6 +1489,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
if ( queryGroupAlias != null ) { if ( queryGroupAlias != null ) {
appendSql( ") " ); appendSql( ") " );
appendSql( queryGroupAlias ); appendSql( queryGroupAlias );
if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) {
visitWhereClause( additionalWherePredicate );
}
}
if ( needsParenthesis ) {
appendSql( CLOSE_PARENTHESIS );
} }
} }
finally { finally {
@ -1504,7 +1529,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
if ( currentQueryPart instanceof QueryGroup ) { if ( currentQueryPart instanceof QueryGroup ) {
// We always need query wrapping if we are in a query group and this query spec has a fetch clause // 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 // 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, // 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 // or if the database does not support simple query grouping, we must use a select wrapper
if ( !supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() ) { if ( !supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() ) {
@ -2849,7 +2875,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
if ( !needsParenthesis || queryPart.isRoot() ) { if ( !needsParenthesis || queryPart.isRoot() ) {
appendSql( CLOSE_PARENTHESIS ); appendSql( CLOSE_PARENTHESIS );
} }
appendSql( WHITESPACE); appendSql( WHITESPACE );
appendSql( alias ); appendSql( alias );
appendSql( " where " ); appendSql( " where " );
final Stack<Clause> clauseStack = getClauseStack(); final Stack<Clause> clauseStack = getClauseStack();
@ -2905,6 +2931,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
} }
// todo: not sure if databases handle order by row number or the original ordering better.. // todo: not sure if databases handle order by row number or the original ordering better..
if ( offsetExpression == null ) { if ( offsetExpression == null ) {
final Predicate additionalWherePredicate = this.additionalWherePredicate;
if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) {
this.additionalWherePredicate = null;
appendSql( " and " );
additionalWherePredicate.accept( this );
}
if ( queryPart.isRoot() ) { if ( queryPart.isRoot() ) {
switch ( fetchClauseType ) { switch ( fetchClauseType ) {
case PERCENT_ONLY: case PERCENT_ONLY:
@ -2929,6 +2961,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
appendSql( alias ); appendSql( alias );
appendSql( ".rn>" ); appendSql( ".rn>" );
offsetExpression.accept( this ); offsetExpression.accept( this );
final Predicate additionalWherePredicate = this.additionalWherePredicate;
if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) {
this.additionalWherePredicate = null;
appendSql( " and " );
additionalWherePredicate.accept( this );
}
if ( queryPart.isRoot() ) { if ( queryPart.isRoot() ) {
appendSql( " order by " ); appendSql( " order by " );
appendSql( alias ); appendSql( alias );
@ -3010,6 +3048,46 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
} }
final SqlAstNodeRenderingMode original = parameterRenderingMode; final SqlAstNodeRenderingMode original = parameterRenderingMode;
if ( needsSelectAliases || referenceStrategy == SelectItemReferenceStrategy.ALIAS && hasSelectAliasInGroupByClause() ) { 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; String separator = NO_SEPARATOR;
for ( int i = 0; i < size; i++ ) { for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i ); final SqlSelection sqlSelection = sqlSelections.get( i );
@ -3022,13 +3100,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
} }
visitSqlSelection( sqlSelection ); visitSqlSelection( sqlSelection );
parameterRenderingMode = original; parameterRenderingMode = original;
appendSql( " c" );
appendSql( i );
separator = COMA_SEPARATOR; separator = COMA_SEPARATOR;
} }
if ( queryPartForRowNumbering != null ) {
renderRowNumberingSelectItems( selectClause, queryPartForRowNumbering );
}
} }
else { else {
String separator = NO_SEPARATOR; String separator = NO_SEPARATOR;
@ -3042,6 +3115,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER; parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
} }
visitSqlSelection( sqlSelection ); visitSqlSelection( sqlSelection );
appendSql( WHITESPACE );
appendSql( columnAliases.get( i ) );
parameterRenderingMode = original; parameterRenderingMode = original;
separator = COMA_SEPARATOR; separator = COMA_SEPARATOR;
} }
@ -3486,7 +3561,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
String separator = NO_SEPARATOR; String separator = NO_SEPARATOR;
for ( TableGroup root : fromClause.getRoots() ) { for ( TableGroup root : fromClause.getRoots() ) {
appendSql( separator ); appendSql( separator );
renderTableGroup( root, null ); renderRootTableGroup( root, null );
separator = COMA_SEPARATOR; separator = COMA_SEPARATOR;
} }
} }
@ -3497,9 +3572,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected void renderTableGroup(TableGroup tableGroup, List<TableGroupJoin> tableGroupJoinCollector) { protected void renderRootTableGroup(TableGroup tableGroup, List<TableGroupJoin> tableGroupJoinCollector) {
final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() ); 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 ); renderTableReferenceJoins( tableGroup );
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector ); processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
@ -3537,7 +3615,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
} }
final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() ); final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() );
final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode ); final boolean usesLockHint = renderPrimaryTableReference( tableGroup, effectiveLockMode );
final List<TableGroupJoin> tableGroupJoins; final List<TableGroupJoin> tableGroupJoins;
if ( realTableGroup ) { if ( realTableGroup ) {
@ -3559,8 +3637,22 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
tableGroupJoins = null; tableGroupJoins = null;
} }
appendSql( " on " ); if ( predicate != null ) {
predicate.accept( this ); 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 ) { if ( !realTableGroup ) {
renderTableReferenceJoins( tableGroup ); renderTableReferenceJoins( tableGroup );
@ -3619,10 +3711,30 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
return false; 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 ) { if ( tableReference instanceof NamedTableReference ) {
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode ); 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 ); tableReference.accept( this );
return false; return false;
} }
@ -3633,11 +3745,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = clauseStack.getCurrent(); final Clause currentClause = clauseStack.getCurrent();
if ( rendersTableReferenceAlias( currentClause ) ) { if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable(); renderTableReferenceIdentificationVariable( tableReference );
if ( identificationVariable != null ) {
appendSql( WHITESPACE );
appendSql( identificationVariable );
}
} }
return false; return false;
} }
@ -3662,6 +3770,48 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
renderDerivedTableReference( tableReference ); renderDerivedTableReference( tableReference );
} }
protected void emulateQueryPartTableReferenceColumnAliasing(QueryPartTableReference tableReference) {
final List<String> columnAliases = this.columnAliases;
this.columnAliases = tableReference.getColumnNames();
tableReference.getQueryPart().accept( this );
this.columnAliases = columnAliases;
renderTableReferenceIdentificationVariable( tableReference );
}
protected void emulateValuesTableReferenceColumnAliasing(ValuesTableReference tableReference) {
final List<Values> valuesList = tableReference.getValuesList();
append( '(' );
final Stack<Clause> 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) { protected void renderDerivedTableReference(DerivedTableReference tableReference) {
final String identificationVariable = tableReference.getIdentificationVariable(); final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) { if ( identificationVariable != null ) {
@ -3678,6 +3828,14 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> 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) { public static boolean rendersTableReferenceAlias(Clause clause) {
// todo (6.0) : For now we just skip the alias rendering in the delete and update clauses // 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 // We need some dialect support if we want to support joins in delete and update statements
@ -3762,13 +3920,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
appendSql( "join " ); appendSql( "join " );
final Predicate predicate; final Predicate predicate;
if ( tableGroupJoin.isLateral() ) { if ( tableGroupJoin.getPredicate() == null ) {
append( "lateral " ); if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) {
if ( tableGroupJoin.getPredicate() == null ) { predicate = null;
predicate = new Junction( Junction.Nature.CONJUNCTION );
} }
else { else {
predicate = tableGroupJoin.getPredicate(); predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) );
} }
} }
else { else {
@ -3778,10 +3935,158 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector ); renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
} }
else { 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<String> columnNames = tableReference.getColumnNames();
final List<ColumnReference> columnReferences = new ArrayList<>( columnNames.size() );
final List<ColumnReference> 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<QueryPart> 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<QueryPart> 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 @Override
public void visitTableGroup(TableGroup tableGroup) { public void visitTableGroup(TableGroup tableGroup) {
// TableGroup and TableGroup handling should be performed as part of `#visitFromClause`... // TableGroup and TableGroup handling should be performed as part of `#visitFromClause`...
@ -4555,7 +4860,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
SubQueryRelationalRestrictionEmulationRenderer<X> renderer, SubQueryRelationalRestrictionEmulationRenderer<X> renderer,
ComparisonOperator tupleComparisonOperator) { ComparisonOperator tupleComparisonOperator) {
final QuerySpec subQuery; 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; subQuery = (QuerySpec) queryPart;
// We can only emulate the tuple sub query predicate as exists predicate when there are no limit/offsets // We can only emulate the tuple sub query predicate as exists predicate when there are no limit/offsets
if ( negated ) { if ( negated ) {
@ -4573,8 +4879,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
appendSql( "exists (select 1" ); appendSql( "exists (select 1" );
visitFromClause( subQuery.getFromClause() ); visitFromClause( subQuery.getFromClause() );
if ( !subQuery.getGroupByClauseExpressions() if ( !subQuery.getGroupByClauseExpressions().isEmpty()
.isEmpty() || subQuery.getHavingClauseRestrictions() != null ) { || subQuery.getHavingClauseRestrictions() != null ) {
// If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause // If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause
visitWhereClause( subQuery.getWhereClauseRestrictions() ); visitWhereClause( subQuery.getWhereClauseRestrictions() );
visitGroupByClause( subQuery, SelectItemReferenceStrategy.EXPRESSION ); visitGroupByClause( subQuery, SelectItemReferenceStrategy.EXPRESSION );
@ -5014,6 +5320,18 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
return true; return true;
} }
/**
* Is this SQL dialect known to support some kind of distinct from predicate.
* <p/>
* 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 * Is this dialect known to support what ANSI-SQL terms "row value
* constructor" syntax; sometimes called tuple syntax. * constructor" syntax; sometimes called tuple syntax.

View File

@ -65,6 +65,16 @@ public class AggregateFunctionChecker extends AbstractSqlAstWalker {
private static class AggregateFunctionException extends RuntimeException {} 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) { public static boolean hasAggregateFunctions(QuerySpec querySpec) {
try { try {
querySpec.getSelectClause().accept( INSTANCE ); querySpec.getSelectClause().accept( INSTANCE );

View File

@ -10,12 +10,10 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer; 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.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.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; 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.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
@ -25,92 +23,29 @@ import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CteTableGroup implements TableGroup { public class CteTableGroup extends AbstractTableGroup {
private final NavigablePath navigablePath;
private final NamedTableReference cteTableReference; private final NamedTableReference cteTableReference;
@SuppressWarnings("WeakerAccess")
public CteTableGroup(NamedTableReference cteTableReference) { 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; 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<TableGroupJoin> getTableGroupJoins() {
return Collections.emptyList();
}
@Override
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
}
@Override @Override
public String getGroupAlias() { public String getGroupAlias() {
return null; return cteTableReference.getIdentificationVariable();
}
@Override
public void addTableGroupJoin(TableGroupJoin join) {
throw new UnsupportedOperationException( );
}
@Override
public void addNestedTableGroupJoin(TableGroupJoin join) {
throw new UnsupportedOperationException( );
} }
@Override @Override

View File

@ -9,10 +9,12 @@ package org.hibernate.sql.ast.tree.from;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase; 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 { public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifier implements TableGroup {
private final boolean canUseInnerJoins; private final boolean canUseInnerJoins;
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final TableGroupProducer producer; private final ModelPartContainer modelPartContainer;
private final String sourceAlias; private final String sourceAlias;
private final SqlAliasBase sqlAliasBase; private final SqlAliasBase sqlAliasBase;
@ -35,14 +37,14 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
public AbstractTableGroup( public AbstractTableGroup(
boolean canUseInnerJoins, boolean canUseInnerJoins,
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroupProducer producer, ModelPartContainer modelPartContainer,
String sourceAlias, String sourceAlias,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
super(); super();
this.canUseInnerJoins = canUseInnerJoins; this.canUseInnerJoins = canUseInnerJoins;
this.navigablePath = navigablePath; this.navigablePath = navigablePath;
this.producer = producer; this.modelPartContainer = modelPartContainer;
this.sourceAlias = sourceAlias; this.sourceAlias = sourceAlias;
this.sqlAliasBase = sqlAliasBase; this.sqlAliasBase = sqlAliasBase;
this.sessionFactory = sessionFactory; this.sessionFactory = sessionFactory;
@ -59,12 +61,12 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
@Override @Override
public String getGroupAlias() { public String getGroupAlias() {
return sqlAliasBase.getAliasStem(); return sqlAliasBase == null ? null : sqlAliasBase.getAliasStem();
} }
@Override @Override
public TableGroupProducer getModelPart() { public ModelPartContainer getModelPart() {
return producer; return modelPartContainer;
} }
@Override @Override
@ -111,6 +113,29 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
tableGroupJoins.add( join ); 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 @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {
if ( nestedTableGroupJoins == null ) { if ( nestedTableGroupJoins == null ) {

View File

@ -56,6 +56,11 @@ public class CorrelatedTableGroup extends AbstractTableGroup {
super.addTableGroupJoin( join ); super.addTableGroupJoin( join );
} }
@Override
public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) {
throw new UnsupportedOperationException();
}
@Override @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {
assert !getTableGroupJoins().contains( join ); assert !getTableGroupJoins().contains( join );

View File

@ -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> T unwrap(Class<T> 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<TableGroupJoin> getTableGroupJoins() {
return getTableGroup().getTableGroupJoins();
}
@Override
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
getTableGroup().visitTableGroupJoins( consumer );
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
getTableGroup().visitNestedTableGroupJoins( consumer );
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
getTableGroup().applyAffectedTableNames( nameCollector );
}
@Override
public TableReference getPrimaryTableReference() {
return getTableGroup().getPrimaryTableReference();
}
@Override
public List<TableReferenceJoin> 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();
}
}

View File

@ -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<String> 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<String> nameCollector) {
functionTableReference.applyAffectedTableNames( nameCollector );
}
@Override
public FunctionTableReference getPrimaryTableReference() {
return functionTableReference;
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return Collections.emptyList();
}
}

View File

@ -8,8 +8,8 @@ package org.hibernate.sql.ast.tree.from;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -25,7 +25,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase;
* *
* @author Christian Beikov * @author Christian Beikov
*/ */
public class LazyTableGroup extends AbstractColumnReferenceQualifier implements TableGroup { public class LazyTableGroup extends DelegatingTableGroup {
private final boolean canUseInnerJoins; private final boolean canUseInnerJoins;
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
@ -33,7 +33,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
private final TableGroupProducer producer; private final TableGroupProducer producer;
private final String sourceAlias; private final String sourceAlias;
private final SqlAliasBase sqlAliasBase; private final SqlAliasBase sqlAliasBase;
private final SessionFactoryImplementor sessionFactory;
private final Supplier<TableGroup> tableGroupSupplier; private final Supplier<TableGroup> tableGroupSupplier;
private final TableGroup parentTableGroup; private final TableGroup parentTableGroup;
private final BiPredicate<NavigablePath, String> navigablePathChecker; private final BiPredicate<NavigablePath, String> navigablePathChecker;
@ -60,14 +59,13 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
this.tableGroupSupplier = tableGroupSupplier; this.tableGroupSupplier = tableGroupSupplier;
this.navigablePathChecker = navigablePathChecker; this.navigablePathChecker = navigablePathChecker;
this.parentTableGroup = parentTableGroup; this.parentTableGroup = parentTableGroup;
this.sessionFactory = sessionFactory;
} }
public TableGroup getUnderlyingTableGroup() { public TableGroup getUnderlyingTableGroup() {
return tableGroup; return tableGroup;
} }
@Override
public TableGroup getTableGroup() { public TableGroup getTableGroup() {
if ( tableGroup != null ) { if ( tableGroup != null ) {
return tableGroup; return tableGroup;
@ -97,11 +95,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
} }
} }
@Override
public TableReference getPrimaryTableReference() {
return getTableGroup().getPrimaryTableReference();
}
@Override @Override
public List<TableReferenceJoin> getTableReferenceJoins() { public List<TableReferenceJoin> getTableReferenceJoins() {
return tableGroup == null ? Collections.emptyList() : tableGroup.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(); 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 @Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) { public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( tableGroup != null ) { if ( tableGroup != null ) {
@ -146,11 +129,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
return canUseInnerJoins; return canUseInnerJoins;
} }
@Override
public boolean isLateral() {
return false;
}
@Override @Override
public NavigablePath getNavigablePath() { public NavigablePath getNavigablePath() {
return navigablePath; return navigablePath;
@ -176,11 +154,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
return sourceAlias; return sourceAlias;
} }
@Override
protected SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
@Override @Override
public boolean isRealTableGroup() { public boolean isRealTableGroup() {
return tableGroup != null && tableGroup.isRealTableGroup(); return tableGroup != null && tableGroup.isRealTableGroup();
@ -192,6 +165,39 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
} }
@Override @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( protected TableReference getTableReferenceInternal(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,

View File

@ -17,7 +17,7 @@ import org.hibernate.query.NavigablePath;
/** /**
* @author Christian Beikov * @author Christian Beikov
*/ */
public class MappedByTableGroup implements VirtualTableGroup { public class MappedByTableGroup extends DelegatingTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final ModelPartContainer modelPart; private final ModelPartContainer modelPart;
@ -41,6 +41,11 @@ public class MappedByTableGroup implements VirtualTableGroup {
this.navigablePathChecker = navigablePathChecker; this.navigablePathChecker = navigablePathChecker;
} }
@Override
protected TableGroup getTableGroup() {
return underlyingTableGroup;
}
@Override @Override
public NavigablePath getNavigablePath() { public NavigablePath getNavigablePath() {
return navigablePath; return navigablePath;
@ -67,9 +72,17 @@ public class MappedByTableGroup implements VirtualTableGroup {
return modelPart; 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 @Override
public String getSourceAlias() { public boolean isRealTableGroup() {
return underlyingTableGroup.getSourceAlias(); return false;
}
@Override
public boolean isLateral() {
return false;
} }
@Override @Override
@ -82,26 +95,6 @@ public class MappedByTableGroup implements VirtualTableGroup {
return Collections.emptyList(); 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 @Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) { public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
// No-op // No-op
@ -112,19 +105,9 @@ public class MappedByTableGroup implements VirtualTableGroup {
// No-op // No-op
} }
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
underlyingTableGroup.applyAffectedTableNames( nameCollector );
}
@Override
public TableReference getPrimaryTableReference() {
return underlyingTableGroup.getPrimaryTableReference();
}
@Override @Override
public List<TableReferenceJoin> getTableReferenceJoins() { public List<TableReferenceJoin> getTableReferenceJoins() {
return underlyingTableGroup.getTableReferenceJoins(); return Collections.emptyList();
} }
@Override @Override

View File

@ -108,6 +108,11 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) {
throw new UnsupportedOperationException();
}
@Override @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View File

@ -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 @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {
if ( join.getJoinedGroup() != elementTableGroup ) { if ( join.getJoinedGroup() != elementTableGroup ) {

View File

@ -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<String> 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<String> nameCollector) {
queryPartTableReference.applyAffectedTableNames( nameCollector );
}
@Override
public QueryPartTableReference getPrimaryTableReference() {
return queryPartTableReference;
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return Collections.emptyList();
}
}

View File

@ -6,8 +6,6 @@
*/ */
package org.hibernate.sql.ast.tree.from; package org.hibernate.sql.ast.tree.from;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -17,109 +15,47 @@ import org.hibernate.query.NavigablePath;
/** /**
* @author Christian Beikov * @author Christian Beikov
*/ */
public class StandardVirtualTableGroup implements VirtualTableGroup { public class StandardVirtualTableGroup extends AbstractTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath;
private final ModelPartContainer modelPart;
private final TableGroup underlyingTableGroup; private final TableGroup underlyingTableGroup;
private final boolean fetched; private final boolean fetched;
private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> nestedTableGroupJoins;
public StandardVirtualTableGroup( public StandardVirtualTableGroup(
NavigablePath navigablePath, NavigablePath navigablePath,
ModelPartContainer modelPart, ModelPartContainer modelPart,
TableGroup underlyingTableGroup, TableGroup underlyingTableGroup,
boolean fetched) { boolean fetched) {
this.navigablePath = navigablePath; super(
this.modelPart = modelPart; underlyingTableGroup.canUseInnerJoins(),
navigablePath,
modelPart,
underlyingTableGroup.getSourceAlias(),
null,
null
);
this.underlyingTableGroup = underlyingTableGroup; this.underlyingTableGroup = underlyingTableGroup;
this.fetched = fetched; this.fetched = fetched;
} }
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override @Override
public ModelPartContainer getExpressionType() { public ModelPartContainer getExpressionType() {
return getModelPart(); return getModelPart();
} }
@Override
public String getGroupAlias() {
// none, although we could also delegate to the underlyingTableGroup's group-alias
return null;
}
@Override @Override
public boolean isFetched() { public boolean isFetched() {
return fetched; return fetched;
} }
@Override
public ModelPartContainer getModelPart() {
return modelPart;
}
@Override @Override
public String getSourceAlias() { public String getSourceAlias() {
return underlyingTableGroup.getSourceAlias(); return underlyingTableGroup.getSourceAlias();
} }
@Override
public List<TableGroupJoin> getTableGroupJoins() {
return tableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( tableGroupJoins );
}
@Override
public List<TableGroupJoin> getNestedTableGroupJoins() {
return nestedTableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( nestedTableGroupJoins );
}
@Override
public boolean isRealTableGroup() {
return nestedTableGroupJoins != null && !nestedTableGroupJoins.isEmpty();
}
@Override @Override
public boolean canUseInnerJoins() { public boolean canUseInnerJoins() {
return underlyingTableGroup.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<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
tableGroupJoins.forEach( consumer );
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( nestedTableGroupJoins != null ) {
nestedTableGroupJoins.forEach( consumer );
}
}
@Override @Override
public void applyAffectedTableNames(Consumer<String> nameCollector) { public void applyAffectedTableNames(Consumer<String> nameCollector) {
underlyingTableGroup.applyAffectedTableNames( nameCollector ); underlyingTableGroup.applyAffectedTableNames( nameCollector );
@ -136,25 +72,7 @@ public class StandardVirtualTableGroup implements VirtualTableGroup {
} }
@Override @Override
public TableReference resolveTableReference( public TableReference getTableReferenceInternal(
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(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization, boolean allowFkOptimization,
@ -168,23 +86,7 @@ public class StandardVirtualTableGroup implements VirtualTableGroup {
if ( tableReference != null ) { if ( tableReference != null ) {
return tableReference; return tableReference;
} }
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
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;
} }
} }

View File

@ -47,12 +47,16 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
boolean canUseInnerJoins(); boolean canUseInnerJoins();
default boolean isLateral() { default boolean isLateral() {
return getPrimaryTableReference() instanceof DerivedTableReference return false;
&& ( (DerivedTableReference) getPrimaryTableReference() ).isLateral();
} }
void addTableGroupJoin(TableGroupJoin join); 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, * 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, * 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 @Override
default void accept(SqlAstWalker sqlTreeWalker) { default void accept(SqlAstWalker sqlTreeWalker) {
sqlTreeWalker.visitTableGroup( this ); sqlTreeWalker.visitTableGroup( this );

View File

@ -19,14 +19,7 @@ import org.hibernate.query.NavigablePath;
/** /**
* @author Andrea Boriero * @author Andrea Boriero
*/ */
public class UnionTableGroup implements VirtualTableGroup { public class UnionTableGroup extends AbstractTableGroup implements VirtualTableGroup {
private final boolean canUseInnerJoins;
private final NavigablePath navigablePath;
private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> nestedTableGroupJoins;
private final UnionSubclassEntityPersister modelPart;
private final String sourceAlias;
private final UnionTableReference tableReference; private final UnionTableReference tableReference;
public UnionTableGroup( public UnionTableGroup(
@ -35,88 +28,8 @@ public class UnionTableGroup implements VirtualTableGroup {
UnionTableReference tableReference, UnionTableReference tableReference,
UnionSubclassEntityPersister modelPart, UnionSubclassEntityPersister modelPart,
String sourceAlias) { String sourceAlias) {
this.canUseInnerJoins = canUseInnerJoins; super( canUseInnerJoins, navigablePath, modelPart, sourceAlias, null, null );
this.navigablePath = navigablePath;
this.tableReference = tableReference; 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<TableGroupJoin> getTableGroupJoins() {
return tableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( tableGroupJoins );
}
@Override
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
tableGroupJoins.forEach( consumer );
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( nestedTableGroupJoins != null ) {
nestedTableGroupJoins.forEach( consumer );
}
} }
@Override @Override
@ -134,40 +47,14 @@ public class UnionTableGroup implements VirtualTableGroup {
} }
@Override @Override
public TableReference getTableReference( public TableReference getTableReferenceInternal(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization, boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
return resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) {
}
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, true ) != null ) {
return tableReference; return tableReference;
} }
if ( nestedTableGroupJoins != null ) { return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
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;
} }
} }

View File

@ -48,7 +48,7 @@ public class ValuesTableGroup extends AbstractTableGroup {
String tableExpression, String tableExpression,
boolean allowFkOptimization, boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( getModelPart().containsTableReference( tableExpression ) ) { if ( ( (TableGroupProducer) getModelPart() ).containsTableReference( tableExpression ) ) {
return getPrimaryTableReference(); return getPrimaryTableReference();
} }
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {

View File

@ -16,6 +16,11 @@ import org.hibernate.sql.ast.tree.expression.Expression;
public class BooleanExpressionPredicate extends AbstractPredicate { public class BooleanExpressionPredicate extends AbstractPredicate {
private final Expression expression; private final Expression expression;
public BooleanExpressionPredicate(Expression expression) {
super( expression.getExpressionType(), false );
this.expression = expression;
}
public BooleanExpressionPredicate(Expression expression, boolean negated, JdbcMappingContainer expressionType) { public BooleanExpressionPredicate(Expression expression, boolean negated, JdbcMappingContainer expressionType) {
super( expressionType, negated ); super( expressionType, negated );
this.expression = expression; this.expression = expression;

View File

@ -48,6 +48,9 @@ public class MapIndexFormulaTest {
session.createQuery( session.createQuery(
"from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada'" ) "from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada'" )
.list(); .list();
session.createQuery(
"from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada' and maxindex(u) = 'nada'" )
.list();
} }
); );
} }

View File

@ -8,10 +8,7 @@
package org.hibernate.envers.function; package org.hibernate.envers.function;
import java.util.List; 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.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.ordering.OrderByFragment; import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; 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.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.sql.ast.spi.SqlAstQueryPartProcessingState; 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.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; 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.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.type.spi.TypeConfiguration; 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 TableGroup delegate;
private final String auditTableExpression; private final String auditTableExpression;
@ -123,8 +119,8 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
} }
@Override @Override
public ModelPart getExpressionType() { protected TableGroup getTableGroup() {
return delegate.getExpressionType(); return delegate;
} }
@Override @Override
@ -135,7 +131,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
if ( tableExpression.equals( normalTableExpression ) ) { if ( tableExpression.equals( normalTableExpression ) ) {
tableExpression = auditTableExpression; tableExpression = auditTableExpression;
} }
return delegate.resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); return super.resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
} }
@Override @Override
@ -147,77 +143,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
if ( tableExpression.equals( normalTableExpression ) ) { if ( tableExpression.equals( normalTableExpression ) ) {
tableExpression = auditTableExpression; tableExpression = auditTableExpression;
} }
return delegate.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); return super.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<TableGroupJoin> getTableGroupJoins() {
return delegate.getTableGroupJoins();
}
@Override
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
delegate.visitTableGroupJoins( consumer );
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
delegate.visitNestedTableGroupJoins( consumer );
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
delegate.applyAffectedTableNames( nameCollector );
}
@Override
public TableReference getPrimaryTableReference() {
return delegate.getPrimaryTableReference();
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return delegate.getTableReferenceJoins();
} }
} }
} }