HHH-13619 - Support for JPA's `size` function as a select expression

- PR revisions
This commit is contained in:
Steve Ebersole 2020-03-05 09:58:35 -06:00
parent 336c3b9e30
commit 26ab3c5362
3 changed files with 45 additions and 41 deletions

View File

@ -48,6 +48,7 @@ public class CollectionPathNode extends SqlNode {
*
* @see #from(AST, AST, HqlSqlWalker)
*/
@SuppressWarnings("WeakerAccess")
public CollectionPathNode(
FromElement ownerFromElement,
CollectionPersister collectionDescriptor,
@ -81,19 +82,14 @@ public class CollectionPathNode extends SqlNode {
HqlSqlWalker walker) {
final String referenceName = reference.getText();
final String qualifierQueryPath = qualifier == null
? ""
: ( (FromReferenceNode) qualifier ).getPath();
final String referencePath = qualifier == null
? referenceName
: qualifierQueryPath + "." + reference;
final String referenceQueryPath;
if ( qualifier == null ) {
// If there is no qualifier it means the argument to `size()` was a simple IDENT node as opposed to a DOT-IDENT
// node. In this case, `reference` could technically be a join alias. This is not JPA
// compliant, but is a Hibernate-specific extension
// size( cu )
referenceQueryPath = referenceName;
final FromElement byAlias = walker.getCurrentFromClause().getFromElement( referenceName );
@ -105,7 +101,7 @@ public class CollectionPathNode extends SqlNode {
ownerRef,
collectionDescriptor,
referenceName,
referencePath,
referenceQueryPath,
referenceName,
walker
);
@ -133,7 +129,7 @@ public class CollectionPathNode extends SqlNode {
ownerRef,
walker.getSessionFactoryHelper().requireQueryableCollection( collectionType.getRole() ),
referenceName,
referencePath,
referenceQueryPath,
referenceName,
walker
);
@ -179,7 +175,7 @@ public class CollectionPathNode extends SqlNode {
ownerRef,
walker.getSessionFactoryHelper().requireQueryableCollection( collectionType.getRole() ),
referenceName,
referencePath,
referenceQueryPath,
referenceName,
walker
);
@ -189,18 +185,22 @@ public class CollectionPathNode extends SqlNode {
else {
// we have a dot-ident structure
final FromReferenceNode qualifierFromReferenceNode = (FromReferenceNode) qualifier;
final String qualifierQueryPath = ( (FromReferenceNode) qualifier ).getPath();
referenceQueryPath = qualifierQueryPath + "." + reference;
try {
qualifierFromReferenceNode.resolve( false, false );
}
catch (SemanticException e) {
throw new QueryException( "Unable to resolve collection-path qualifier : " + qualifier.getText(), e );
throw new QueryException( "Unable to resolve collection-path qualifier : " + qualifierQueryPath, e );
}
final Type qualifierType = qualifierFromReferenceNode.getDataType();
final FromElement ownerRef = ( (FromReferenceNode) qualifier ).getFromElement();
final CollectionType collectionType;
final String mappedPath;
final String referenceMappedPath;
if ( qualifierType instanceof CompositeType ) {
final CompositeType qualifierCompositeType = (CompositeType) qualifierType;
@ -208,10 +208,10 @@ public class CollectionPathNode extends SqlNode {
collectionType = (CollectionType) qualifierCompositeType.getSubtypes()[collectionPropertyIndex];
if ( ownerRef instanceof ComponentJoin ) {
mappedPath = ( (ComponentJoin) ownerRef ).getComponentPath() + "." + referenceName;
referenceMappedPath = ( (ComponentJoin) ownerRef ).getComponentPath() + "." + referenceName;
}
else {
mappedPath = qualifierQueryPath.substring( qualifierQueryPath.indexOf( "." ) + 1 );
referenceMappedPath = qualifierQueryPath.substring( qualifierQueryPath.indexOf( "." ) + 1 );
}
}
else if ( qualifierType instanceof EntityType ) {
@ -220,7 +220,7 @@ public class CollectionPathNode extends SqlNode {
final EntityPersister entityPersister = walker.getSessionFactoryHelper().findEntityPersisterByName( entityName );
final int propertyIndex = entityPersister.getEntityMetamodel().getPropertyIndex( referenceName );
collectionType = (CollectionType) entityPersister.getPropertyTypes()[ propertyIndex ];
mappedPath = referenceName;
referenceMappedPath = referenceName;
}
else {
throw new QueryException( "Unexpected collection-path reference qualifier type : " + qualifier );
@ -230,33 +230,48 @@ public class CollectionPathNode extends SqlNode {
( (FromReferenceNode) qualifier ).getFromElement(),
walker.getSessionFactoryHelper().requireQueryableCollection( collectionType.getRole() ),
referenceName,
referencePath,
mappedPath,
referenceQueryPath,
referenceMappedPath,
walker
);
}
}
@SuppressWarnings("WeakerAccess")
public FromElement getCollectionOwnerFromElement() {
return ownerFromElement;
}
@SuppressWarnings("WeakerAccess")
public CollectionPersister getCollectionDescriptor() {
return collectionDescriptor;
}
/**
* The name of the collection property - e.g. `theCollection`
*/
public String getCollectionPropertyName() {
return collectionPropertyName;
}
/**
* The collection property path relative to the "owning entity" in the mapping model - e.g. `theCollection`
* or `someEmbeddable.theCollection`.
*/
@SuppressWarnings("unused")
public String getCollectionPropertyPath() {
return collectionPropertyPath;
}
/**
* The "full" path. E.g. `a.theCollection` or `a.someEmbeddable.theCollection`
*/
@SuppressWarnings("WeakerAccess")
public String getCollectionQueryPath() {
return collectionQueryPath;
}
@SuppressWarnings("WeakerAccess")
public String[] resolveOwnerKeyColumnExpressions() {
final AST ast = walker.getAST();
final String ownerTableAlias;

View File

@ -43,28 +43,23 @@ public class CollectionSizeNode extends SqlNode implements SelectExpression {
}
public String toSqlExpression() {
// generate subquery in the form:
//
// select count( alias_.<collection-size-columns> )
// from <collection-table> as alias_
// where <owner-key-column> = alias_.<collection-key-column>
// need:
// <collection-size-columns> => QueryableCollection#getKeyColumnNames
// <collection-key-column> => QueryableCollection#getKeyColumnNames
// <collection-table> => QueryableCollection#getTableName
// <owner-key-column> => ???
final String[] ownerKeyColumns = collectionPathNode.resolveOwnerKeyColumnExpressions();
final FromElement collectionOwnerFromElement = collectionPathNode.getCollectionOwnerFromElement();
final QueryableCollection collectionDescriptor = (QueryableCollection) collectionPathNode.getCollectionDescriptor();
// collection-key
// generate subquery in the form:
//
// select count( alias_.<collection-key-column> )
// from <collection-table> as alias_
// where <owner-key-column> = alias_.<collection-key-column>
// Note that `collectionPropertyMapping.toColumns(.., COLLECTION_SIZE)` returns the complete `count(...)` SQL
// expression, hence he expectation for a single expression regardless of the number of columns in the key.
final String collectionTableAlias = collectionOwnerFromElement.getFromClause()
.getAliasGenerator()
.createName( collectionPathNode.getCollectionPropertyName() );
final String[] ownerKeyColumns = collectionPathNode.resolveOwnerKeyColumnExpressions();
final String[] collectionKeyColumns = StringHelper.qualify( collectionTableAlias, collectionDescriptor.getKeyColumnNames() );
if ( collectionKeyColumns.length != ownerKeyColumns.length ) {
@ -111,11 +106,12 @@ public class CollectionSizeNode extends SqlNode implements SelectExpression {
return subQuery;
}
private String scalarName;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SelectExpression
private String scalarName;
@Override
public void setScalarColumnText(int i) {
log.debugf( "setScalarColumnText(%s)", i );

View File

@ -31,13 +31,6 @@ public class ImpliedFromElement extends FromElement {
super();
}
/**
* Here to add debug breakpoints
*/
public ImpliedFromElement(FromClause fromClause, FromElement origin, String alias) {
super( fromClause, origin, alias );
}
public boolean isImplied() {
return true;
}