From c86c2b83003fa1e24ee1daad8282893683b1c558 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 11 Apr 2023 18:08:49 +0200 Subject: [PATCH] HHH-16392 Add column qualifier support to Dialect --- .../dialect/OracleLegacyDialect.java | 7 ++- .../dialect/OracleLegacySqlAstTranslator.java | 19 -------- .../java/org/hibernate/dialect/Dialect.java | 9 ++++ .../DmlTargetColumnQualifierSupport.java | 32 +++++++++++++ .../org/hibernate/dialect/OracleDialect.java | 5 ++ .../dialect/OracleSqlAstTranslator.java | 18 ------- .../sqm/sql/BaseSqmToSqlAstConverter.java | 5 +- .../sql/ast/spi/AbstractSqlAstTranslator.java | 47 ++++++++++++------- 8 files changed, 86 insertions(+), 56 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/DmlTargetColumnQualifierSupport.java diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index 505a199571..942e84e844 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -24,6 +24,7 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.cfg.Environment; import org.hibernate.dialect.BooleanDecoder; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.OracleBooleanJdbcType; @@ -96,7 +97,6 @@ import org.hibernate.type.SqlTypes; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType; import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; -import org.hibernate.type.descriptor.jdbc.ArrayJdbcType; import org.hibernate.type.descriptor.jdbc.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType; @@ -1443,4 +1443,9 @@ public class OracleLegacyDialect extends Dialect { public String rowId(String rowId) { return "rowid"; } + + @Override + public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { + return DmlTargetColumnQualifierSupport.TABLE_ALIAS; + } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java index a58561efd6..f74fad6da5 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java @@ -514,14 +514,6 @@ public class OracleLegacySqlAstTranslator extends Abstr return getDialect().supportsFetchClause( FetchClauseType.ROWS_ONLY ); } - @Override - protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) { - appendSql( tableReference.getTableExpression() ); - registerAffectedTable( tableReference ); - renderTableReferenceIdentificationVariable( tableReference ); - return false; - } - @Override protected void visitSetAssignment(Assignment assignment) { final List columnReferences = assignment.getAssignable().getColumnReferences(); @@ -549,15 +541,4 @@ public class OracleLegacySqlAstTranslator extends Abstr assignment.getAssignedValue().accept( this ); } } - - @Override - public void visitColumnReference(ColumnReference columnReference) { - columnReference.appendReadExpression( this ); - } - - @Override - public void visitAggregateColumnWriteExpression(AggregateColumnWriteExpression aggregateColumnWriteExpression) { - aggregateColumnWriteExpression.appendWriteExpression( this, this ); - } - } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 05ab18945b..0a905d798f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -5224,4 +5224,13 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun public String getRowIdColumnString(String rowId) { return null; } + + /** + * Get the minimum {@link DmlTargetColumnQualifierSupport} required by this dialect. + * + * @return the column qualifier support required by this dialect + */ + public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { + return DmlTargetColumnQualifierSupport.NONE; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DmlTargetColumnQualifierSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/DmlTargetColumnQualifierSupport.java new file mode 100644 index 0000000000..04394812d1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DmlTargetColumnQualifierSupport.java @@ -0,0 +1,32 @@ +/* + * 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.dialect; + +/** + * Indicates the level of qualifier support used by + * the dialect when referencing a column. + * + * @author Marco Belladelli + */ +public enum DmlTargetColumnQualifierSupport { + /** + * Qualify the column using the table expression, + * ignoring a possible table alias. + */ + TABLE_EXPRESSION, + + /** + * Qualify the column using the table alias, whenever available, + * and fallback to the table expression. + */ + TABLE_ALIAS, + + /** + * No need to explicitly qualify the column. + */ + NONE; +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index ba484f4823..a90efa9d63 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -1467,4 +1467,9 @@ public class OracleDialect extends Dialect { final OracleSqlAstTranslator translator = new OracleSqlAstTranslator<>( factory, optionalTableUpdate ); return translator.createMergeOperation( optionalTableUpdate ); } + + @Override + public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { + return DmlTargetColumnQualifierSupport.TABLE_ALIAS; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java index 99d5ac0ef3..6cc303d48c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java @@ -512,14 +512,6 @@ public class OracleSqlAstTranslator extends SqlAstTrans return getDialect().supportsFetchClause( FetchClauseType.ROWS_ONLY ); } - @Override - protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) { - appendSql( tableReference.getTableExpression() ); - registerAffectedTable( tableReference ); - renderTableReferenceIdentificationVariable( tableReference ); - return false; - } - @Override protected void visitSetAssignment(Assignment assignment) { final List columnReferences = assignment.getAssignable().getColumnReferences(); @@ -548,16 +540,6 @@ public class OracleSqlAstTranslator extends SqlAstTrans } } - @Override - public void visitColumnReference(ColumnReference columnReference) { - columnReference.appendReadExpression( this ); - } - - @Override - public void visitAggregateColumnWriteExpression(AggregateColumnWriteExpression aggregateColumnWriteExpression) { - aggregateColumnWriteExpression.appendWriteExpression( this, this ); - } - @Override protected void renderMergeTargetAlias() { appendSql( " t" ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 61672350a6..f1fec3684a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -34,6 +34,7 @@ import org.hibernate.Internal; import org.hibernate.LockMode; import org.hibernate.QueryException; import org.hibernate.boot.model.process.internal.InferredBasicValueResolver; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.function.TimestampaddFunction; import org.hibernate.dialect.function.TimestampdiffFunction; @@ -831,7 +832,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base (filterPredicate) -> additionalRestrictions = combinePredicates( additionalRestrictions, filterPredicate), entityDescriptor, rootTableGroup, - AbstractSqlAstTranslator.rendersTableReferenceAlias( Clause.UPDATE ), + getDialect().getDmlTargetColumnQualifierSupport() == DmlTargetColumnQualifierSupport.TABLE_ALIAS, getLoadQueryInfluencers(), this ); @@ -1088,7 +1089,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base (filterPredicate) -> additionalRestrictions = combinePredicates( additionalRestrictions, filterPredicate), entityDescriptor, rootTableGroup, - AbstractSqlAstTranslator.rendersTableReferenceAlias( Clause.DELETE ), + getDialect().getDmlTargetColumnQualifierSupport() == DmlTargetColumnQualifierSupport.TABLE_ALIAS, getLoadQueryInfluencers(), this ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index c33e3f46ec..23fe0c4629 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -27,6 +27,7 @@ import java.util.function.Supplier; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.QueryException; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.RowLockStrategy; import org.hibernate.dialect.SelectItemReferenceStrategy; @@ -5478,13 +5479,13 @@ public abstract class AbstractSqlAstTranslator implemen } } - public static boolean rendersTableReferenceAlias(Clause clause) { + protected 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 switch ( clause ) { case DELETE: case UPDATE: - return false; + return getDialect().getDmlTargetColumnQualifierSupport() == DmlTargetColumnQualifierSupport.TABLE_ALIAS; } return true; } @@ -6018,19 +6019,24 @@ public abstract class AbstractSqlAstTranslator implemen public void visitColumnReference(ColumnReference columnReference) { final String dmlTargetTableAlias = getDmlTargetTableAlias(); if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) { - // todo (6.0) : use the Dialect to determine how to handle column references - // - specifically should they use the table-alias, the table-expression - // or neither for its qualifier - - final String tableExpression = getCurrentDmlStatement().getTargetTable().getTableExpression(); - // Qualify the column reference with the table expression only in subqueries - final boolean qualifyColumn = !queryPartStack.isEmpty(); + final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport(); + final String qualifier; + if ( qualifierSupport == DmlTargetColumnQualifierSupport.TABLE_ALIAS ) { + qualifier = dmlTargetTableAlias; + } + // Qualify the column reference with the table expression also when in subqueries + else if ( qualifierSupport != DmlTargetColumnQualifierSupport.NONE || !queryPartStack.isEmpty() ) { + qualifier = getCurrentDmlStatement().getTargetTable().getTableExpression(); + } + else { + qualifier = null; + } if ( columnReference.isColumnExpressionFormula() ) { // For formulas, we have to replace the qualifier as the alias was already rendered into the formula // This is fine for now as this is only temporary anyway until we render aliases for table references final String replacement; - if ( qualifyColumn ) { - replacement = "$1" + tableExpression + ".$3"; + if ( qualifier != null ) { + replacement = "$1" + qualifier + ".$3"; } else { replacement = "$1$3"; @@ -6041,7 +6047,7 @@ public abstract class AbstractSqlAstTranslator implemen ); } else { - columnReference.appendReadExpression( this, qualifyColumn ? tableExpression : null ); + columnReference.appendReadExpression( this, qualifier ); } } else { @@ -6054,10 +6060,19 @@ public abstract class AbstractSqlAstTranslator implemen final String dmlTargetTableAlias = getDmlTargetTableAlias(); final ColumnReference columnReference = aggregateColumnWriteExpression.getColumnReference(); if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) { - final String tableExpression = getCurrentDmlStatement().getTargetTable().getTableExpression(); - // Qualify the column reference with the table expression only in subqueries - final boolean qualifyColumn = !queryPartStack.isEmpty(); - aggregateColumnWriteExpression.appendWriteExpression( this, this, qualifyColumn ? tableExpression : null ); + final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport(); + final String qualifier; + if ( qualifierSupport == DmlTargetColumnQualifierSupport.TABLE_ALIAS ) { + qualifier = dmlTargetTableAlias; + } + // Qualify the column reference with the table expression also when in subqueries + else if ( qualifierSupport != DmlTargetColumnQualifierSupport.NONE || !queryPartStack.isEmpty() ) { + qualifier = getCurrentDmlStatement().getTargetTable().getTableExpression(); + } + else { + qualifier = null; + } + aggregateColumnWriteExpression.appendWriteExpression( this, this, qualifier ); } else { aggregateColumnWriteExpression.appendWriteExpression( this, this );