HHH-17982 Implement some JPA 3.2 methods

This commit is contained in:
Christian Beikov 2024-04-26 15:49:30 +02:00 committed by Steve Ebersole
parent 3e423ce900
commit 472d8eaa25
3 changed files with 135 additions and 54 deletions

View File

@ -276,39 +276,37 @@ import static org.hibernate.type.spi.TypeConfiguration.isJdbcTemporalType;
public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implements SqmCreationState {
private static final Logger log = Logger.getLogger( SemanticQueryBuilder.class );
private static final Set<String> JPA_STANDARD_FUNCTIONS;
private static final Set<String> JPA_STANDARD_FUNCTIONS = Set.of(
"avg",
"max",
"min",
"sum",
"count",
"length",
"locate",
"abs",
"sqrt",
"mod",
"size",
"index",
"current_date",
"current_time",
"current_timestamp",
"concat",
"substring",
"trim",
"lower",
"upper",
"coalesce",
"nullif",
"left",
"right",
"replace"
);
private static final BasicTypeImpl<Object> OBJECT_BASIC_TYPE =
new BasicTypeImpl<>( new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE );
static {
final Set<String> jpaStandardFunctions = new HashSet<>();
// Extracted from the BNF in JPA spec 4.14.
jpaStandardFunctions.add( "avg" );
jpaStandardFunctions.add( "max" );
jpaStandardFunctions.add( "min" );
jpaStandardFunctions.add( "sum" );
jpaStandardFunctions.add( "count" );
jpaStandardFunctions.add( "length" );
jpaStandardFunctions.add( "locate" );
jpaStandardFunctions.add( "abs" );
jpaStandardFunctions.add( "sqrt" );
jpaStandardFunctions.add( "mod" );
jpaStandardFunctions.add( "size" );
jpaStandardFunctions.add( "index" );
jpaStandardFunctions.add( "current_date" );
jpaStandardFunctions.add( "current_time" );
jpaStandardFunctions.add( "current_timestamp" );
jpaStandardFunctions.add( "concat" );
jpaStandardFunctions.add( "substring" );
jpaStandardFunctions.add( "trim" );
jpaStandardFunctions.add( "lower" );
jpaStandardFunctions.add( "upper" );
jpaStandardFunctions.add( "coalesce" );
jpaStandardFunctions.add( "nullif" );
JPA_STANDARD_FUNCTIONS = jpaStandardFunctions;
}
/**
* Main entry point into analysis of HQL/JPQL parse tree - producing
* a semantic model of the query.
@ -1073,11 +1071,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else {
firstIndex = 0;
}
if ( creationOptions.useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation(
StrictJpaComplianceViolation.Type.SET_OPERATIONS
);
}
final SqmQueryPart<?> firstQueryPart = (SqmQueryPart<?>) children.get( firstIndex ).accept( this );
SqmQueryGroup<?> queryGroup;
if ( firstQueryPart instanceof SqmQueryGroup<?>) {

View File

@ -427,7 +427,13 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <T> CriteriaSelect<T> unionAll(CriteriaSelect<? extends T> left, CriteriaSelect<? extends T> right) {
return null;
if ( left instanceof Subquery<?> ) {
assert right instanceof Subquery<?>;
//noinspection unchecked
return setOperation( SetOperator.UNION_ALL, (Subquery<T>) left, (Subquery<T>) right );
}
//noinspection unchecked
return setOperation( SetOperator.UNION_ALL, (JpaCriteriaQuery<T>) left, (JpaCriteriaQuery<T>) right );
}
@Override
@ -437,12 +443,24 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <T> CriteriaSelect<T> except(CriteriaSelect<T> left, CriteriaSelect<?> right) {
return null;
if ( left instanceof Subquery<?> ) {
assert right instanceof Subquery<?>;
//noinspection unchecked
return setOperation( SetOperator.EXCEPT, (Subquery<T>) left, (Subquery<T>) right );
}
//noinspection unchecked
return setOperation( SetOperator.EXCEPT, (JpaCriteriaQuery<T>) left, (JpaCriteriaQuery<T>) right );
}
@Override
public <T> CriteriaSelect<T> exceptAll(CriteriaSelect<T> left, CriteriaSelect<?> right) {
return null;
if ( left instanceof Subquery<?> ) {
assert right instanceof Subquery<?>;
//noinspection unchecked
return setOperation( SetOperator.EXCEPT_ALL, (Subquery<T>) left, (Subquery<T>) right );
}
//noinspection unchecked
return setOperation( SetOperator.EXCEPT_ALL, (JpaCriteriaQuery<T>) left, (JpaCriteriaQuery<T>) right );
}
@Override
@ -588,12 +606,24 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <T> CriteriaSelect<T> intersect(CriteriaSelect<? super T> left, CriteriaSelect<? super T> right) {
return null;
if ( left instanceof Subquery<?> ) {
assert right instanceof Subquery<?>;
//noinspection unchecked
return setOperation( SetOperator.INTERSECT, (Subquery<T>) left, (Subquery<T>) right );
}
//noinspection unchecked
return setOperation( SetOperator.INTERSECT, (JpaCriteriaQuery<T>) left, (JpaCriteriaQuery<T>) right );
}
@Override
public <T> CriteriaSelect<T> intersectAll(CriteriaSelect<? super T> left, CriteriaSelect<? super T> right) {
return null;
if ( left instanceof Subquery<?> ) {
assert right instanceof Subquery<?>;
//noinspection unchecked
return setOperation( SetOperator.INTERSECT_ALL, (Subquery<T>) left, (Subquery<T>) right );
}
//noinspection unchecked
return setOperation( SetOperator.INTERSECT_ALL, (JpaCriteriaQuery<T>) left, (JpaCriteriaQuery<T>) right );
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@ -1614,6 +1644,16 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return new JpaCriteriaParameter<>( name, parameterType, notBasic, this );
}
@Override
public SqmExpression<String> concat(List<Expression<String>> expressions) {
//noinspection unchecked
return getFunctionDescriptor( "concat" ).generateSqmExpression(
(List<? extends SqmTypedNode<?>>) (List<?>) expressions,
null,
getQueryEngine()
);
}
@Override
public SqmExpression<String> concat(Expression<String> x, Expression<String> y) {
final SqmExpression<String> xSqmExpression = (SqmExpression<String>) x;
@ -1918,11 +1958,6 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
);
}
@Override
public <N, T extends Temporal> SqmExpression<N> extract(TemporalField<N, T> field, Expression<T> temporal) {
return null;
}
@Override
public <T> SqmFunction<T> function(String name, Class<T> type, Expression<?>[] args) {
final BasicType<T> resultType = getTypeConfiguration().standardBasicTypeForJavaType( type );
@ -2244,7 +2279,15 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate and(List<Predicate> restrictions) {
return null;
if ( restrictions == null || restrictions.isEmpty() ) {
return conjunction();
}
final List<SqmPredicate> predicates = new ArrayList<>( restrictions.size() );
for ( Predicate expression : restrictions ) {
predicates.add( (SqmPredicate) expression );
}
return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this );
}
@Override
@ -2805,11 +2848,6 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return not( like( x, pattern, escapeChar ) );
}
@Override
public SqmExpression<String> concat(List<Expression<String>> expressions) {
return null;
}
@Override
public SqmPredicate notIlike(Expression<String> x, Expression<String> pattern) {
return not( ilike( x, pattern ) );
@ -2957,6 +2995,51 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
);
}
@Override
public <N, T extends Temporal> SqmExpression<N> extract(TemporalField<N, T> field, Expression<T> temporal) {
Class<?> resultType = Integer.class;
final TemporalUnit temporalUnit;
switch ( field.toString() ) {
case "year":
temporalUnit = TemporalUnit.YEAR;
break;
case "quarter":
temporalUnit = TemporalUnit.QUARTER;
break;
case "month":
temporalUnit = TemporalUnit.MONTH;
break;
case "week":
temporalUnit = TemporalUnit.WEEK;
break;
case "day":
temporalUnit = TemporalUnit.DAY;
break;
case "hour":
temporalUnit = TemporalUnit.HOUR;
break;
case "minute":
temporalUnit = TemporalUnit.MINUTE;
break;
case "second":
temporalUnit = TemporalUnit.SECOND;
resultType = Double.class;
break;
case "date":
temporalUnit = TemporalUnit.DATE;
resultType = LocalDate.class;
break;
case "time":
temporalUnit = TemporalUnit.TIME;
resultType = LocalTime.class;
break;
default:
throw new IllegalArgumentException( "Invalid temporal field [" + field + "]" );
}
//noinspection unchecked
return extract( temporal, temporalUnit, (Class<N>) resultType );
}
private <T> SqmFunction<T> extract(
Expression<? extends TemporalAccessor> datetime,
TemporalUnit temporalUnit,

View File

@ -355,11 +355,16 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
@Override
public SqmQuerySpec<T> setRestriction(Expression<Boolean> restriction) {
SqmWhereClause whereClause = getWhereClause();
if ( whereClause == null ) {
setWhereClause( whereClause = new SqmWhereClause( nodeBuilder() ) );
if ( restriction == null ) {
setWhereClause( null );
}
else {
SqmWhereClause whereClause = getWhereClause();
if ( whereClause == null ) {
setWhereClause( whereClause = new SqmWhereClause( nodeBuilder() ) );
}
whereClause.setPredicate( nodeBuilder().wrap( restriction ) );
}
whereClause.setPredicate( nodeBuilder().wrap( restriction ) );
return this;
}