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 );
}
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 4, 0 );
}
@Override
public String translateExtractField(TemporalUnit unit) {
switch ( unit ) {

View File

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

View File

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

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

View File

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

View File

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

View File

@ -11,7 +11,6 @@ import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.SemanticException;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection;
@ -19,10 +18,11 @@ import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart;
@ -70,13 +70,12 @@ public class TimesTenSqlAstTranslator<T extends JdbcOperation> extends AbstractS
}
final Predicate predicate;
if ( tableGroupJoin.isLateral() ) {
append( "lateral " );
if ( tableGroupJoin.getPredicate() == null ) {
predicate = new Junction( Junction.Nature.CONJUNCTION );
if ( tableGroupJoin.getPredicate() == null ) {
if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) {
predicate = null;
}
else {
predicate = tableGroupJoin.getPredicate();
predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) );
}
}
else {
@ -86,7 +85,7 @@ public class TimesTenSqlAstTranslator<T extends JdbcOperation> extends AbstractS
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
}
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.jdbc.*;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.*;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
@ -228,6 +229,26 @@ public abstract class AbstractHANADialect extends Dialect {
return false;
}
protected static DatabaseVersion createVersion(DialectResolutionInfo info) {
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
final String versionString = info.getDatabaseVersion();
int majorVersion = 1;
int minorVersion = 0;
int patchLevel = 0;
final String[] components = versionString.split( "\\." );
if ( components.length >= 3 ) {
try {
majorVersion = Integer.parseInt( components[0] );
minorVersion = Integer.parseInt( components[1] );
patchLevel = Integer.parseInt( components[2] );
}
catch (NumberFormatException ex) {
// Ignore
}
}
return DatabaseVersion.make( majorVersion, minorVersion, patchLevel );
}
@Override
public String castPattern(CastType from, CastType to) {
if ( to == CastType.BOOLEAN ) {
@ -991,6 +1012,11 @@ public abstract class AbstractHANADialect extends Dialect {
return true;
}
@Override
public boolean supportsLateral() {
return getVersion().isSameOrAfter( 2, 0, 40 );
}
@Override
public boolean supportsNoWait() {
return true;

View File

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

View File

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

View File

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

View File

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

View File

@ -6,11 +6,18 @@
*/
package org.hibernate.dialect;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.exec.spi.JdbcOperation;
@ -50,4 +57,24 @@ public class DB2zSqlAstTranslator<T extends JdbcOperation> extends DB2SqlAstTran
renderComparisonStandard( lhs, operator, rhs );
}
@Override
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
final TableReference tableReference = tableGroup.getPrimaryTableReference();
if ( tableReference instanceof NamedTableReference ) {
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
}
// DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries
append( "table " );
tableReference.accept( this );
return false;
}
@Override
public void visitFunctionTableReference(FunctionTableReference tableReference) {
// For the table qualifier we need parenthesis on DB2 z/OS
append( OPEN_PARENTHESIS );
tableReference.getFunctionExpression().accept( this );
append( CLOSE_PARENTHESIS );
renderDerivedTableReference( tableReference );
}
}

View File

@ -3503,6 +3503,16 @@ public abstract class Dialect implements ConversionContext {
return false;
}
/**
* Does this dialect support the SQL lateral keyword or a proprietary alternative=
*
* @return {@code true} if the underlying database supports lateral,
* {@code false} otherwise. The default is {@code false}.
*/
public boolean supportsLateral() {
return false;
}
public CallableStatementSupport getCallableStatementSupport() {
// most databases do not support returning cursors (ref_cursor)...
return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE;

View File

@ -6,11 +6,6 @@
*/
package org.hibernate.dialect;
import java.sql.Types;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.type.StandardBasicTypes;
/**
* An SQL dialect for the SAP HANA Cloud column store.
* <p>
@ -30,7 +25,29 @@ import org.hibernate.type.StandardBasicTypes;
public class HANACloudColumnStoreDialect extends HANAColumnStoreDialect {
public HANACloudColumnStoreDialect() {
// No idea how the versioning scheme is here, but since this is deprecated anyway, keep it as is
super( DatabaseVersion.make( 4 ) );
}
@Override
public boolean supportsLateral() {
// Couldn't find a reference since when this is supported
return true;
}
@Override
protected boolean supportsAsciiStringTypes() {
return getVersion().isBefore( 4 );
}
@Override
protected Boolean useUnicodeStringTypesDefault() {
return getVersion().isSameOrAfter( 4 );
}
@Override
public boolean isUseUnicodeStringTypes() {
return getVersion().isSameOrAfter( 4 ) || super.isUseUnicodeStringTypes();
}
}

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

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.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.Values;
@ -248,48 +250,20 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
@Override
public void visitValuesTableReference(ValuesTableReference tableReference) {
final List<Values> valuesList = tableReference.getValuesList();
if ( valuesList.size() < 2 ) {
super.visitValuesTableReference( tableReference );
}
else {
append( '(' );
// Oracle doesn't support a multi-values insert
// So we render a select union emulation instead
final Stack<Clause> clauseStack = getClauseStack();
clauseStack.push( Clause.VALUES );
try {
// We render the first select statement with aliases
clauseStack.push( Clause.SELECT );
emulateValuesTableReferenceColumnAliasing( tableReference );
}
try {
appendSql( "select " );
@Override
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
emulateQueryPartTableReferenceColumnAliasing( tableReference );
}
renderCommaSeparatedSelectExpression(
valuesList.get( 0 ).getExpressions(),
tableReference.getColumnNames()
);
appendSql( getFromDualForSelectOnly() );
}
finally {
clauseStack.pop();
}
// The others, without the aliases
for ( int i = 1; i < valuesList.size(); i++ ) {
appendSql( " union all " );
renderExpressionsAsSubquery( valuesList.get( i ).getExpressions() );
}
}
finally {
clauseStack.pop();
}
append( ')' );
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
append( WHITESPACE );
append( tableReference.getIdentificationVariable() );
}
}
@Override
public void visitFunctionTableReference(FunctionTableReference tableReference) {
append( "table(" );
tableReference.getFunctionExpression().accept( this );
append( CLOSE_PARENTHESIS );
renderTableReferenceIdentificationVariable( tableReference );
}
@Override

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

@ -166,7 +166,7 @@ public class DynamicResultBuilderEntityStandard
if ( lockMode != null ) {
domainResultCreationState.getSqlAstCreationState().registerLockMode( tableAlias, lockMode );
}
return new TableGroupImpl( navigablePath, tableAlias, tableReference, entityMapping, tableAlias );
return new TableGroupImpl( navigablePath, tableAlias, tableReference, entityMapping );
}
);
final TableReference tableReference = tableGroup.getPrimaryTableReference();

View File

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

View File

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

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.DiscriminatorSqmPath;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.query.FetchClauseType;
import org.hibernate.query.SortOrder;
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
@ -108,6 +110,8 @@ import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression;
import org.hibernate.sql.ast.tree.from.CorrelatedPluralTableGroup;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
import org.hibernate.sql.ast.tree.from.QueryPartTableGroup;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
@ -3221,22 +3225,22 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public Expression visitMaxElementPath(SqmMaxElementPath<?> path) {
return createCorrelatedAggregateSubQuery( path, false, true );
return createMinOrMaxIndexOrElement( path, false, true );
}
@Override
public Expression visitMinElementPath(SqmMinElementPath<?> path) {
return createCorrelatedAggregateSubQuery( path, false, false );
return createMinOrMaxIndexOrElement( path, false, false );
}
@Override
public Expression visitMaxIndexPath(SqmMaxIndexPath<?> path) {
return createCorrelatedAggregateSubQuery( path, true, true );
return createMinOrMaxIndexOrElement( path, true, true );
}
@Override
public Expression visitMinIndexPath(SqmMinIndexPath<?> path) {
return createCorrelatedAggregateSubQuery( path, true, false );
return createMinOrMaxIndexOrElement( path, true, false );
}
@Override
@ -3388,6 +3392,19 @@ public abstract class BaseSqmToSqlAstConverter<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(
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
boolean index,
@ -3497,6 +3514,219 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return subQuerySpec;
}
protected Expression createLateralJoinExpression(
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
boolean index,
boolean max) {
prepareReusablePath( pluralPartPath.getLhs(), () -> null );
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
pluralPartPath.getPluralDomainPath() );
final FromClauseAccess parentFromClauseAccess = getFromClauseAccess();
final TableGroup parentTableGroup = parentFromClauseAccess.findTableGroup(
pluralPartPath.getNavigablePath().getParent()
);
final CollectionPart collectionPart = index
? pluralAttributeMapping.getIndexDescriptor()
: pluralAttributeMapping.getElementDescriptor();
final ModelPart modelPart;
if ( collectionPart instanceof EntityAssociationMapping ) {
modelPart = ( (EntityAssociationMapping) collectionPart ).getKeyTargetMatchPart();
}
else {
modelPart = collectionPart;
}
final int jdbcTypeCount = modelPart.getJdbcTypeCount();
final String pathName = ( max ? "max" : "min" ) + ( index ? "_index" : "_element" );
final String identifierVariable = parentTableGroup.getPrimaryTableReference().getIdentificationVariable()
+ "_" + pathName;
final NavigablePath queryPath = new NavigablePath( parentTableGroup.getNavigablePath(), pathName, identifierVariable );
TableGroup lateralTableGroup = parentFromClauseAccess.findTableGroup( queryPath );
if ( lateralTableGroup == null ) {
final QuerySpec subQuerySpec = new QuerySpec( false );
pushProcessingState(
new SqlAstQueryPartProcessingStateImpl(
subQuerySpec,
getCurrentProcessingState(),
this,
currentClauseStack::getCurrent
)
);
try {
final TableGroup tableGroup = pluralAttributeMapping.createRootTableGroup(
true,
pluralPartPath.getNavigablePath(),
null,
() -> subQuerySpec::applyPredicate,
this,
creationContext
);
final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate(
getLoadQueryInfluencers(),
(Joinable) pluralAttributeMapping.getCollectionDescriptor(),
tableGroup
);
if ( filterPredicate != null ) {
subQuerySpec.applyPredicate( filterPredicate );
}
getFromClauseAccess().registerTableGroup( pluralPartPath.getNavigablePath(), tableGroup );
registerPluralTableGroupParts( tableGroup );
subQuerySpec.getFromClause().addRoot( tableGroup );
final List<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) {
final SqmPath<?> lhs = path.getLhs();
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.internal.SqlFragmentPredicate;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.SetOperator;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
@ -114,9 +115,11 @@ import org.hibernate.sql.ast.tree.from.FromClause;
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableGroup;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
@ -194,6 +197,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
private final Set<String> affectedTableNames = new HashSet<>();
private MutationStatement dmlStatement;
private boolean needsSelectAliases;
// Column aliases that need to be injected
private List<String> columnAliases;
private Predicate additionalWherePredicate;
// We must reset the queryPartForRowNumbering fields to null if a query part is visited that does not
// contribute to the row numbering i.e. if the query part is a sub-query in the where clause.
@ -206,6 +211,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
private transient AbstractSqmSelfRenderingFunctionDescriptor castFunction;
private transient LazySessionWrapperOptions lazySessionWrapperOptions;
private transient BasicType<Integer> integerType;
private transient BasicType<Boolean> booleanType;
private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT;
@ -258,6 +264,15 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
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.
* Usually, only the {@link WrapperOptions} interface is needed,
@ -1418,9 +1433,13 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
this.queryPartForRowNumberingClauseDepth = -1;
this.needsSelectAliases = false;
}
final boolean needsParenthesis = !queryGroup.isRoot();
if ( needsParenthesis ) {
appendSql( OPEN_PARENTHESIS );
}
// If we are row numbering the current query group, this means that we can't render the
// order by and offset fetch clause, so we must do row counting on the query group level
if ( queryPartForRowNumbering == queryGroup ) {
if ( queryPartForRowNumbering == queryGroup || additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) {
this.needsSelectAliases = true;
queryGroupAlias = "grp_" + queryGroupAliasCounter + '_';
queryGroupAliasCounter++;
@ -1470,6 +1489,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
if ( queryGroupAlias != null ) {
appendSql( ") " );
appendSql( queryGroupAlias );
if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) {
visitWhereClause( additionalWherePredicate );
}
}
if ( needsParenthesis ) {
appendSql( CLOSE_PARENTHESIS );
}
}
finally {
@ -1504,7 +1529,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
if ( currentQueryPart instanceof QueryGroup ) {
// We always need query wrapping if we are in a query group and this query spec has a fetch clause
// because of order by precedence in SQL
if ( needsParenthesis = querySpec.hasOffsetOrFetchClause() ) {
needsParenthesis = querySpec.hasOffsetOrFetchClause();
if ( needsParenthesis ) {
// If the parent is a query group with a fetch clause,
// or if the database does not support simple query grouping, we must use a select wrapper
if ( !supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() ) {
@ -2849,7 +2875,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
if ( !needsParenthesis || queryPart.isRoot() ) {
appendSql( CLOSE_PARENTHESIS );
}
appendSql( WHITESPACE);
appendSql( WHITESPACE );
appendSql( alias );
appendSql( " where " );
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..
if ( offsetExpression == null ) {
final Predicate additionalWherePredicate = this.additionalWherePredicate;
if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) {
this.additionalWherePredicate = null;
appendSql( " and " );
additionalWherePredicate.accept( this );
}
if ( queryPart.isRoot() ) {
switch ( fetchClauseType ) {
case PERCENT_ONLY:
@ -2929,6 +2961,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
appendSql( alias );
appendSql( ".rn>" );
offsetExpression.accept( this );
final Predicate additionalWherePredicate = this.additionalWherePredicate;
if ( additionalWherePredicate != null && !additionalWherePredicate.isEmpty() ) {
this.additionalWherePredicate = null;
appendSql( " and " );
additionalWherePredicate.accept( this );
}
if ( queryPart.isRoot() ) {
appendSql( " order by " );
appendSql( alias );
@ -3010,6 +3048,46 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
final SqlAstNodeRenderingMode original = parameterRenderingMode;
if ( needsSelectAliases || referenceStrategy == SelectItemReferenceStrategy.ALIAS && hasSelectAliasInGroupByClause() ) {
String separator = NO_SEPARATOR;
if ( columnAliases == null ) {
for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i );
appendSql( separator );
if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) {
parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS;
}
else {
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
}
visitSqlSelection( sqlSelection );
parameterRenderingMode = original;
appendSql( " c" );
appendSql( i );
separator = COMA_SEPARATOR;
}
}
else {
for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i );
appendSql( separator );
if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) {
parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS;
}
else {
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
}
visitSqlSelection( sqlSelection );
parameterRenderingMode = original;
appendSql( WHITESPACE );
appendSql( columnAliases.get( i ) );
separator = COMA_SEPARATOR;
}
}
if ( queryPartForRowNumbering != null ) {
renderRowNumberingSelectItems( selectClause, queryPartForRowNumbering );
}
}
else if ( columnAliases == null ) {
String separator = NO_SEPARATOR;
for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i );
@ -3022,13 +3100,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
visitSqlSelection( sqlSelection );
parameterRenderingMode = original;
appendSql( " c" );
appendSql( i );
separator = COMA_SEPARATOR;
}
if ( queryPartForRowNumbering != null ) {
renderRowNumberingSelectItems( selectClause, queryPartForRowNumbering );
}
}
else {
String separator = NO_SEPARATOR;
@ -3042,6 +3115,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
}
visitSqlSelection( sqlSelection );
appendSql( WHITESPACE );
appendSql( columnAliases.get( i ) );
parameterRenderingMode = original;
separator = COMA_SEPARATOR;
}
@ -3486,7 +3561,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
String separator = NO_SEPARATOR;
for ( TableGroup root : fromClause.getRoots() ) {
appendSql( separator );
renderTableGroup( root, null );
renderRootTableGroup( root, null );
separator = COMA_SEPARATOR;
}
}
@ -3497,9 +3572,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
@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 boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode );
final boolean usesLockHint = renderPrimaryTableReference( tableGroup, effectiveLockMode );
if ( tableGroup.isLateral() && !getDialect().supportsLateral() ) {
addAdditionalWherePredicate( determineLateralEmulationPredicate( tableGroup ) );
}
renderTableReferenceJoins( tableGroup );
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
@ -3537,7 +3615,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() );
final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode );
final boolean usesLockHint = renderPrimaryTableReference( tableGroup, effectiveLockMode );
final List<TableGroupJoin> tableGroupJoins;
if ( realTableGroup ) {
@ -3559,8 +3637,22 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
tableGroupJoins = null;
}
appendSql( " on " );
predicate.accept( this );
if ( predicate != null ) {
appendSql( " on " );
predicate.accept( this );
}
if ( tableGroup.isLateral() && !getDialect().supportsLateral() ) {
final Predicate lateralEmulationPredicate = determineLateralEmulationPredicate( tableGroup );
if ( lateralEmulationPredicate != null ) {
if ( predicate == null ) {
appendSql( " on " );
}
else {
appendSql( " and " );
}
lateralEmulationPredicate.accept( this );
}
}
if ( !realTableGroup ) {
renderTableReferenceJoins( tableGroup );
@ -3619,10 +3711,30 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
return false;
}
protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) {
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
final TableReference tableReference = tableGroup.getPrimaryTableReference();
if ( tableReference instanceof NamedTableReference ) {
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
}
final DerivedTableReference derivedTableReference = (DerivedTableReference) tableReference;
if ( derivedTableReference.isLateral() ) {
if ( getDialect().supportsLateral() ) {
appendSql( "lateral" );
}
else if ( tableReference instanceof QueryPartTableReference ) {
final QueryPartTableReference queryPartTableReference = (QueryPartTableReference) tableReference;
final QueryPart emulationQueryPart = stripToSelectClause( queryPartTableReference.getQueryPart() );
final QueryPartTableReference emulationTableReference = new QueryPartTableReference(
emulationQueryPart,
tableReference.getIdentificationVariable(),
queryPartTableReference.getColumnNames(),
false,
sessionFactory
);
emulationTableReference.accept( this );
return false;
}
}
tableReference.accept( this );
return false;
}
@ -3633,11 +3745,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
registerAffectedTable( tableReference );
final Clause currentClause = clauseStack.getCurrent();
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( WHITESPACE );
appendSql( identificationVariable );
}
renderTableReferenceIdentificationVariable( tableReference );
}
return false;
}
@ -3662,6 +3770,48 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
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) {
final String identificationVariable = tableReference.getIdentificationVariable();
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) {
// todo (6.0) : For now we just skip the alias rendering in the delete and update clauses
// We need some dialect support if we want to support joins in delete and update statements
@ -3762,13 +3920,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
appendSql( "join " );
final Predicate predicate;
if ( tableGroupJoin.isLateral() ) {
append( "lateral " );
if ( tableGroupJoin.getPredicate() == null ) {
predicate = new Junction( Junction.Nature.CONJUNCTION );
if ( tableGroupJoin.getPredicate() == null ) {
if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) {
predicate = null;
}
else {
predicate = tableGroupJoin.getPredicate();
predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) );
}
}
else {
@ -3778,10 +3935,158 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
}
else {
renderTableGroup( tableGroupJoin.getJoinedGroup(), tableGroupJoinCollector );
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
}
}
protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) {
if ( tableGroup.getPrimaryTableReference() instanceof QueryPartTableReference ) {
final QueryPartTableReference tableReference = (QueryPartTableReference) tableGroup.getPrimaryTableReference();
final List<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
public void visitTableGroup(TableGroup tableGroup) {
// 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,
ComparisonOperator tupleComparisonOperator) {
final QuerySpec subQuery;
if ( queryPart instanceof QuerySpec && queryPart.getFetchClauseExpression() == null && queryPart.getOffsetClauseExpression() == null ) {
if ( queryPart instanceof QuerySpec && queryPart.getFetchClauseExpression() == null
&& queryPart.getOffsetClauseExpression() == null ) {
subQuery = (QuerySpec) queryPart;
// We can only emulate the tuple sub query predicate as exists predicate when there are no limit/offsets
if ( negated ) {
@ -4573,8 +4879,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
appendSql( "exists (select 1" );
visitFromClause( subQuery.getFromClause() );
if ( !subQuery.getGroupByClauseExpressions()
.isEmpty() || subQuery.getHavingClauseRestrictions() != null ) {
if ( !subQuery.getGroupByClauseExpressions().isEmpty()
|| subQuery.getHavingClauseRestrictions() != null ) {
// If we have a group by or having clause, we have to move the tuple comparison emulation to the HAVING clause
visitWhereClause( subQuery.getWhereClauseRestrictions() );
visitGroupByClause( subQuery, SelectItemReferenceStrategy.EXPRESSION );
@ -5014,6 +5320,18 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
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
* constructor" syntax; sometimes called tuple syntax.

View File

@ -65,6 +65,16 @@ public class AggregateFunctionChecker extends AbstractSqlAstWalker {
private static class AggregateFunctionException extends RuntimeException {}
public static boolean hasAggregateFunctions(Expression expression) {
try {
expression.accept( INSTANCE );
return false;
}
catch (AggregateFunctionException ex) {
return true;
}
}
public static boolean hasAggregateFunctions(QuerySpec querySpec) {
try {
querySpec.getSelectClause().accept( INSTANCE );

View File

@ -10,12 +10,10 @@ import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.tree.from.AbstractTableGroup;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
@ -25,92 +23,29 @@ import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
*
* @author Steve Ebersole
*/
public class CteTableGroup implements TableGroup {
private final NavigablePath navigablePath;
public class CteTableGroup extends AbstractTableGroup {
private final NamedTableReference cteTableReference;
@SuppressWarnings("WeakerAccess")
public CteTableGroup(NamedTableReference cteTableReference) {
this.navigablePath = new NavigablePath( cteTableReference.getTableExpression() );
this( false, cteTableReference );
}
@SuppressWarnings("WeakerAccess")
public CteTableGroup(boolean canUseInnerJoins, NamedTableReference cteTableReference) {
super(
canUseInnerJoins,
new NavigablePath( cteTableReference.getTableExpression() ),
null,
cteTableReference.getIdentificationVariable(),
null,
null
);
this.cteTableReference = cteTableReference;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public String getSourceAlias() {
return null;
}
@Override
public ModelPartContainer getModelPart() {
return null;
}
@Override
public ModelPart getExpressionType() {
return getModelPart();
}
@Override
public List<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
public String getGroupAlias() {
return null;
}
@Override
public void addTableGroupJoin(TableGroupJoin join) {
throw new UnsupportedOperationException( );
}
@Override
public void addNestedTableGroupJoin(TableGroupJoin join) {
throw new UnsupportedOperationException( );
return cteTableReference.getIdentificationVariable();
}
@Override

View File

@ -9,10 +9,12 @@ package org.hibernate.sql.ast.tree.from;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase;
@ -22,7 +24,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase;
public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifier implements TableGroup {
private final boolean canUseInnerJoins;
private final NavigablePath navigablePath;
private final TableGroupProducer producer;
private final ModelPartContainer modelPartContainer;
private final String sourceAlias;
private final SqlAliasBase sqlAliasBase;
@ -35,14 +37,14 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
public AbstractTableGroup(
boolean canUseInnerJoins,
NavigablePath navigablePath,
TableGroupProducer producer,
ModelPartContainer modelPartContainer,
String sourceAlias,
SqlAliasBase sqlAliasBase,
SessionFactoryImplementor sessionFactory) {
super();
this.canUseInnerJoins = canUseInnerJoins;
this.navigablePath = navigablePath;
this.producer = producer;
this.modelPartContainer = modelPartContainer;
this.sourceAlias = sourceAlias;
this.sqlAliasBase = sqlAliasBase;
this.sessionFactory = sessionFactory;
@ -59,12 +61,12 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
@Override
public String getGroupAlias() {
return sqlAliasBase.getAliasStem();
return sqlAliasBase == null ? null : sqlAliasBase.getAliasStem();
}
@Override
public TableGroupProducer getModelPart() {
return producer;
public ModelPartContainer getModelPart() {
return modelPartContainer;
}
@Override
@ -111,6 +113,29 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
tableGroupJoins.add( join );
}
@Override
public void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join) {
int i = 0;
if ( tableGroupJoins != null ) {
for ( ; i < tableGroupJoins.size(); i++ ) {
if ( tableGroupJoins.get( i ).getNavigablePath() == navigablePath ) {
tableGroupJoins.add( i, join );
return;
}
}
}
i = 0;
if ( nestedTableGroupJoins != null ) {
for ( ; i < nestedTableGroupJoins.size(); i++ ) {
if ( nestedTableGroupJoins.get( i ).getNavigablePath() == navigablePath ) {
nestedTableGroupJoins.add( i, join );
return;
}
}
}
throw new NoSuchElementException("Table group for navigable path not found: " + navigablePath);
}
@Override
public void addNestedTableGroupJoin(TableGroupJoin join) {
if ( nestedTableGroupJoins == null ) {

View File

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

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.List;
import java.util.function.Consumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -25,7 +25,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase;
*
* @author Christian Beikov
*/
public class LazyTableGroup extends AbstractColumnReferenceQualifier implements TableGroup {
public class LazyTableGroup extends DelegatingTableGroup {
private final boolean canUseInnerJoins;
private final NavigablePath navigablePath;
@ -33,7 +33,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
private final TableGroupProducer producer;
private final String sourceAlias;
private final SqlAliasBase sqlAliasBase;
private final SessionFactoryImplementor sessionFactory;
private final Supplier<TableGroup> tableGroupSupplier;
private final TableGroup parentTableGroup;
private final BiPredicate<NavigablePath, String> navigablePathChecker;
@ -60,14 +59,13 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
this.tableGroupSupplier = tableGroupSupplier;
this.navigablePathChecker = navigablePathChecker;
this.parentTableGroup = parentTableGroup;
this.sessionFactory = sessionFactory;
}
public TableGroup getUnderlyingTableGroup() {
return tableGroup;
}
@Override
public TableGroup getTableGroup() {
if ( tableGroup != null ) {
return tableGroup;
@ -97,11 +95,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
}
}
@Override
public TableReference getPrimaryTableReference() {
return getTableGroup().getPrimaryTableReference();
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return tableGroup == null ? Collections.emptyList() : tableGroup.getTableReferenceJoins();
@ -117,16 +110,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
return tableGroup == null ? Collections.emptyList() : tableGroup.getNestedTableGroupJoins();
}
@Override
public void addTableGroupJoin(TableGroupJoin join) {
getTableGroup().addTableGroupJoin( join );
}
@Override
public void addNestedTableGroupJoin(TableGroupJoin join) {
getTableGroup().addNestedTableGroupJoin( join );
}
@Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( tableGroup != null ) {
@ -146,11 +129,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
return canUseInnerJoins;
}
@Override
public boolean isLateral() {
return false;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
@ -176,11 +154,6 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
return sourceAlias;
}
@Override
protected SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
@Override
public boolean isRealTableGroup() {
return tableGroup != null && tableGroup.isRealTableGroup();
@ -192,6 +165,39 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
}
@Override
public boolean isLateral() {
return false;
}
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
assert tableExpression != null;
final TableReference tableReference = getTableReferenceInternal(
navigablePath,
tableExpression,
allowFkOptimization,
true
);
if ( tableReference == null ) {
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
}
return tableReference;
}
@Override
public TableReference getTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
}
protected TableReference getTableReferenceInternal(
NavigablePath navigablePath,
String tableExpression,

View File

@ -17,7 +17,7 @@ import org.hibernate.query.NavigablePath;
/**
* @author Christian Beikov
*/
public class MappedByTableGroup implements VirtualTableGroup {
public class MappedByTableGroup extends DelegatingTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath;
private final ModelPartContainer modelPart;
@ -41,6 +41,11 @@ public class MappedByTableGroup implements VirtualTableGroup {
this.navigablePathChecker = navigablePathChecker;
}
@Override
protected TableGroup getTableGroup() {
return underlyingTableGroup;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
@ -67,9 +72,17 @@ public class MappedByTableGroup implements VirtualTableGroup {
return modelPart;
}
// Don't provide access to table group joins as this is table group is just a "named reference"
// The underlying table group contains the joins and will render them
@Override
public String getSourceAlias() {
return underlyingTableGroup.getSourceAlias();
public boolean isRealTableGroup() {
return false;
}
@Override
public boolean isLateral() {
return false;
}
@Override
@ -82,26 +95,6 @@ public class MappedByTableGroup implements VirtualTableGroup {
return Collections.emptyList();
}
@Override
public boolean isRealTableGroup() {
return false;
}
@Override
public boolean canUseInnerJoins() {
return underlyingTableGroup.canUseInnerJoins();
}
@Override
public void addTableGroupJoin(TableGroupJoin join) {
underlyingTableGroup.addTableGroupJoin( join );
}
@Override
public void addNestedTableGroupJoin(TableGroupJoin join) {
underlyingTableGroup.addNestedTableGroupJoin( join );
}
@Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
// No-op
@ -112,19 +105,9 @@ public class MappedByTableGroup implements VirtualTableGroup {
// No-op
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
underlyingTableGroup.applyAffectedTableNames( nameCollector );
}
@Override
public TableReference getPrimaryTableReference() {
return underlyingTableGroup.getPrimaryTableReference();
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return underlyingTableGroup.getTableReferenceJoins();
return Collections.emptyList();
}
@Override

View File

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

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
public void addNestedTableGroupJoin(TableGroupJoin join) {
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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@ -17,109 +15,47 @@ import org.hibernate.query.NavigablePath;
/**
* @author Christian Beikov
*/
public class StandardVirtualTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath;
private final ModelPartContainer modelPart;
public class StandardVirtualTableGroup extends AbstractTableGroup implements VirtualTableGroup {
private final TableGroup underlyingTableGroup;
private final boolean fetched;
private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> nestedTableGroupJoins;
public StandardVirtualTableGroup(
NavigablePath navigablePath,
ModelPartContainer modelPart,
TableGroup underlyingTableGroup,
boolean fetched) {
this.navigablePath = navigablePath;
this.modelPart = modelPart;
super(
underlyingTableGroup.canUseInnerJoins(),
navigablePath,
modelPart,
underlyingTableGroup.getSourceAlias(),
null,
null
);
this.underlyingTableGroup = underlyingTableGroup;
this.fetched = fetched;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public ModelPartContainer getExpressionType() {
return getModelPart();
}
@Override
public String getGroupAlias() {
// none, although we could also delegate to the underlyingTableGroup's group-alias
return null;
}
@Override
public boolean isFetched() {
return fetched;
}
@Override
public ModelPartContainer getModelPart() {
return modelPart;
}
@Override
public String getSourceAlias() {
return underlyingTableGroup.getSourceAlias();
}
@Override
public List<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 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
public void applyAffectedTableNames(Consumer<String> nameCollector) {
underlyingTableGroup.applyAffectedTableNames( nameCollector );
@ -136,25 +72,7 @@ public class StandardVirtualTableGroup implements VirtualTableGroup {
}
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
final TableReference tableReference = getTableReference(
navigablePath,
tableExpression,
allowFkOptimization,
true
);
if ( tableReference == null ) {
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
}
return tableReference;
}
@Override
public TableReference getTableReference(
public TableReference getTableReferenceInternal(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
@ -168,23 +86,7 @@ public class StandardVirtualTableGroup implements VirtualTableGroup {
if ( tableReference != null ) {
return tableReference;
}
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
if ( primaryTableReference != null ) {
return primaryTableReference;
}
}
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
if ( primaryTableReference != null ) {
return primaryTableReference;
}
}
return null;
return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
}
}

View File

@ -47,12 +47,16 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
boolean canUseInnerJoins();
default boolean isLateral() {
return getPrimaryTableReference() instanceof DerivedTableReference
&& ( (DerivedTableReference) getPrimaryTableReference() ).isLateral();
return false;
}
void addTableGroupJoin(TableGroupJoin join);
/**
* Adds the given table group join before a join as found via the given navigable path.
*/
void prependTableGroupJoin(NavigablePath navigablePath, TableGroupJoin join);
/**
* A nested table group join is a join against a table group,
* that is ensured to be joined against the primary table reference and table reference joins in isolation,
@ -136,12 +140,6 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
);
}
default ColumnReference locateColumnReferenceByName(String name) {
throw new UnsupportedOperationException(
"Cannot call #locateColumnReferenceByName on this type of TableGroup"
);
}
@Override
default void accept(SqlAstWalker sqlTreeWalker) {
sqlTreeWalker.visitTableGroup( this );

View File

@ -19,14 +19,7 @@ import org.hibernate.query.NavigablePath;
/**
* @author Andrea Boriero
*/
public class UnionTableGroup implements VirtualTableGroup {
private final boolean canUseInnerJoins;
private final NavigablePath navigablePath;
private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> nestedTableGroupJoins;
private final UnionSubclassEntityPersister modelPart;
private final String sourceAlias;
public class UnionTableGroup extends AbstractTableGroup implements VirtualTableGroup {
private final UnionTableReference tableReference;
public UnionTableGroup(
@ -35,88 +28,8 @@ public class UnionTableGroup implements VirtualTableGroup {
UnionTableReference tableReference,
UnionSubclassEntityPersister modelPart,
String sourceAlias) {
this.canUseInnerJoins = canUseInnerJoins;
this.navigablePath = navigablePath;
super( canUseInnerJoins, navigablePath, modelPart, sourceAlias, null, null );
this.tableReference = tableReference;
this.modelPart = modelPart;
this.sourceAlias = sourceAlias;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public ModelPart getExpressionType() {
return getModelPart();
}
@Override
public String getGroupAlias() {
return null;
}
@Override
public ModelPartContainer getModelPart() {
return modelPart;
}
@Override
public String getSourceAlias() {
return sourceAlias;
}
@Override
public List<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
@ -134,40 +47,14 @@ public class UnionTableGroup implements VirtualTableGroup {
}
@Override
public TableReference getTableReference(
public TableReference getTableReferenceInternal(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
return resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
}
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, true ) != null ) {
if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) {
return tableReference;
}
if ( nestedTableGroupJoins != null ) {
for ( TableGroupJoin tableGroupJoin : nestedTableGroupJoins ) {
final TableReference tableReference = tableGroupJoin.getJoinedGroup()
.resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
if ( tableReference != null ) {
return tableReference;
}
}
}
if ( tableGroupJoins != null ) {
for ( TableGroupJoin tableGroupJoin : tableGroupJoins ) {
final TableReference tableReference = tableGroupJoin.getJoinedGroup()
.resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
if ( tableReference != null ) {
return tableReference;
}
}
}
return null;
return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
}
}

View File

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

View File

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

View File

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

View File

@ -8,10 +8,7 @@
package org.hibernate.envers.function;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
@ -29,11 +26,10 @@ import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.sql.ast.spi.SqlAstQueryPartProcessingState;
import org.hibernate.sql.ast.tree.from.DelegatingTableGroup;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.type.spi.TypeConfiguration;
@ -110,7 +106,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
};
}
private static class AuditingTableGroup implements TableGroup {
private static class AuditingTableGroup extends DelegatingTableGroup {
private final TableGroup delegate;
private final String auditTableExpression;
@ -123,8 +119,8 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
}
@Override
public ModelPart getExpressionType() {
return delegate.getExpressionType();
protected TableGroup getTableGroup() {
return delegate;
}
@Override
@ -135,7 +131,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
if ( tableExpression.equals( normalTableExpression ) ) {
tableExpression = auditTableExpression;
}
return delegate.resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
return super.resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
}
@Override
@ -147,77 +143,7 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
if ( tableExpression.equals( normalTableExpression ) ) {
tableExpression = auditTableExpression;
}
return delegate.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
}
@Override
public NavigablePath getNavigablePath() {
return delegate.getNavigablePath();
}
@Override
public String getGroupAlias() {
return delegate.getGroupAlias();
}
@Override
public ModelPartContainer getModelPart() {
return delegate.getModelPart();
}
@Override
public String getSourceAlias() {
return delegate.getSourceAlias();
}
@Override
public List<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();
return super.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
}
}
}