diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index 344d51c2e9..41c242ffad 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -219,7 +219,7 @@ public class EmbeddedAttributeMapping ) ); - columnReferences.add( (ColumnReference) columnReference ); + columnReferences.add( columnReference.unwrap( ColumnReference.class ) ); } ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstProcessingStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstProcessingStateImpl.java deleted file mode 100644 index 9702e5d5ab..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstProcessingStateImpl.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.query.results; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.hibernate.Internal; -import org.hibernate.sql.ast.spi.FromClauseAccess; -import org.hibernate.sql.ast.spi.SqlAstCreationState; -import org.hibernate.sql.ast.spi.SqlAstProcessingState; -import org.hibernate.sql.ast.spi.SqlExpressionResolver; -import org.hibernate.sql.ast.spi.SqlSelection; -import org.hibernate.sql.ast.tree.expression.ColumnReference; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.type.descriptor.java.JavaTypeDescriptor; -import org.hibernate.type.spi.TypeConfiguration; - -/** - * @author Steve Ebersole - */ -@Internal -public class SqlAstProcessingStateImpl implements SqlAstProcessingState, SqlExpressionResolver { - private final FromClauseAccessImpl fromClauseAccess; - - private final Map sqlSelectionMap = new HashMap<>(); - private final Consumer sqlSelectionConsumer; - - private final SqlAstCreationState sqlAstCreationState; - - public SqlAstProcessingStateImpl( - FromClauseAccessImpl fromClauseAccess, - Consumer sqlSelectionConsumer, - SqlAstCreationState sqlAstCreationState) { - this.fromClauseAccess = fromClauseAccess; - this.sqlSelectionConsumer = sqlSelectionConsumer; - this.sqlAstCreationState = sqlAstCreationState; - } - - @Override - public SqlAstProcessingState getParentState() { - // none - return null; - } - - @Override - public SqlExpressionResolver getSqlExpressionResolver() { - return this; - } - - @Override - public SqlAstCreationState getSqlAstCreationState() { - return sqlAstCreationState; - } - - public int getNumberOfProcessedSelections() { - return sqlSelectionMap.size(); - } - - @Override - public Expression resolveSqlExpression( - String key, - Function creator) { - final SqlSelectionImpl existing = sqlSelectionMap.get( key ); - if ( existing != null ) { - return existing; - } - - final Expression created = creator.apply( this ); - - if ( created instanceof SqlSelectionImpl ) { - sqlSelectionMap.put( key, (SqlSelectionImpl) created ); - sqlSelectionConsumer.accept( (SqlSelectionImpl) created ); - } - else if ( created instanceof ColumnReference ) { - final ColumnReference columnReference = (ColumnReference) created; - - final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( - sqlSelectionMap.size() + 1, - columnReference.getJdbcMapping() - ); - - sqlSelectionMap.put( key, sqlSelection ); - sqlSelectionConsumer.accept( sqlSelection ); - } - - return created; - } - - @Override - public SqlSelection resolveSqlSelection( - Expression expression, - JavaTypeDescriptor javaTypeDescriptor, - TypeConfiguration typeConfiguration) { - assert expression instanceof SqlSelectionImpl; - return (SqlSelection) expression; - } - - public FromClauseAccess getFromClauseAccess() { - return fromClauseAccess; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java index e78b5773d0..bb3de8320e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java @@ -86,6 +86,7 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta final List columnReferences = new ArrayList<>(); // todo (6.0) : "polymorphize" this + final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); if ( mapping instanceof ToOneAttributeMapping ) { final ToOneAttributeMapping toOne = (ToOneAttributeMapping) mapping; final ModelPart modelPart = getModelPart( sqlAstCreationState, toOne ); @@ -99,21 +100,19 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta toOne, selection.getContainingTableExpression() ); + final Expression columnReference = sqlExpressionResolver.resolveSqlExpression( + createColumnReferenceKey( + tableReference, + selection.getSelectionExpression() + ), + sqlAstProcessingState -> new ColumnReference( + tableReference.getIdentificationVariable(), + selection, + sqlAstCreationState.getCreationContext().getSessionFactory() + ) + ); - final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver() - .resolveSqlExpression( - createColumnReferenceKey( - tableReference, - selection.getSelectionExpression() - ), - sqlAstProcessingState -> new ColumnReference( - tableReference.getIdentificationVariable(), - selection, - sqlAstCreationState.getCreationContext().getSessionFactory() - ) - ); - - columnReferences.add( (ColumnReference) columnReference ); + columnReferences.add( columnReference.unwrap( ColumnReference.class ) ); } ); } @@ -151,7 +150,7 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta (index, selectable) -> { final TableReference tableReference = mapTableGroup.resolveTableReference( selectable.getContainingTableExpression() ); - final SqlExpressionResolver expressionResolver = sqlAstCreationState.getSqlExpressionResolver(); + final SqlExpressionResolver expressionResolver = sqlExpressionResolver; columnReferences.add( (ColumnReference) expressionResolver.resolveSqlExpression( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java index 5bdcc6ee6f..c2e15f1aac 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java @@ -25,6 +25,10 @@ public interface Expression extends SqlAstNode, SqlSelectionProducer { */ MappingModelExpressable getExpressionType(); + default T unwrap(Class target) { + return (T) this; + } + @Override default SqlSelection createSqlSelection( int jdbcPosition, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/SqlSelectionExpression.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/SqlSelectionExpression.java index 07b5f9ee77..f84c86fea6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/SqlSelectionExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/SqlSelectionExpression.java @@ -29,6 +29,28 @@ public class SqlSelectionExpression implements Expression { return theSelection; } + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class target) { + if ( target.isInstance( this ) ) { + return (T) this; + } + + if ( target.isInstance( theSelection ) ) { + return (T) theSelection; + } + + if ( target.isInstance( theSelection.getExpression() ) ) { + return (T) theSelection.getExpression(); + } + + if ( target.isInstance( theSelection.getExpressionType() ) ) { + return (T) theSelection.getExpressionType(); + } + + return theSelection.getExpression().unwrap( target ); + } + @Override public void accept(SqlAstWalker sqlTreeWalker) { sqlTreeWalker.visitSqlSelectionExpression( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java index 865ebadfae..5d630b3515 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java @@ -9,6 +9,7 @@ package org.hibernate.sql.results.jdbc.internal; import java.sql.SQLException; import org.hibernate.CacheMode; +import org.hibernate.HibernateException; import org.hibernate.cache.spi.QueryKey; import org.hibernate.cache.spi.QueryResultsCache; import org.hibernate.query.spi.QueryOptions; @@ -248,11 +249,19 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { private void readCurrentRowValues() throws SQLException { for ( final SqlSelection sqlSelection : sqlSelections ) { - currentRowJdbcValues[ sqlSelection.getValuesArrayPosition() ] = sqlSelection.getJdbcValueExtractor().extract( - resultSetAccess.getResultSet(), - sqlSelection.getJdbcResultSetIndex(), - executionContext.getSession() - ); + try { + currentRowJdbcValues[ sqlSelection.getValuesArrayPosition() ] = sqlSelection.getJdbcValueExtractor().extract( + resultSetAccess.getResultSet(), + sqlSelection.getJdbcResultSetIndex(), + executionContext.getSession() + ); + } + catch (Exception e) { + throw new HibernateException( + "Unable to extract JDBC value for position `" + sqlSelection.getJdbcResultSetIndex() + "`", + e + ); + } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java index 6d1eab8bfd..713c6c04fa 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java @@ -55,7 +55,6 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { super.configure( cfg ); cfg.setProperty( Environment.USE_QUERY_CACHE, "false" ); cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); - cfg.setProperty( Environment.SEMANTIC_QUERY_PRODUCER, HqlTranslator.class.getName() ); } private void createData() { @@ -68,6 +67,7 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { address1.setStreet( "1313 Mockingbird Lane" ); address1.setCity( "Anywhere" ); address1.setStateProvince( stateProvince ); + address1.setPostalCode( "12345" ); address1.setCountry( "USA" ); zoo1.setAddress( address1 ); zoo1Mammal1 = new Mammal(); @@ -84,6 +84,7 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { Address address2 = new Address(); address2.setStreet( "1313 Mockingbird Lane" ); address2.setCity( "Anywhere" ); + address2.setPostalCode( "12345" ); address2.setStateProvince( stateProvince ); address2.setCountry( "USA" ); zoo2.setAddress( address2 ); @@ -100,6 +101,7 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { address3.setStreet( "1312 Mockingbird Lane" ); address3.setCity( "Anywhere" ); address3.setStateProvince( stateProvince ); + address3.setPostalCode( "12345" ); address3.setCountry( "USA" ); zoo3.setAddress( address3 ); @@ -109,6 +111,7 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { address4.setStreet( "1312 Mockingbird Lane" ); address4.setCity( "Nowhere" ); address4.setStateProvince( stateProvince ); + address4.setPostalCode( "12345" ); address4.setCountry( "USA" ); zoo4.setAddress( address4 ); @@ -241,17 +244,58 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { // zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA // zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA // zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA + + // NOTE (6.0) : the above illustration is based on 5.x code. but the outcome + // is ultimately based on the order in which we render the attributes of + // the Address component. In 6.0 this has been made consistent and easily + // explainable, whereas that was not previously the case. +// checkTestOrderByResults( +// s.createQuery( +// "select z.name, z.address from Zoo z order by z.address, z.name" +// ).list(), +// zoo3, zoo4, zoo2, zoo1, null +// ); + + // NOTE (6.0) - continued : this is functionally equivalent to the 5.x case + checkTestOrderByResults( + s.createQuery( + "select z.name, z.address " + + "from Zoo z " + + "order by z.address.street," + + " z.address.city," + + " z.address.postalCode, " + + " z.address.country," + + " z.name" + ).list(), + zoo3, zoo4, zoo2, zoo1, null + ); + + // NOTE (6.0) - continued 2 : this is the 6.x functionally + // ordered by address, name: + // zoo3 Zoo 1312 Mockingbird Lane, Anywhere, IL USA + // zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA + // zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA + // zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA checkTestOrderByResults( s.createQuery( "select z.name, z.address from Zoo z order by z.address, z.name" ).list(), - zoo3, zoo4, zoo2, zoo1, null + zoo3, zoo2, zoo1, zoo4, null ); + + // NOTE (6.0) - continued 3 : and the functionally equiv "full ordering" + checkTestOrderByResults( + s.createQuery( + "select z.name, z.address from Zoo z order by z.address.city, z.address.country, z.address.stateProvince, z.address.street, z.name" + ).list(), + zoo3, zoo2, zoo1, zoo4, null + ); + checkTestOrderByResults( s.createQuery( "select name, address from Zoo order by address, name" ).list(), - zoo3, zoo4, zoo2, zoo1, null + zoo3, zoo2, zoo1, zoo4, null ); // ordered by address: @@ -260,15 +304,24 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { // unordered: // zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA // zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA + // + // NOTE 6.0 : these results are not well defined. Because we primarily order + // based on city zoo1, zoo2 and zoo3 are all sorted "above" zoo4. however + // the order between zoo1 and zoo2 is completely undefined because they + // have the same address + // + // and honestly, not even sure what this assertion is even trying to test. + // but changed the query to what you'd need to get those results - which I guess + // is the order used by 5.x checkTestOrderByResults( s.createQuery( - "select z.name, z.address from Zoo z order by z.address" + "select z.name, z.address from Zoo z order by z.address.street, z.address.city" ).list(), zoo3, zoo4, null, null, zoosWithSameAddress ); checkTestOrderByResults( s.createQuery( - "select name, address from Zoo order by address" + "select name, address from Zoo order by address.street, address.city" ).list(), zoo3, zoo4, null, null, zoosWithSameAddress ); @@ -406,30 +459,37 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { // zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA // zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA // zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA + // + // NOTE (6.0) : another case of different handling for the component attributes + // causing a "failure" + // zoo3 Zoo 1312 Mockingbird Lane, Anywhere, IL USA + // zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA + // zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA + // zoo4 Duh Zoo 1312 Mockingbird Lane, Nowhere, IL USA checkTestOrderByResults( s.createQuery( "select z.name as address, z.address as name from Zoo z order by name, address" ).list(), - zoo3, zoo4, zoo2, zoo1, null + zoo3, zoo2, zoo1, zoo4, null ); checkTestOrderByResults( s.createQuery( "select z.name, z.address as name from Zoo z order by name, z.name" ).list(), - zoo3, zoo4, zoo2, zoo1, null + zoo3, zoo2, zoo1, zoo4, null ); // using ASC checkTestOrderByResults( s.createQuery( "select z.name as address, z.address as name from Zoo z order by name ASC, address ASC" ).list(), - zoo3, zoo4, zoo2, zoo1, null + zoo3, zoo2, zoo1, zoo4, null ); checkTestOrderByResults( s.createQuery( "select z.name, z.address as name from Zoo z order by name ASC, z.name ASC" ).list(), - zoo3, zoo4, zoo2, zoo1, null + zoo3, zoo2, zoo1, zoo4, null ); // ordered by address: @@ -438,19 +498,22 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { // unordered: // zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA // zoo1 Zoo 1313 Mockingbird Lane, Anywhere, IL USA - checkTestOrderByResults( - s.createQuery( - "select z.name as zooName, z.address as zooAddress from Zoo z order by zooAddress" - ).list(), - zoo3, zoo4, null, null, zoosWithSameAddress - ); + // + // NOTE (6.0) : another one where the result order would be undefined +// checkTestOrderByResults( +// s.createQuery( +// "select z.name as zooName, z.address as zooAddress from Zoo z order by zooAddress" +// ).list(), +// zoo3, zoo4, null, null, zoosWithSameAddress +// ); - checkTestOrderByResults( - s.createQuery( - "select z.name as zooName, z.address as name from Zoo z order by name" - ).list(), - zoo3, zoo4, null, null, zoosWithSameAddress - ); + // NOTE (6.0) : another undefined case +// checkTestOrderByResults( +// s.createQuery( +// "select z.name as zooName, z.address as name from Zoo z order by name" +// ).list(), +// zoo3, zoo4, null, null, zoosWithSameAddress +// ); // ordered by name: // zoo2 A Zoo 1313 Mockingbird Lane, Anywhere, IL USA @@ -679,8 +742,8 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { assertTrue( zoosUnordered.remove( zooResult ) ); } else { - assertEquals( zooExpected.getName(), ( ( Object[] ) result )[ 0 ] ); assertEquals( zooExpected.getAddress(), ( ( Object[] ) result )[ 1 ] ); + assertEquals( zooExpected.getName(), ( ( Object[] ) result )[ 0 ] ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/Address.java b/hibernate-core/src/test/java/org/hibernate/test/hql/Address.java index 942742ad9e..a5d9cc62b1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/Address.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/Address.java @@ -100,4 +100,15 @@ public class Address { result = 31 * result + ( stateProvince != null ? stateProvince.hashCode() : 0 ); return result; } + + @Override + public String toString() { + return "Address{" + + "street='" + street + '\'' + + ", city='" + city + '\'' + + ", postalCode='" + postalCode + '\'' + + ", country='" + country + '\'' + + ", stateProvince=" + stateProvince + + '}'; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/StateProvince.java b/hibernate-core/src/test/java/org/hibernate/test/hql/StateProvince.java index be66eb33a7..f259727d87 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/StateProvince.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/StateProvince.java @@ -70,4 +70,13 @@ public class StateProvince { result = 31 * result + ( isoCode != null ? isoCode.hashCode() : 0 ); return result; } + + @Override + public String toString() { + return "StateProvince{" + + "id=" + id + + ", name='" + name + '\'' + + ", isoCode='" + isoCode + '\'' + + '}'; + } }