77import org .hibernate .metamodel .model .domain .JpaMetamodel ;
88import org .hibernate .metamodel .model .domain .ManagedDomainType ;
99import org .hibernate .query .SemanticException ;
10- import org .hibernate .query .hql .HqlLogging ;
1110import org .hibernate .query .hql .spi .DotIdentifierConsumer ;
1211import org .hibernate .query .hql .spi .SemanticPathPart ;
1312import org .hibernate .query .hql .spi .SqmCreationState ;
1413import org .hibernate .query .hql .spi .SqmPathRegistry ;
14+ import org .hibernate .query .spi .QueryEngine ;
1515import org .hibernate .query .sqm .NodeBuilder ;
1616import org .hibernate .query .sqm .function .SqmFunctionDescriptor ;
1717import org .hibernate .query .sqm .spi .SqmCreationContext ;
2727import org .hibernate .type .descriptor .java .EnumJavaType ;
2828import org .hibernate .type .descriptor .java .JavaType ;
2929
30+ import static org .hibernate .query .hql .HqlLogging .QUERY_LOGGER ;
31+
3032/**
3133 * A {@link DotIdentifierConsumer} used to interpret paths outside any
3234 * specific context. This is the handler used at the root of the handler
@@ -78,7 +80,7 @@ public void consumeIdentifier(String identifier, boolean isBase, boolean isTermi
7880 }
7981 pathSoFar .append ( identifier );
8082
81- HqlLogging . QUERY_LOGGER .tracef (
83+ QUERY_LOGGER .tracef (
8284 "BasicDotIdentifierHandler#consumeIdentifier( %s, %s, %s ) - %s" ,
8385 identifier ,
8486 isBase ,
@@ -119,7 +121,7 @@ public SemanticPathPart resolvePathPart(
119121 String identifier ,
120122 boolean isTerminal ,
121123 SqmCreationState creationState ) {
122- HqlLogging . QUERY_LOGGER .tracef (
124+ QUERY_LOGGER .tracef (
123125 "BaseLocalSequencePart#consumeIdentifier( %s, %s, %s ) - %s" ,
124126 identifier ,
125127 isBase ,
@@ -129,87 +131,124 @@ public SemanticPathPart resolvePathPart(
129131
130132 if ( isBase ) {
131133 isBase = false ;
132-
133- final SqmPathRegistry sqmPathRegistry =
134- creationState .getProcessingStateStack ().getCurrent ()
135- .getPathRegistry ();
136-
137- final SqmFrom <?,?> pathRootByAlias = sqmPathRegistry .findFromByAlias ( identifier , true );
138- if ( pathRootByAlias != null ) {
139- // identifier is an alias (identification variable)
140- validateAsRoot ( pathRootByAlias );
141- return isTerminal ? pathRootByAlias : new DomainPathPart ( pathRootByAlias );
142- }
143-
144- final SqmFrom <?, ?> pathRootByExposedNavigable = sqmPathRegistry .findFromExposing ( identifier );
145- if ( pathRootByExposedNavigable != null ) {
146- // identifier is an "unqualified attribute reference"
147- validateAsRoot ( pathRootByExposedNavigable );
148- final SqmPath <?> sqmPath = pathRootByExposedNavigable .get ( identifier , true );
149- return isTerminal ? sqmPath : new DomainPathPart ( sqmPath );
134+ final SemanticPathPart pathPart =
135+ resolvePath ( identifier , isTerminal , creationState );
136+ if ( pathPart != null ) {
137+ return pathPart ;
150138 }
151139 }
152140
153- // at the moment, below this point we wait to resolve the sequence until we hit the terminal
154- //
155- // we could check for "intermediate resolution", but that comes with a performance hit. E.g., consider
141+ // Below this point we wait to resolve the sequence until we hit the terminal.
142+ // We could check for "intermediate resolution", but that comes with a performance hit.
143+ // Consider:
156144 //
157- // ` org.hibernate.test.Sex.MALE`
145+ // org.hibernate.test.Sex.MALE
158146 //
159- // we could check `org` and then `org.hibernate` and then `org.hibernate.test` and then ... until
160- // we know it is a package, class or entity name. That gets expensive though. For now, plan on
161- // resolving these at the terminal
162- //
163- // todo (6.0) : finish this logic. and see above note in `! isTerminal` block
164-
147+ // We could check 'org', then 'org.hibernate', then 'org.hibernate.test' and so on until
148+ // we know it's a package, class or entity name. That's more expensive though, and the
149+ // error message would not be better.
165150
166- if ( ! isTerminal ) {
167- return this ;
168- }
151+ return isTerminal ? resolveTerminal ( creationState ) : this ;
152+ }
169153
154+ private SemanticPathPart resolveTerminal (SqmCreationState creationState ) {
170155 final SqmCreationContext creationContext = creationState .getCreationContext ();
171- final JpaMetamodel jpaMetamodel = creationContext .getJpaMetamodel ();
172- final String path = pathSoFar .toString ();
173- final String importableName = jpaMetamodel .qualifyImportableName ( path );
174156 final NodeBuilder nodeBuilder = creationContext .getNodeBuilder ();
175- if ( importableName != null ) {
157+ final JpaMetamodel jpaMetamodel = creationContext .getJpaMetamodel ();
158+ final QueryEngine queryEngine = creationContext .getQueryEngine ();
159+
160+ final SemanticPathPart literalType =
161+ resolveLiteralType ( jpaMetamodel , nodeBuilder );
162+ if ( literalType != null ) {
163+ return literalType ;
164+ }
165+
166+ final SqmFunctionDescriptor functionDescriptor =
167+ resolveFunction ( queryEngine );
168+ if ( functionDescriptor != null ) {
169+ return functionDescriptor .generateSqmExpression ( null , queryEngine );
170+ }
171+
172+ final SemanticPathPart literalJava =
173+ resolveLiteralJavaElement ( jpaMetamodel , nodeBuilder );
174+ if ( literalJava != null ) {
175+ return literalJava ;
176+ }
177+
178+ throw new SemanticException ( "Could not interpret path expression '" + pathSoFar + "'" );
179+ }
180+
181+ private SemanticPathPart resolvePath (String identifier , boolean isTerminal , SqmCreationState creationState ) {
182+ final SqmPathRegistry sqmPathRegistry =
183+ creationState .getProcessingStateStack ().getCurrent ()
184+ .getPathRegistry ();
185+
186+ final SqmFrom <?,?> pathRootByAlias =
187+ sqmPathRegistry .findFromByAlias ( identifier , true );
188+ if ( pathRootByAlias != null ) {
189+ // identifier is an alias (identification variable)
190+ validateAsRoot ( pathRootByAlias );
191+ return isTerminal ? pathRootByAlias : new DomainPathPart ( pathRootByAlias );
192+ }
193+
194+ final SqmFrom <?, ?> pathRootByExposedNavigable =
195+ sqmPathRegistry .findFromExposing ( identifier );
196+ if ( pathRootByExposedNavigable != null ) {
197+ // identifier is an "unqualified attribute reference"
198+ validateAsRoot ( pathRootByExposedNavigable );
199+ final SqmPath <?> sqmPath =
200+ pathRootByExposedNavigable .get ( identifier , true );
201+ return isTerminal ? sqmPath : new DomainPathPart ( sqmPath );
202+ }
203+
204+ return null ;
205+ }
206+
207+ private SqmFunctionDescriptor resolveFunction (QueryEngine queryEngine ) {
208+ return queryEngine .getSqmFunctionRegistry ().findFunctionDescriptor ( pathSoFar .toString () );
209+ }
210+
211+ private SemanticPathPart resolveLiteralType (JpaMetamodel jpaMetamodel , NodeBuilder nodeBuilder ) {
212+ final String importableName = jpaMetamodel .qualifyImportableName ( pathSoFar .toString () );
213+ if ( importableName == null ) {
214+ return null ;
215+ }
216+ else {
176217 final ManagedDomainType <?> managedType = jpaMetamodel .managedType ( importableName );
177218 if ( managedType instanceof SqmEntityDomainType <?> entityDomainType ) {
178219 return new SqmLiteralEntityType <>( entityDomainType , nodeBuilder );
179220 }
180221 else if ( managedType instanceof SqmEmbeddableDomainType <?> embeddableDomainType ) {
181222 return new SqmLiteralEmbeddableType <>( embeddableDomainType , nodeBuilder );
182223 }
224+ else {
225+ return null ;
226+ }
183227 }
228+ }
184229
185- final SqmFunctionDescriptor functionDescriptor =
186- creationContext .getQueryEngine ().getSqmFunctionRegistry ()
187- .findFunctionDescriptor ( path );
188- if ( functionDescriptor != null ) {
189- return functionDescriptor .generateSqmExpression ( null , creationContext .getQueryEngine () );
190- }
191-
230+ private SemanticPathPart resolveLiteralJavaElement (JpaMetamodel metamodel , NodeBuilder nodeBuilder ) {
231+ final String path = pathSoFar .toString ();
192232 // see if it is a named field/enum reference
193233 final int splitPosition = path .lastIndexOf ( '.' );
194234 if ( splitPosition > 0 ) {
195235 final String prefix = path .substring ( 0 , splitPosition );
196236 final String terminal = path .substring ( splitPosition + 1 );
197237 try {
198- final EnumJavaType <?> enumType = jpaMetamodel .getEnumType ( prefix );
238+ final EnumJavaType <?> enumType = metamodel .getEnumType ( prefix );
199239 if ( enumType != null ) {
200- return sqmEnumLiteral ( jpaMetamodel , enumType , terminal , nodeBuilder );
240+ return sqmEnumLiteral ( metamodel , enumType , terminal , nodeBuilder );
201241 }
202242
203- final JavaType <?> fieldJtdTest = jpaMetamodel .getJavaConstantType ( prefix , terminal );
243+ final JavaType <?> fieldJtdTest = metamodel .getJavaConstantType ( prefix , terminal );
204244 if ( fieldJtdTest != null ) {
205- return sqmFieldLiteral ( jpaMetamodel , prefix , terminal , fieldJtdTest , nodeBuilder );
245+ return sqmFieldLiteral ( metamodel , prefix , terminal , fieldJtdTest , nodeBuilder );
206246 }
207247 }
208248 catch (Exception ignore ) {
209249 }
210250 }
211-
212- throw new SemanticException ( "Could not interpret path expression '" + path + "'" );
251+ return null ;
213252 }
214253
215254 private static <E > SqmFieldLiteral <E > sqmFieldLiteral (
0 commit comments