introduce new syntax for aggregate functions applying to collections
max(element x.y), min(index x.y), sum(element x.y) and rationalize the node types here
This commit is contained in:
parent
38fc97feb3
commit
4b5e6e1969
|
@ -1782,7 +1782,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Phone> phones = entityManager.createQuery(
|
List<Phone> phones = entityManager.createQuery(
|
||||||
"select p " +
|
"select p " +
|
||||||
"from Phone p " +
|
"from Phone p " +
|
||||||
"where maxelement(p.calls) = :call",
|
"where max(element p.calls) = :call",
|
||||||
Phone.class)
|
Phone.class)
|
||||||
.setParameter("call", call)
|
.setParameter("call", call)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
|
@ -1800,7 +1800,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Phone> phones = entityManager.createQuery(
|
List<Phone> phones = entityManager.createQuery(
|
||||||
"select p " +
|
"select p " +
|
||||||
"from Phone p " +
|
"from Phone p " +
|
||||||
"where minelement(p.calls) = :call",
|
"where min(element p.calls) = :call",
|
||||||
Phone.class)
|
Phone.class)
|
||||||
.setParameter("call", call)
|
.setParameter("call", call)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
|
@ -1817,7 +1817,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Person> persons = entityManager.createQuery(
|
List<Person> persons = entityManager.createQuery(
|
||||||
"select p " +
|
"select p " +
|
||||||
"from Person p " +
|
"from Person p " +
|
||||||
"where maxindex(p.phones) = 0",
|
"where max(index p.phones) = 0",
|
||||||
Person.class)
|
Person.class)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
//end::hql-collection-expressions-example[]
|
//end::hql-collection-expressions-example[]
|
||||||
|
@ -1991,7 +1991,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Person> persons = entityManager.createQuery(
|
List<Person> persons = entityManager.createQuery(
|
||||||
"select pr " +
|
"select pr " +
|
||||||
"from Person pr " +
|
"from Person pr " +
|
||||||
"where pr.phones[maxindex(pr.phones)].type = 'LAND_LINE'",
|
"where pr.phones[max(index pr.phones)].type = 'LAND_LINE'",
|
||||||
Person.class)
|
Person.class)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
//end::hql-collection-index-operator-example[]
|
//end::hql-collection-index-operator-example[]
|
||||||
|
|
|
@ -145,6 +145,7 @@ AND : [aA] [nN] [dD];
|
||||||
ANY : [aA] [nN] [yY];
|
ANY : [aA] [nN] [yY];
|
||||||
AS : [aA] [sS];
|
AS : [aA] [sS];
|
||||||
ASC : [aA] [sS] [cC];
|
ASC : [aA] [sS] [cC];
|
||||||
|
AVG : [aA] [vV] [gG];
|
||||||
BY : [bB] [yY];
|
BY : [bB] [yY];
|
||||||
BETWEEN : [bB] [eE] [tT] [wW] [eE] [eE] [nN];
|
BETWEEN : [bB] [eE] [tT] [wW] [eE] [eE] [nN];
|
||||||
BOTH : [bB] [oO] [tT] [hH];
|
BOTH : [bB] [oO] [tT] [hH];
|
||||||
|
@ -164,6 +165,7 @@ DAY : [dD] [aA] [yY];
|
||||||
DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
|
DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
|
||||||
DESC : [dD] [eE] [sS] [cC];
|
DESC : [dD] [eE] [sS] [cC];
|
||||||
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
|
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
|
||||||
|
ELEMENT : [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
||||||
ELEMENTS : [eE] [lL] [eE] [mM] [eE] [nN] [tT] [sS];
|
ELEMENTS : [eE] [lL] [eE] [mM] [eE] [nN] [tT] [sS];
|
||||||
ELSE : [eE] [lL] [sS] [eE];
|
ELSE : [eE] [lL] [sS] [eE];
|
||||||
EMPTY : [eE] [mM] [pP] [tT] [yY];
|
EMPTY : [eE] [mM] [pP] [tT] [yY];
|
||||||
|
@ -208,7 +210,9 @@ LOCAL_DATE : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE];
|
||||||
LOCAL_DATETIME : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
|
LOCAL_DATETIME : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
|
||||||
LOCAL_TIME : [lL] [oO] [cC] [aA] [lL] '_' [tT] [iI] [mM] [eE];
|
LOCAL_TIME : [lL] [oO] [cC] [aA] [lL] '_' [tT] [iI] [mM] [eE];
|
||||||
MAP : [mM] [aA] [pP];
|
MAP : [mM] [aA] [pP];
|
||||||
|
MAX : [mM] [aA] [xX];
|
||||||
MAXELEMENT : [mM] [aA] [xX] [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
MAXELEMENT : [mM] [aA] [xX] [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
||||||
|
MIN : [mM] [iI] [nN];
|
||||||
MAXINDEX : [mM] [aA] [xX] [iI] [nN] [dD] [eE] [xX];
|
MAXINDEX : [mM] [aA] [xX] [iI] [nN] [dD] [eE] [xX];
|
||||||
MEMBER : [mM] [eE] [mM] [bB] [eE] [rR];
|
MEMBER : [mM] [eE] [mM] [bB] [eE] [rR];
|
||||||
MICROSECOND : [mM] [iI] [cC] [rR] [oO] [sS] [eE] [cC] [oO] [nN] [dD];
|
MICROSECOND : [mM] [iI] [cC] [rR] [oO] [sS] [eE] [cC] [oO] [nN] [dD];
|
||||||
|
@ -247,6 +251,7 @@ SET : [sS] [eE] [tT];
|
||||||
SIZE : [sS] [iI] [zZ] [eE];
|
SIZE : [sS] [iI] [zZ] [eE];
|
||||||
SOME : [sS] [oO] [mM] [eE];
|
SOME : [sS] [oO] [mM] [eE];
|
||||||
SUBSTRING : [sS] [uU] [bB] [sS] [tT] [rR] [iI] [nN] [gG];
|
SUBSTRING : [sS] [uU] [bB] [sS] [tT] [rR] [iI] [nN] [gG];
|
||||||
|
SUM : [sS] [uM] [mM];
|
||||||
THEN : [tT] [hH] [eE] [nN];
|
THEN : [tT] [hH] [eE] [nN];
|
||||||
TIES : [tT] [iI] [eE] [sS];
|
TIES : [tT] [iI] [eE] [sS];
|
||||||
TIME : [tT] [iI] [mM] [eE];
|
TIME : [tT] [iI] [mM] [eE];
|
||||||
|
|
|
@ -948,9 +948,10 @@ function
|
||||||
: standardFunction
|
: standardFunction
|
||||||
| aggregateFunction
|
| aggregateFunction
|
||||||
| jpaCollectionFunction
|
| jpaCollectionFunction
|
||||||
| hqlCollectionFunction
|
| indexAggregateFunction
|
||||||
| jpaNonstandardFunction
|
| elementAggregateFunction
|
||||||
| collectionFunctionMisuse
|
| collectionFunctionMisuse
|
||||||
|
| jpaNonstandardFunction
|
||||||
| genericFunction
|
| genericFunction
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1002,13 +1003,27 @@ jpaCollectionFunction
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The special collection functions defined by HQL
|
* The special aggregate collection functions defined by HQL
|
||||||
*/
|
*/
|
||||||
hqlCollectionFunction
|
indexAggregateFunction
|
||||||
: MAXINDEX LEFT_PAREN path RIGHT_PAREN # MaxIndexFunction
|
: MAXINDEX LEFT_PAREN path RIGHT_PAREN
|
||||||
| MAXELEMENT LEFT_PAREN path RIGHT_PAREN # MaxElementFunction
|
| MININDEX LEFT_PAREN path RIGHT_PAREN
|
||||||
| MININDEX LEFT_PAREN path RIGHT_PAREN # MinIndexFunction
|
| MAX LEFT_PAREN INDEX path RIGHT_PAREN
|
||||||
| MINELEMENT LEFT_PAREN path RIGHT_PAREN # MinElementFunction
|
| MIN LEFT_PAREN INDEX path RIGHT_PAREN
|
||||||
|
| SUM LEFT_PAREN INDEX path RIGHT_PAREN
|
||||||
|
| AVG LEFT_PAREN ELEMENT path RIGHT_PAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The special aggregate collection functions defined by HQL
|
||||||
|
*/
|
||||||
|
elementAggregateFunction
|
||||||
|
: MAXELEMENT LEFT_PAREN path RIGHT_PAREN
|
||||||
|
| MINELEMENT LEFT_PAREN path RIGHT_PAREN
|
||||||
|
| MAX LEFT_PAREN ELEMENT path RIGHT_PAREN
|
||||||
|
| MIN LEFT_PAREN ELEMENT path RIGHT_PAREN
|
||||||
|
| SUM LEFT_PAREN ELEMENT path RIGHT_PAREN
|
||||||
|
| AVG LEFT_PAREN ELEMENT path RIGHT_PAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1365,6 +1380,7 @@ identifier
|
||||||
| ANY
|
| ANY
|
||||||
| AS
|
| AS
|
||||||
| ASC
|
| ASC
|
||||||
|
| AVG
|
||||||
| BETWEEN
|
| BETWEEN
|
||||||
| BOTH
|
| BOTH
|
||||||
| BY
|
| BY
|
||||||
|
@ -1429,11 +1445,13 @@ identifier
|
||||||
| LOCAL_DATETIME
|
| LOCAL_DATETIME
|
||||||
| LOCAL_TIME
|
| LOCAL_TIME
|
||||||
| MAP
|
| MAP
|
||||||
|
| MAX
|
||||||
| MAXELEMENT
|
| MAXELEMENT
|
||||||
| MAXINDEX
|
| MAXINDEX
|
||||||
| MEMBER
|
| MEMBER
|
||||||
| MICROSECOND
|
| MICROSECOND
|
||||||
| MILLISECOND
|
| MILLISECOND
|
||||||
|
| MIN
|
||||||
| MINELEMENT
|
| MINELEMENT
|
||||||
| MININDEX
|
| MININDEX
|
||||||
| MINUTE
|
| MINUTE
|
||||||
|
@ -1469,6 +1487,7 @@ identifier
|
||||||
| SIZE
|
| SIZE
|
||||||
| SOME
|
| SOME
|
||||||
| SUBSTRING
|
| SUBSTRING
|
||||||
|
| SUM
|
||||||
| THEN
|
| THEN
|
||||||
| TIES
|
| TIES
|
||||||
| TIME
|
| TIME
|
||||||
|
|
|
@ -102,10 +102,8 @@ import org.hibernate.query.sqm.tree.domain.AbstractSqmFrom;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
|
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapJoin;
|
import org.hibernate.query.sqm.tree.domain.SqmMapJoin;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
||||||
|
@ -3969,57 +3967,40 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmMaxElementPath<?> visitMaxElementFunction(HqlParser.MaxElementFunctionContext ctx) {
|
public SqmElementAggregateFunction<?> visitElementAggregateFunction(HqlParser.ElementAggregateFunctionContext ctx) {
|
||||||
if ( creationOptions.useStrictJpaCompliance() ) {
|
if ( creationOptions.useStrictJpaCompliance() ) {
|
||||||
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
|
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SqmMaxElementPath<>( consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ) );
|
SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() );
|
||||||
|
if ( !(pluralPath instanceof SqmPluralValuedSimplePath) ) {
|
||||||
|
throw new SemanticException( "Path '" + ctx.path().getText() + "' did not resolve to a many-valued attribute" );
|
||||||
|
}
|
||||||
|
|
||||||
|
String functionName = ctx.getChild(0).getText().substring(0, 3);
|
||||||
|
return new SqmElementAggregateFunction<>( pluralPath, functionName );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmMinElementPath<?> visitMinElementFunction(HqlParser.MinElementFunctionContext ctx) {
|
public SqmIndexAggregateFunction<?> visitIndexAggregateFunction(HqlParser.IndexAggregateFunctionContext ctx) {
|
||||||
if ( creationOptions.useStrictJpaCompliance() ) {
|
if ( creationOptions.useStrictJpaCompliance() ) {
|
||||||
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
|
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SqmMinElementPath<>( consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ) );
|
final SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() );
|
||||||
|
if ( !(pluralPath instanceof SqmPluralValuedSimplePath) ) {
|
||||||
|
throw new SemanticException( "Path '" + ctx.path().getText() + "' did not resolve to a many-valued attribute" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqmMaxIndexPath<?> visitMaxIndexFunction(HqlParser.MaxIndexFunctionContext ctx) {
|
|
||||||
if ( creationOptions.useStrictJpaCompliance() ) {
|
|
||||||
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
|
|
||||||
}
|
|
||||||
|
|
||||||
final SqmPath<?> pluralPath = consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
|
|
||||||
if ( !isIndexedPluralAttribute( pluralPath ) ) {
|
if ( !isIndexedPluralAttribute( pluralPath ) ) {
|
||||||
throw new SemanticException(
|
throw new SemanticException(
|
||||||
"maxindex() function can only be applied to path expressions which resolve to an " +
|
"maxindex() function can only be applied to path expressions which resolve to an " +
|
||||||
"indexed collection (list,map); specified path [" + ctx.getChild( 2 ).getText() +
|
"indexed collection (list,map); specified path [" + ctx.path() +
|
||||||
"] resolved to " + pluralPath.getReferencedPathSource()
|
"] resolved to " + pluralPath.getReferencedPathSource()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SqmMaxIndexPath<>( pluralPath );
|
String functionName = ctx.getChild(0).getText().substring(0, 3);
|
||||||
}
|
return new SqmIndexAggregateFunction<>( pluralPath, functionName );
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqmMinIndexPath<?> visitMinIndexFunction(HqlParser.MinIndexFunctionContext ctx) {
|
|
||||||
if ( creationOptions.useStrictJpaCompliance() ) {
|
|
||||||
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
|
|
||||||
}
|
|
||||||
|
|
||||||
final SqmPath<?> pluralPath = consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
|
|
||||||
if ( !isIndexedPluralAttribute( pluralPath ) ) {
|
|
||||||
throw new SemanticException(
|
|
||||||
"minindex() function can only be applied to path expressions which resolve to an " +
|
|
||||||
"indexed collection (list,map); specified path [" + ctx.getChild( 2 ).getText() +
|
|
||||||
"] resolved to " + pluralPath.getReferencedPathSource()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SqmMinIndexPath<>( pluralPath );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,10 +20,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||||
|
@ -152,13 +150,9 @@ public interface SemanticQueryWalker<T> {
|
||||||
|
|
||||||
T visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path);
|
T visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path);
|
||||||
|
|
||||||
T visitMaxElementPath(SqmMaxElementPath<?> path);
|
T visitElementAggregateFunction(SqmElementAggregateFunction<?> path);
|
||||||
|
|
||||||
T visitMinElementPath(SqmMinElementPath<?> path);
|
T visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path);
|
||||||
|
|
||||||
T visitMaxIndexPath(SqmMaxIndexPath<?> path);
|
|
||||||
|
|
||||||
T visitMinIndexPath(SqmMinIndexPath<?> path);
|
|
||||||
|
|
||||||
T visitTreatedPath(SqmTreatedPath<?, ?> sqmTreatedPath);
|
T visitTreatedPath(SqmTreatedPath<?, ?> sqmTreatedPath);
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||||
|
@ -947,22 +945,12 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitMaxElementPath(SqmMaxElementPath binding) {
|
public Object visitElementAggregateFunction(SqmElementAggregateFunction binding) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitMinElementPath(SqmMinElementPath path) {
|
public Object visitIndexAggregateFunction(SqmIndexAggregateFunction path) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object visitMaxIndexPath(SqmMaxIndexPath path) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object visitMinIndexPath(SqmMinIndexPath path) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||||
|
@ -318,22 +316,12 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitMaxElementPath(SqmMaxElementPath<?> path) {
|
public Object visitElementAggregateFunction(SqmElementAggregateFunction<?> path) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitMinElementPath(SqmMinElementPath<?> path) {
|
public Object visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path) {
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object visitMaxIndexPath(SqmMaxIndexPath<?> path) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object visitMinIndexPath(SqmMinIndexPath<?> path) {
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,10 +168,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
|
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||||
|
@ -3270,24 +3268,29 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected Expression createMinOrMaxIndexOrElement(
|
||||||
public Expression visitMaxElementPath(SqmMaxElementPath<?> path) {
|
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
|
||||||
return createMinOrMaxIndexOrElement( path, false, true );
|
boolean index,
|
||||||
|
String functionName) {
|
||||||
|
boolean isMinOrMax = functionName.equalsIgnoreCase("min")
|
||||||
|
|| functionName.equalsIgnoreCase("max");
|
||||||
|
// Try to create a lateral sub-query join if possible which allows the re-use of the expression
|
||||||
|
if ( isMinOrMax && creationContext.getSessionFactory().getJdbcServices().getDialect().supportsLateral() ) {
|
||||||
|
return createLateralJoinExpression( pluralPartPath, index, functionName );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return createCorrelatedAggregateSubQuery( pluralPartPath, index, functionName );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Expression visitMinElementPath(SqmMinElementPath<?> path) {
|
public Expression visitElementAggregateFunction(SqmElementAggregateFunction<?> path) {
|
||||||
return createMinOrMaxIndexOrElement( path, false, false );
|
return createMinOrMaxIndexOrElement( path, false, path.getFunctionName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Expression visitMaxIndexPath(SqmMaxIndexPath<?> path) {
|
public Expression visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path) {
|
||||||
return createMinOrMaxIndexOrElement( path, true, true );
|
return createMinOrMaxIndexOrElement( path, true, path.getFunctionName() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Expression visitMinIndexPath(SqmMinIndexPath<?> path) {
|
|
||||||
return createMinOrMaxIndexOrElement( path, true, false );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3466,23 +3469,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Expression createMinOrMaxIndexOrElement(
|
|
||||||
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
|
|
||||||
boolean index,
|
|
||||||
boolean max) {
|
|
||||||
// Try to create a lateral sub-query join if possible which allows the re-use of the expression
|
|
||||||
if ( creationContext.getSessionFactory().getJdbcServices().getDialect().supportsLateral() ) {
|
|
||||||
return createLateralJoinExpression( pluralPartPath, index, max );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return createCorrelatedAggregateSubQuery( pluralPartPath, index, max );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Expression createCorrelatedAggregateSubQuery(
|
protected Expression createCorrelatedAggregateSubQuery(
|
||||||
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
|
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
|
||||||
boolean index,
|
boolean index,
|
||||||
boolean max) {
|
String function) {
|
||||||
prepareReusablePath( pluralPartPath.getLhs(), () -> null );
|
prepareReusablePath( pluralPartPath.getLhs(), () -> null );
|
||||||
|
|
||||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
|
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
|
||||||
|
@ -3520,11 +3510,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
registerPluralTableGroupParts( tableGroup );
|
registerPluralTableGroupParts( tableGroup );
|
||||||
subQuerySpec.getFromClause().addRoot( tableGroup );
|
subQuerySpec.getFromClause().addRoot( tableGroup );
|
||||||
|
|
||||||
final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor = (AbstractSqmSelfRenderingFunctionDescriptor) creationContext
|
final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor =
|
||||||
|
(AbstractSqmSelfRenderingFunctionDescriptor) creationContext
|
||||||
.getSessionFactory()
|
.getSessionFactory()
|
||||||
.getQueryEngine()
|
.getQueryEngine()
|
||||||
.getSqmFunctionRegistry()
|
.getSqmFunctionRegistry()
|
||||||
.findFunctionDescriptor( max ? "max" : "min" );
|
.findFunctionDescriptor( function );
|
||||||
final CollectionPart collectionPart = index
|
final CollectionPart collectionPart = index
|
||||||
? pluralAttributeMapping.getIndexDescriptor()
|
? pluralAttributeMapping.getIndexDescriptor()
|
||||||
: pluralAttributeMapping.getElementDescriptor();
|
: pluralAttributeMapping.getElementDescriptor();
|
||||||
|
@ -3591,7 +3582,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
protected Expression createLateralJoinExpression(
|
protected Expression createLateralJoinExpression(
|
||||||
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
|
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
|
||||||
boolean index,
|
boolean index,
|
||||||
boolean max) {
|
String functionName) {
|
||||||
prepareReusablePath( pluralPartPath.getLhs(), () -> null );
|
prepareReusablePath( pluralPartPath.getLhs(), () -> null );
|
||||||
|
|
||||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
|
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
|
||||||
|
@ -3611,7 +3602,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
modelPart = collectionPart;
|
modelPart = collectionPart;
|
||||||
}
|
}
|
||||||
final int jdbcTypeCount = modelPart.getJdbcTypeCount();
|
final int jdbcTypeCount = modelPart.getJdbcTypeCount();
|
||||||
final String pathName = ( max ? "max" : "min" ) + ( index ? "_index" : "_element" );
|
final String pathName = functionName + ( index ? "_index" : "_element" );
|
||||||
final String identifierVariable = parentTableGroup.getPrimaryTableReference().getIdentificationVariable()
|
final String identifierVariable = parentTableGroup.getPrimaryTableReference().getIdentificationVariable()
|
||||||
+ "_" + pathName;
|
+ "_" + pathName;
|
||||||
final NavigablePath queryPath = new NavigablePath( parentTableGroup.getNavigablePath(), pathName, identifierVariable );
|
final NavigablePath queryPath = new NavigablePath( parentTableGroup.getNavigablePath(), pathName, identifierVariable );
|
||||||
|
@ -3680,7 +3671,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
subQuerySpec.addSortSpecification(
|
subQuerySpec.addSortSpecification(
|
||||||
new SortSpecification(
|
new SortSpecification(
|
||||||
columnReference,
|
columnReference,
|
||||||
max ? SortOrder.DESCENDING : SortOrder.ASCENDING
|
functionName.equalsIgnoreCase("max")
|
||||||
|
? SortOrder.DESCENDING
|
||||||
|
: SortOrder.ASCENDING
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
resultColumnReferences.add(
|
resultColumnReferences.add(
|
||||||
|
|
|
@ -14,16 +14,23 @@ import org.hibernate.query.sqm.SqmPathSource;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SqmMaxElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
public class SqmElementAggregateFunction<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
||||||
public static final String NAVIGABLE_NAME = "{max-element}";
|
public static final String NAVIGABLE_NAME = "{max-element}";
|
||||||
|
|
||||||
public SqmMaxElementPath(SqmPath<?> pluralDomainPath) {
|
private final String functionName;
|
||||||
|
|
||||||
|
public SqmElementAggregateFunction(SqmPath<?> pluralDomainPath, String functionName) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
super(
|
super(
|
||||||
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
|
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
|
||||||
pluralDomainPath,
|
pluralDomainPath,
|
||||||
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
|
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
|
||||||
);
|
);
|
||||||
|
this.functionName = functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFunctionName() {
|
||||||
|
return functionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,12 +50,12 @@ public class SqmMaxElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
public <X> X accept(SemanticQueryWalker<X> walker) {
|
||||||
return walker.visitMaxElementPath( this );
|
return walker.visitElementAggregateFunction( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendHqlString(StringBuilder sb) {
|
public void appendHqlString(StringBuilder sb) {
|
||||||
sb.append( "maxelement(" );
|
sb.append(functionName).append( "(" );
|
||||||
getLhs().appendHqlString( sb );
|
getLhs().appendHqlString( sb );
|
||||||
sb.append( ')' );
|
sb.append( ')' );
|
||||||
}
|
}
|
|
@ -11,24 +11,25 @@ import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||||
import org.hibernate.query.sqm.SqmPathSource;
|
import org.hibernate.query.sqm.SqmPathSource;
|
||||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SqmMaxIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
public class SqmIndexAggregateFunction<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
||||||
public static final String NAVIGABLE_NAME = "{max-index}";
|
public static final String NAVIGABLE_NAME = "{max-index}";
|
||||||
|
|
||||||
private final SqmPathSource<T> indexPathSource;
|
private final SqmPathSource<T> indexPathSource;
|
||||||
|
private final String functionName;
|
||||||
|
|
||||||
public SqmMaxIndexPath(SqmPath<?> pluralDomainPath) {
|
public SqmIndexAggregateFunction(SqmPath<?> pluralDomainPath, String functionName) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
super(
|
super(
|
||||||
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
|
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
|
||||||
pluralDomainPath,
|
pluralDomainPath,
|
||||||
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
|
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
|
||||||
);
|
);
|
||||||
|
this.functionName = functionName;
|
||||||
|
|
||||||
if ( getPluralAttribute() instanceof ListPersistentAttribute ) {
|
if ( getPluralAttribute() instanceof ListPersistentAttribute ) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
|
@ -43,6 +44,10 @@ public class SqmMaxIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFunctionName() {
|
||||||
|
return functionName;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmPath<?> resolvePathPart(
|
public SqmPath<?> resolvePathPart(
|
||||||
String name,
|
String name,
|
||||||
|
@ -60,12 +65,12 @@ public class SqmMaxIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
public <X> X accept(SemanticQueryWalker<X> walker) {
|
||||||
return walker.visitMaxIndexPath( this );
|
return walker.visitIndexAggregateFunction( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendHqlString(StringBuilder sb) {
|
public void appendHqlString(StringBuilder sb) {
|
||||||
sb.append( "maxindex(" );
|
sb.append(functionName).append( "(" );
|
||||||
getLhs().appendHqlString( sb );
|
getLhs().appendHqlString( sb );
|
||||||
sb.append( ')' );
|
sb.append( ')' );
|
||||||
}
|
}
|
|
@ -1,58 +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.sqm.tree.domain;
|
|
||||||
|
|
||||||
import org.hibernate.metamodel.model.domain.ManagedDomainType;
|
|
||||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
|
||||||
import org.hibernate.query.SemanticException;
|
|
||||||
import org.hibernate.query.sqm.SqmPathSource;
|
|
||||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
|
||||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class SqmMinElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
|
||||||
public static final String NAVIGABLE_NAME = "{min-element}";
|
|
||||||
|
|
||||||
public SqmMinElementPath(SqmPath<?> pluralDomainPath) {
|
|
||||||
//noinspection unchecked
|
|
||||||
super(
|
|
||||||
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
|
|
||||||
pluralDomainPath,
|
|
||||||
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqmPath<?> resolvePathPart(
|
|
||||||
String name,
|
|
||||||
boolean isTerminal,
|
|
||||||
SqmCreationState creationState) {
|
|
||||||
final SqmPath<?> sqmPath = get( name );
|
|
||||||
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
|
|
||||||
return sqmPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqmPathSource<T> getReferencedPathSource() {
|
|
||||||
return getPluralAttribute().getElementPathSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
|
||||||
return walker.visitMinElementPath( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void appendHqlString(StringBuilder sb) {
|
|
||||||
sb.append( "minelement(" );
|
|
||||||
getLhs().appendHqlString( sb );
|
|
||||||
sb.append( ')' );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +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.sqm.tree.domain;
|
|
||||||
|
|
||||||
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
|
|
||||||
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
|
|
||||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
|
||||||
import org.hibernate.query.sqm.SqmPathSource;
|
|
||||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
|
||||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class SqmMinIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
|
||||||
public static final String NAVIGABLE_NAME = "{min-index}";
|
|
||||||
|
|
||||||
private final SqmPathSource<T> indexPathSource;
|
|
||||||
|
|
||||||
public SqmMinIndexPath(SqmPath<?> pluralDomainPath) {
|
|
||||||
//noinspection unchecked
|
|
||||||
super(
|
|
||||||
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
|
|
||||||
pluralDomainPath,
|
|
||||||
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( getPluralAttribute() instanceof ListPersistentAttribute ) {
|
|
||||||
//noinspection unchecked
|
|
||||||
this.indexPathSource = (SqmPathSource<T>) getPluralAttribute().getIndexPathSource();
|
|
||||||
}
|
|
||||||
else if ( getPluralAttribute() instanceof MapPersistentAttribute ) {
|
|
||||||
//noinspection unchecked
|
|
||||||
this.indexPathSource = ( (MapPersistentAttribute<?, T, ?>) getPluralAttribute() ).getKeyPathSource();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new UnsupportedOperationException( "Plural attribute [" + getPluralAttribute() + "] is not indexed" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqmPath<?> resolvePathPart(
|
|
||||||
String name,
|
|
||||||
boolean isTerminal,
|
|
||||||
SqmCreationState creationState) {
|
|
||||||
final SqmPath<?> sqmPath = get( name );
|
|
||||||
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
|
|
||||||
return sqmPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqmPathSource<T> getReferencedPathSource() {
|
|
||||||
return indexPathSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
|
||||||
return walker.visitMinIndexPath( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void appendHqlString(StringBuilder sb) {
|
|
||||||
sb.append( "minindex(" );
|
|
||||||
getLhs().appendHqlString( sb );
|
|
||||||
sb.append( ')' );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -43,13 +43,13 @@ public class MapIndexFormulaTest {
|
||||||
session.createQuery( "from Group g join g.users u where g.name = 'something' and index(u) = 'nada'" )
|
session.createQuery( "from Group g join g.users u where g.name = 'something' and index(u) = 'nada'" )
|
||||||
.list();
|
.list();
|
||||||
session.createQuery(
|
session.createQuery(
|
||||||
"from Group g join g.users u where g.name = 'something' and minindex(u) = 'nada'" )
|
"from Group g where g.name = 'something' and minindex(g.users) = 'nada'" )
|
||||||
.list();
|
.list();
|
||||||
session.createQuery(
|
session.createQuery(
|
||||||
"from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada'" )
|
"from Group g where g.name = 'something' and maxindex(g.users) = 'nada'" )
|
||||||
.list();
|
.list();
|
||||||
session.createQuery(
|
session.createQuery(
|
||||||
"from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada' and maxindex(u) = 'nada'" )
|
"from Group g where g.name = 'something' and maxindex(g.users) = 'nada' and maxindex(g.users) = 'nada'" )
|
||||||
.list();
|
.list();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class PluralAttributeMappingTests {
|
||||||
final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel();
|
final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel();
|
||||||
final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfLists.class );
|
final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfLists.class );
|
||||||
|
|
||||||
assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 7 ) );
|
assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 8 ) );
|
||||||
|
|
||||||
final AttributeMapping listOfBasics = containerEntityDescriptor.findAttributeMapping( "listOfBasics" );
|
final AttributeMapping listOfBasics = containerEntityDescriptor.findAttributeMapping( "listOfBasics" );
|
||||||
assertThat( listOfBasics, notNullValue() );
|
assertThat( listOfBasics, notNullValue() );
|
||||||
|
@ -116,7 +116,7 @@ public class PluralAttributeMappingTests {
|
||||||
final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel();
|
final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel();
|
||||||
final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfMaps.class );
|
final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfMaps.class );
|
||||||
|
|
||||||
assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 16 ) );
|
assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 17 ) );
|
||||||
|
|
||||||
final PluralAttributeMapping basicByBasic = (PluralAttributeMapping) containerEntityDescriptor.findAttributeMapping( "basicByBasic" );
|
final PluralAttributeMapping basicByBasic = (PluralAttributeMapping) containerEntityDescriptor.findAttributeMapping( "basicByBasic" );
|
||||||
assertThat( basicByBasic, notNullValue() );
|
assertThat( basicByBasic, notNullValue() );
|
||||||
|
|
|
@ -67,10 +67,13 @@ public class FunctionTests {
|
||||||
|
|
||||||
EntityOfLists eol = new EntityOfLists(1,"");
|
EntityOfLists eol = new EntityOfLists(1,"");
|
||||||
eol.addBasic("hello");
|
eol.addBasic("hello");
|
||||||
|
eol.addNumber(1.0);
|
||||||
|
eol.addNumber(2.0);
|
||||||
em.persist(eol);
|
em.persist(eol);
|
||||||
|
|
||||||
EntityOfMaps eom = new EntityOfMaps(2,"");
|
EntityOfMaps eom = new EntityOfMaps(2,"");
|
||||||
eom.addBasicByBasic("hello", "world");
|
eom.addBasicByBasic("hello", "world");
|
||||||
|
eom.addNumberByNumber(1,1.0);
|
||||||
em.persist(eom);
|
em.persist(eom);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -90,6 +93,45 @@ public class FunctionTests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresDialect(H2Dialect.class)
|
||||||
|
@RequiresDialect(HSQLDialect.class)
|
||||||
|
@RequiresDialect(DerbyDialect.class)
|
||||||
|
@RequiresDialect(MySQLDialect.class)
|
||||||
|
@RequiresDialect(SybaseDialect.class)
|
||||||
|
@RequiresDialect(MariaDBDialect.class)
|
||||||
|
@RequiresDialect(TiDBDialect.class)
|
||||||
|
// it's failing on the other dialects due to a bug in query translator
|
||||||
|
public void testMaxMinSumIndexElement(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
assertThat( session.createQuery("select max(index eol.listOfNumbers) from EntityOfLists eol")
|
||||||
|
.getSingleResult(), is(1) );
|
||||||
|
assertThat( session.createQuery("select max(element eol.listOfNumbers) from EntityOfLists eol")
|
||||||
|
.list().get(0),
|
||||||
|
// .getSingleResult(),
|
||||||
|
is(2.0) );
|
||||||
|
|
||||||
|
assertThat( session.createQuery("select sum(index eol.listOfNumbers) from EntityOfLists eol")
|
||||||
|
.getSingleResult(), is(1) );
|
||||||
|
assertThat( session.createQuery("select sum(element eol.listOfNumbers) from EntityOfLists eol")
|
||||||
|
.list().get(0),
|
||||||
|
// .getSingleResult(),
|
||||||
|
is(3.0) );
|
||||||
|
|
||||||
|
assertThat( session.createQuery("select max(index eom.numberByNumber) from EntityOfMaps eom")
|
||||||
|
.getSingleResult(), is(1) );
|
||||||
|
assertThat( session.createQuery("select max(element eom.numberByNumber) from EntityOfMaps eom")
|
||||||
|
.getSingleResult(), is(1.0) );
|
||||||
|
|
||||||
|
assertThat( session.createQuery("select sum(index eom.numberByNumber) from EntityOfMaps eom")
|
||||||
|
.getSingleResult(), is(1) );
|
||||||
|
assertThat( session.createQuery("select sum(element eom.numberByNumber) from EntityOfMaps eom")
|
||||||
|
.getSingleResult(), is(1.0) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@RequiresDialect(H2Dialect.class)
|
@RequiresDialect(H2Dialect.class)
|
||||||
@RequiresDialect(HSQLDialect.class)
|
@RequiresDialect(HSQLDialect.class)
|
||||||
|
@ -102,19 +144,11 @@ public class FunctionTests {
|
||||||
public void testMaxindexMaxelement(SessionFactoryScope scope) {
|
public void testMaxindexMaxelement(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
assertThat( session.createQuery("select maxindex(l) from EntityOfLists eol join eol.listOfBasics l")
|
|
||||||
.getSingleResult(), is(0) );
|
|
||||||
assertThat( session.createQuery("select maxelement(l) from EntityOfLists eol join eol.listOfBasics l")
|
|
||||||
.getSingleResult(), is("hello") );
|
|
||||||
assertThat( session.createQuery("select maxindex(eol.listOfBasics) from EntityOfLists eol")
|
assertThat( session.createQuery("select maxindex(eol.listOfBasics) from EntityOfLists eol")
|
||||||
.getSingleResult(), is(0) );
|
.getSingleResult(), is(0) );
|
||||||
assertThat( session.createQuery("select maxelement(eol.listOfBasics) from EntityOfLists eol")
|
assertThat( session.createQuery("select maxelement(eol.listOfBasics) from EntityOfLists eol")
|
||||||
.getSingleResult(), is("hello") );
|
.getSingleResult(), is("hello") );
|
||||||
|
|
||||||
assertThat( session.createQuery("select maxindex(m) from EntityOfMaps eom join eom.basicByBasic m")
|
|
||||||
.getSingleResult(), is("hello") );
|
|
||||||
assertThat( session.createQuery("select maxelement(m) from EntityOfMaps eom join eom.basicByBasic m")
|
|
||||||
.getSingleResult(), is("world") );
|
|
||||||
assertThat( session.createQuery("select maxindex(eom.basicByBasic) from EntityOfMaps eom")
|
assertThat( session.createQuery("select maxindex(eom.basicByBasic) from EntityOfMaps eom")
|
||||||
.getSingleResult(), is("hello") );
|
.getSingleResult(), is("hello") );
|
||||||
assertThat( session.createQuery("select maxelement(eom.basicByBasic) from EntityOfMaps eom")
|
assertThat( session.createQuery("select maxelement(eom.basicByBasic) from EntityOfMaps eom")
|
||||||
|
|
|
@ -126,9 +126,9 @@ public class TernaryTest extends BaseCoreFunctionalTestCase {
|
||||||
session.beginTransaction();
|
session.beginTransaction();
|
||||||
session.createQuery( "from Employee e join e.managerBySite as m where index(m) is not null" )
|
session.createQuery( "from Employee e join e.managerBySite as m where index(m) is not null" )
|
||||||
.list();
|
.list();
|
||||||
session.createQuery( "from Employee e join e.managerBySite as m where minIndex(m) is not null" )
|
session.createQuery( "from Employee e where minIndex(e.managerBySite) is not null" )
|
||||||
.list();
|
.list();
|
||||||
session.createQuery( "from Employee e join e.managerBySite as m where maxIndex(m) is not null" )
|
session.createQuery( "from Employee e where maxIndex(e.managerBySite) is not null" )
|
||||||
.list();
|
.list();
|
||||||
session.getTransaction().commit();
|
session.getTransaction().commit();
|
||||||
session.close();
|
session.close();
|
||||||
|
|
|
@ -29,6 +29,7 @@ public class EntityOfLists {
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private List<String> listOfBasics;
|
private List<String> listOfBasics;
|
||||||
|
private List<Double> listOfNumbers;
|
||||||
|
|
||||||
private List<EnumValue> listOfConvertedEnums;
|
private List<EnumValue> listOfConvertedEnums;
|
||||||
private List<EnumValue> listOfEnums;
|
private List<EnumValue> listOfEnums;
|
||||||
|
@ -78,6 +79,17 @@ public class EntityOfLists {
|
||||||
this.listOfBasics = listOfBasics;
|
this.listOfBasics = listOfBasics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@OrderColumn(name="num_indx")
|
||||||
|
@CollectionTable(name = "EntityOfLists_numbers")
|
||||||
|
public List<Double> getListOfNumbers() {
|
||||||
|
return listOfNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListOfNumbers(List<Double> listOfNumbers) {
|
||||||
|
this.listOfNumbers = listOfNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
public void addBasic(String basic) {
|
public void addBasic(String basic) {
|
||||||
if ( listOfBasics == null ) {
|
if ( listOfBasics == null ) {
|
||||||
listOfBasics = new ArrayList<>();
|
listOfBasics = new ArrayList<>();
|
||||||
|
@ -85,6 +97,13 @@ public class EntityOfLists {
|
||||||
listOfBasics.add( basic );
|
listOfBasics.add( basic );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addNumber(double number) {
|
||||||
|
if ( listOfNumbers == null ) {
|
||||||
|
listOfNumbers = new ArrayList<>();
|
||||||
|
}
|
||||||
|
listOfNumbers.add( number );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// listOfConvertedEnums
|
// listOfConvertedEnums
|
||||||
|
|
|
@ -40,6 +40,7 @@ public class EntityOfMaps {
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Map<String, String> basicByBasic;
|
private Map<String, String> basicByBasic;
|
||||||
|
private Map<Integer, Double> numberByNumber;
|
||||||
|
|
||||||
private SortedMap<String, String> sortedBasicByBasic;
|
private SortedMap<String, String> sortedBasicByBasic;
|
||||||
private SortedMap<String, String> sortedBasicByBasicWithComparator;
|
private SortedMap<String, String> sortedBasicByBasicWithComparator;
|
||||||
|
@ -109,6 +110,25 @@ public class EntityOfMaps {
|
||||||
basicByBasic.put( key, val );
|
basicByBasic.put( key, val );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@CollectionTable(name = "EntityOfMaps_number_number1")
|
||||||
|
@MapKeyColumn(name = "number_key")
|
||||||
|
@Column(name = "number_val")
|
||||||
|
public Map<Integer, Double> getNumberByNumber() {
|
||||||
|
return numberByNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumberByNumber(Map<Integer, Double> numberByNumber) {
|
||||||
|
this.numberByNumber = numberByNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNumberByNumber(int key, double val) {
|
||||||
|
if ( numberByNumber == null ) {
|
||||||
|
numberByNumber = new HashMap<>();
|
||||||
|
}
|
||||||
|
numberByNumber.put( key, val );
|
||||||
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// sortedBasicByBasic
|
// sortedBasicByBasic
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue