support for canonical reference to id and version. consider other entity parts - natural-id, tenant-discriminator, etc

This commit is contained in:
Steve Ebersole 2020-01-14 07:24:07 -06:00
parent c2bb7d65f2
commit b23e5ba54a
4 changed files with 97 additions and 8 deletions

View File

@ -141,6 +141,10 @@ ARROW : '->';
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Keywords
ID : [iI][dD];
VERSION : [vV] [eE] [rR] [sS] [iI] [oO] [nN];
NATURALID : [nN] [aA] [tT] [uU] [rR] [aA] [lL] [iI] [dD];
ABS : [aA] [bB] [sS];
ALL : [aA] [lL] [lL];
AND : [aA] [nN] [dD];

View File

@ -413,6 +413,9 @@ expression
| literal # LiteralExpression
| parameter # ParameterExpression
| entityTypeReference # EntityTypeExpression
| entityIdReference # EntityIdExpression
| entityVersionReference # EntityVersionExpression
| entityNaturalIdReference # EntityNaturalIdExpression
| path # PathExpression
| function # FunctionExpression
| LEFT_PAREN subQuery RIGHT_PAREN # SubQueryExpression
@ -422,6 +425,18 @@ entityTypeReference
: TYPE LEFT_PAREN (path | parameter) RIGHT_PAREN
;
entityIdReference
: ID LEFT_PAREN path RIGHT_PAREN pathContinuation?
;
entityVersionReference
: VERSION LEFT_PAREN path RIGHT_PAREN
;
entityNaturalIdReference
: NATURALID LEFT_PAREN path RIGHT_PAREN pathContinuation?
;
caseStatement
: simpleCaseStatement
| searchedCaseStatement
@ -987,6 +1002,7 @@ identifier
| GREATEST
| GROUP
| HOUR
| ID
| IFNULL
| IN
| INDEX
@ -1020,6 +1036,7 @@ identifier
| MOD
| MONTH
| NANOSECOND
| NATURALID
| NEW
| NOT
| NULLIF
@ -1056,6 +1073,7 @@ identifier
| UPDATE
| UPPER
| VALUE
| VERSION
| WEEK
| WHERE
| WITH

View File

@ -39,11 +39,13 @@ import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.BinaryArithmeticOperator;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.PathException;
@ -1320,6 +1322,70 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
throw new ParsingException( "Could not interpret grammar context as 'entity type' expression : " + ctx.getText() );
}
@Override
public SqmPath visitEntityIdExpression(HqlParser.EntityIdExpressionContext ctx) {
return visitEntityIdReference( ctx.entityIdReference() );
}
@Override
public SqmPath visitEntityIdReference(HqlParser.EntityIdReferenceContext ctx) {
final SqmPath sqmPath = consumeDomainPath( ctx.path() );
final DomainType sqmPathType = sqmPath.getReferencedPathSource().getSqmPathType();
if ( sqmPathType instanceof IdentifiableDomainType ) {
//noinspection unchecked
final SqmPath idPath = ( (IdentifiableDomainType) sqmPathType ).getIdentifierDescriptor().createSqmPath(
sqmPath,
this
);
if ( ctx.pathContinuation() == null ) {
return idPath;
}
throw new NotYetImplementedFor6Exception( "Path continuation from `id()` reference not yet implemented" );
}
throw new SemanticException( "Path does not reference an identifiable-type : " + sqmPath.getNavigablePath().getFullPath() );
}
@Override
public SqmPath visitEntityVersionExpression(HqlParser.EntityVersionExpressionContext ctx) {
return visitEntityVersionReference( ctx.entityVersionReference() );
}
@Override
public SqmPath visitEntityVersionReference(HqlParser.EntityVersionReferenceContext ctx) {
final SqmPath sqmPath = consumeDomainPath( ctx.path() );
final DomainType sqmPathType = sqmPath.getReferencedPathSource().getSqmPathType();
if ( sqmPathType instanceof IdentifiableDomainType ) {
final IdentifiableDomainType identifiableType = (IdentifiableDomainType) sqmPathType;
final SingularPersistentAttribute versionAttribute = identifiableType.findVersionAttribute();
if ( versionAttribute == null ) {
throw new SemanticException(
"`" + sqmPath.getNavigablePath().getFullPath() + "` resolved to an identifiable-type (`" +
identifiableType.getTypeName() + "`) which does not define a version"
);
}
//noinspection unchecked
return versionAttribute.createSqmPath( sqmPath, this );
}
throw new SemanticException( "Path does not reference an identifiable-type : " + sqmPath.getNavigablePath().getFullPath() );
}
@Override
public SqmPath visitEntityNaturalIdExpression(HqlParser.EntityNaturalIdExpressionContext ctx) {
return visitEntityNaturalIdReference( ctx.entityNaturalIdReference() );
}
@Override
public SqmPath visitEntityNaturalIdReference(HqlParser.EntityNaturalIdReferenceContext ctx) {
throw new NotYetImplementedFor6Exception( "Support for HQL natural-id references not yet implemented" );
}
@Override
@SuppressWarnings("unchecked")
public SqmMapEntryReference visitMapEntrySelection(HqlParser.MapEntrySelectionContext ctx) {
@ -3551,7 +3617,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return result;
}
private SqmPath consumeDomainPath(HqlParser.PathContext parserPath) {
final SemanticPathPart consumedPart = (SemanticPathPart) parserPath.accept( this );
if ( consumedPart instanceof SqmPath ) {

View File

@ -7,18 +7,16 @@
package org.hibernate.orm.test.query.hql;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.SingularAttributeImpl;
import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest;
import org.hibernate.orm.test.query.sqm.domain.Person;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
@ -29,8 +27,6 @@ import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.junit.jupiter.api.Test;
import org.hamcrest.CoreMatchers;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
@ -112,8 +108,6 @@ public class AttributePathTests extends BaseSqmUnitTest {
interpretSelect( "select s.mate from Person s where s.id = ?1" );
interpretSelect( "select s.mate from Person s where s.pk = ?1" );
// NOTE: this next form does not (yet) work...
interpretSelect( "select s.mate from Person s where s.{id} = ?1" );
final EntityDomainType<OddOne> entity = sessionFactory().getRuntimeMetamodels()
.getJpaMetamodel()
@ -131,6 +125,14 @@ public class AttributePathTests extends BaseSqmUnitTest {
assertThat( ( (SqmPath) pkRef ).getJavaType(), sameInstance( String.class ) );
}
@Test
public void testCanonicalReferences() {
final SqmSelectStatement sqm = interpretSelect( "select s.mate from Person s where id(s) = ?1" );
assertThat( sqm.getQuerySpec().getRestriction(), notNullValue() );
final SqmComparisonPredicate restriction = (SqmComparisonPredicate) sqm.getQuerySpec().getRestriction();
assertThat( restriction, notNullValue() );
}
@Test
public void testManyToOneReference() {
final SqmSelectStatement sqm = interpretSelect( "select s.mate from Person s" );